From: jenkins-bot Date: Thu, 28 Dec 2017 09:44:58 +0000 (+0000) Subject: Merge "Change php extract() to explicit code" X-Git-Tag: 1.31.0-rc.0~1070 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=1a40e0cc86b6ee0706606ded3ea243dfde4a414c;hp=3d560be428920fd4eb10ea6e9760dafbba8f9ddc Merge "Change php extract() to explicit code" --- diff --git a/.travis.yml b/.travis.yml index 78a2b7d735..64414b5a98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,8 @@ matrix: php: 7 - env: dbtype=mysql dbuser=root php: 7.1 + - env: dbtype=mysql dbuser=root + php: 7.2 services: - mysql diff --git a/HISTORY b/HISTORY index 1f30b7068e..244d681902 100644 --- a/HISTORY +++ b/HISTORY @@ -1,4 +1,267 @@ -Change notes from older releases. For current info see RELEASE-NOTES-1.30. +Change notes from older releases. For current info see RELEASE-NOTES-1.31. + += MediaWiki 1.30 = + +== MediaWiki 1.30.0 == + +=== Changes since MediaWiki 1.30.0-rc.0 === +* Upgraded Moment.js from v2.15.0 to v2.19.3. +* Add ip_changes to postgres/tables.sql. +* Skip null shell parameters. +* Add wfWaitForSlaves() to maintenance/migrateComments.php. +* (T182245) Fix join conditions in ImageListPager. +* (T178626) Revert #contentSub and #jump-to-nav margin changes. + +=== MySQL version requirement in 1.30 === +As of 1.30, MediaWiki now requires MySQL 5.5.8 or higher (see Compatibility +section). + +=== Configuration changes in 1.30 === +* The "C.UTF-8" locale should be used for $wgShellLocale, if available, to avoid + unexpected behavior when code uses locale-sensitive string comparisons. For + example, the Scribunto extension considers "bar" < "Foo" in most locales + since it ignores case. +* $wgShellLocale now affects LC_ALL rather than only LC_CTYPE. See + documentation of $wgShellLocale for details. +* $wgShellLocale is now applied for all requests. wfInitShellLocale() is + deprecated and a no-op, as it is no longer needed. +* $wgJobClasses may now specify callback functions as an alternative to plain + class names. This is intended for extensions that want control over the + instantiation of their jobs, to allow for proper dependency injection. +* $wgResourceModules may now specify callback functions as an alternative + to plain class names, using the 'factory' key in the module description + array. This allows dependency injection to be used for ResourceLoader modules. +* $wgExceptionHooks has been removed. +* (T163562) $wgRangeContributionsCIDRLimit was introduced to control the size + of IP ranges that can be queried at Special:Contributions. +* (T45547) $wgUsePigLatinVariant added (off by default). +* (T152540) MediaWiki now supports a section ID escaping style that allows to display + non-Latin characters verbatim on many modern browsers. This is controlled by the + new configuration setting, $wgFragmentMode. +* $wgExperimentalHtmlIds is now deprecated and will be removed in a future version, + use $wgFragmentMode to migrate off it to a modern alternative. +* $wgExternalInterwikiFragmentMode was introduced to control how fragments in + sinterwikis going outside of current wiki farm are encoded. +* (T120333) Soft-deprecated the use of PHP extension 'mysql' in favor of 'mysqli'. + This PHP extension was deprecated in PHP 5.5 and removed in PHP 7.0. MediaWiki + auto-selects the 'mysqli' driver since MediaWiki 1.22, except if explicitly + requested through the configuration parameter $wgDBservers. +* $wgOOUIEditPage was removed, as it is now the default. This was documented as a + temporary variable during the migration period. + +=== New features in 1.30 === +* (T37247) Output from Parser::parse() will now be wrapped in a div with + class="mw-parser-output" by default. This may be changed or disabled using + ParserOptions::setWrapOutputClass(). +* (T163562) Added ability to search for contributions within an IP ranges + at Special:Contributions. +* Added 'ChangeTagsAllowedAdd' hook, enabling extensions to allow software- + specific tags to be added by users. +* Added a 'ParserOptionsRegister' hook to allow extensions to register + additional parser options. +* (T45547) Included Pig Latin, a language game in English, as a + LanguageConverter variant. This allows English-speaking developers + to develop and test LanguageConverter more easily. Pig Latin can be + enabled by setting $wgUsePigLatinVariant to true. +* Added RecentChangesPurgeRows hook to allow extensions to purge data that + depends on the recentchanges table. +* Added JS config values wgDiffOldId/wgDiffNewId to the output of diff pages. +* (T2424) Added direct unwatch links to entries in Special:Watchlist (if the + 'watchlistunwatchlinks' preference option is enabled). With JavaScript + enabled, these links toggle so the user can also re-watch pages that have + just been unwatched. +* Added $wgParserTestMediaHandlers, where mock media handlers can be passed to + MediaHandlerFactory for parser tests. +* Edit summaries, block reasons, and other "comments" are now stored in a + separate database table. Use the CommentFormatter class to access them. +** This is currently gated by $wgCommentTableSchemaMigrationStage. Most wikis + can set this to MIGRATION_NEW and run maintenance/migrateComments.php as + soon as any necessary extensions are updated. +* (T138166) Added ability for users to prohibit other users from sending them + emails with Special:Emailuser. Can be enabled by setting + $wgEnableUserEmailBlacklist to true. +* (T67297) $wgBrowserBlacklist is deprecated, and changing it will have no effect. + Instead, users using browsers that do not support Unicode will be unable to edit + and should upgrade to a modern browser instead. + +=== External library changes in 1.30 === + +==== Upgraded external libraries ==== +* Updated justinrainbow/json-schema from v3.0 to v5.2. +* Updated mediawiki/mediawiki-codesniffer from v0.7.2 to v0.12.0. +* Updated wikimedia/composer-merge-plugin from v1.4.0 to v1.4.1. +* Updated wikimedia/relpath from v1.0.3 to v2.0.0. +* Updated OOjs from v2.0.0 to v2.1.0. +* Updated OOUI from v0.21.1 to v0.23.0. +* Updated QUnit from v1.23.1 to v2.4.0. +* Updated phpunit/phpunit from v4.8.35 to v4.8.36. +* Upgraded Moment.js from v2.15.0 to v2.19.3. + +==== New external libraries ==== +* The class \TestingAccessWrapper has been moved to the external library + wikimedia/testing-access-wrapper and renamed \Wikimedia\TestingAccessWrapper. +* Purtle, a fast, lightweight RDF generator. + +==== Removed and replaced external libraries ==== +* … + +=== Bug fixes in 1.30 === +* (T151633) Ordered list items use now Devanagari digits in Nepalese + (thanks to Sfic) + +=== Action API changes in 1.30 === +* (T37247) action=parse output will be wrapped in a div with + class="mw-parser-output" by default. This may be changed or disabled using + the new 'wrapoutputclass' parameter. +* When errorformat is not 'bc', abort reasons from action=login will be + formatted as specified by the error formatter parameters. +* action=compare can now handle arbitrary text, deleted revisions, and + returning users and edit comments. +* (T164106) The 'rvdifftotext', 'rvdifftotextpst', 'rvdiffto', + 'rvexpandtemplates', 'rvgeneratexml', 'rvparse', and 'rvprop=parsetree' + parameters to prop=revisions are deprecated, as are the similarly named + parameters to prop=deletedrevisions, list=allrevisions, and + list=alldeletedrevisions. Use action=compare, action=parse, or + action=expandtemplates instead. + +=== Action API internal changes in 1.30 === +* ApiBase::getDescriptionMessage() and the "apihelp-*-description" messages are + deprecated. The existing message should be split between "apihelp-*-summary" + and "apihelp-*-extended-description". +* (T123931) Individual values of multi-valued parameters can now be marked as + deprecated. + +=== Languages updated in 1.30 === +MediaWiki supports over 350 languages. Many localisations are updated +regularly. Below only new and removed languages are listed, as well as +changes to languages because of Phabricator reports. + +* Added: kbp (Kabɩyɛ / Kabiyè) +* Added: skr (Saraiki, سرائیکی) +* Added: tay (Tayal / Atayal) +* Removed: tokipona (Toki Pona) + +==== Pig Latin added ==== +* (T45547) Added Pig Latin, a made-up English variant (en-x-piglatin), + for easier variant development and testing. Disabled by default. It can be + enabled by setting $wgUsePigLatinVariant to true. + +=== Other changes in 1.30 === +* The use of an associative array for $wgProxyList, where the IP address is in + the key instead of the value, is deprecated (e.g. [ '127.0.0.1' => 'value' ]). + Please convert these arrays to indexed/sequential ones (e.g. [ '127.0.0.1' ]). +* mw.user.bucket (deprecated in 1.23) was removed. +* LoadBalancer::getServerInfo() and LoadBalancer::setServerInfo() are + deprecated. There are no known callers. +* File::getStreamHeaders() was deprecated. +* MediaHandler::getStreamHeaders() was deprecated. +* Title::canTalk() was deprecated. The new Title::canHaveTalkPage() should be + used instead. +* MWNamespace::canTalk() was deprecated. The new MWNamespace::hasTalkNamespace() + should be used instead. +* The ExtractThumbParameters hook (deprecated in 1.21) was removed. +* The OutputPage::addParserOutputNoText and ::getHeadLinks methods (both + deprecated in 1.24) were removed. +* wfMemcKey() and wfGlobalCacheKey() were deprecated. BagOStuff::makeKey() and + BagOStuff::makeGlobalKey() should be used instead. +* (T146304) Preprocessor handling of LanguageConverter markup has been improved. + As a result of the new uniform handling, '-{' may need to be escaped + (for example, as '-{') where it occurs inside template arguments + or wikilinks. +* (T163966) Page moves are now counted as edits for the purposes of + autopromotion, i.e., they increment the user_editcount field in the database. +* Two new hooks, LogEventsListLineEnding and NewPagesLineEnding, were added for + manipulating Special:Log and Special:NewPages lines. +* The OldChangesListRecentChangesLine, EnhancedChangesListModifyLineData, + PageHistoryLineEnding, ContributionsLineEnding and DeletedContributionsLineEnding + hooks have an additional parameter, for manipulating HTML data attributes of + RC/history lines. EnhancedChangesListModifyBlockLineData can do that via the + $data['attribs'] subarray. +* (T130632) The OutputPage::enableTOC() method was removed. +* WikiPage::getParserOutput() will now throw an exception if passed + ParserOptions that would pollute the parser cache. Callers should use + WikiPage::makeParserOptions() to create the ParserOptions object and only + change options that affect the parser cache key. +* Article::viewRedirect() is deprecated. +* IP::isValidBlock() was deprecated. Use the equivalent IP::isValidRange(). +* DeprecatedGlobal no longer supports passing in a direct value, it requires a + callable factory function or a class name. +* The $parserMemc global, wfGetParserCacheStorage(), and ParserCache::singleton() + are all deprecated. The main ParserCache instance should be obtained from + MediaWikiServices instead. Access to the underlying BagOStuff is possible + through the new ParserCache::getCacheStorage() method. +* .mw-ui-constructive CSS class (deprecated in 1.27) was removed. +* Sanitizer::escapeId() was deprecated, use escapeIdForAttribute(), + escapeIdForLink() or escapeIdForExternalInterwiki() instead. +* Title::escapeFragmentForURL() was deprecated, use one of the aforementioned + Sanitizer functions or, if possible, Title::getFragmentForURL(). +* Second parameter to Sanitizer::escapeIdReferenceList() ($options) now does + nothing and is deprecated. +* mw.util.escapeId() was deprecated, use escapeIdForAttribute() or + escapeIdForLink(). +* MagicWord::replaceMultiple() (deprecated in 1.25) was removed. +* WikiImporter now requires the second parameter to be an instance of the Config, + class. Prior to that, the Config parameter was optional (a behavior deprecated in + 1.25). +* Removed 'jquery.mwExtension' module. (deprecated since 1.26) +* mediawiki.ui: Deprecate greys, which are not part of WikimediaUI color palette + any more. +* CdbReader, CdbWriter, CdbException classes (deprecated in 1.25) were removed. + The namespaced classes in the Cdb namespace should be used instead. +* IPSet class (deprecated in 1.26) was removed. The namespaced IPSet\IPSet + should be used instead. +* RunningStat class (deprecated in 1.27) was removed. The namespaced + RunningStat\RunningStat should be used instead. +* MWMemcached and MemCachedClientforWiki classes (deprecated in 1.27) were removed. + The MemcachedClient class should be used instead. +* EditPage underwent some refactoring and deprecations: + * EditPage::isOouiEnabled() is deprecated and will always return true. + * EditPage::getSummaryInput() and ::getSummaryInputOOUI() are deprecated. Please + use ::getSummaryInputWidget() instead. + * EditPage::getCheckboxes() and ::getCheckboxesOOUI() are deprecated. Please + use ::getCheckboxesWidget() instead. + * Creating an EditPage instance without calling EditPage::setContextTitle() should + be avoided and will be deprecated in a future release. + * EditPage::safeUnicodeInput() and ::safeUnicodeOutput() are deprecated and no-ops. + * EditPage::$isCssJsSubpage, ::$isCssSubpage, and ::$isJsSubpage are deprecated. The + corresponding methods from Title should be used instead. + * EditPage::$isWrongCaseCssJsPage is deprecated. There is no replacement. + * EditPage::$mArticle and ::$mTitle are deprecated for public usage. The getters + ::getArticle() and ::getTitle() should be used instead. + * Trying to control or fake EditPage context by overriding $wgUser, $wgRequest, $wgOut, + and $wgLang is no longer supported and won't work. The IContextSource returned from + EditPage::getContext() must be modified instead. +* Parser::getRandomString() (deprecated in 1.26) was removed. +* Parser::uniqPrefix() (deprecated in 1.26) was removed. +* Parser::extractTagsAndParams() now only accepts three arguments. The fourth, + $uniq_prefix was deprecated in 1.26 and has now been removed. +* (T172514) The following tables have had their UNIQUE indexes turned into proper + PRIMARY KEYs for increased maintainability: categorylinks, imagelinks, iwlinks, + langlinks, log_search, module_deps, objectcache, pagelinks, query_cache, site_stats, + templatelinks, text, transcache, user_former_groups, user_properties. +* IDatabase::nextSequenceValue() is no longer needed by any database backends + (formerly it was needed by PostgreSQL and Oracle), and is now deprecated. +* (T146591) The lc_lang_key index on the l10n_cache table has been changed into a + PRIMARY KEY. +* (T157227) bot_password.bp_user, change_tag.ct_log_id, change_tag.ct_rev_id, + page_restrictions.pr_user, tag_summary.ts_log_id, tag_summary.ts_rev_id and + user_properties.up_user have all been made unsigned on MySQL. +* DB_SLAVE is deprecated. DB_REPLICA should be used instead. +* wfUsePHP() is deprecated. +* wfFixSessionID() was removed. +* wfShellExec() and related functions are deprecated, use Shell::command(). This also + slightly changes the behavior of how execution time limits are calculated when only + some of defaults are overridden per-call. When in doubt, always override both wall + clock and CPU time. +* (T138166) SpecialEmailUser::getTarget() now requires a second argument, the sending + user object. Using the method without the second argument is deprecated. +* (T67297) Browsers that don't support Unicode will have their edits rejected. +* (T178450) The module 'jquery.badge' is deprecated and will be removed in a future + release. For notifying the user of an event, the Notifications ("Echo") system + should be used instead. +* (T178451) SECURITY: Potential XSS when $wgShowExceptionDetails = false and browser + sends non-standard url escaping. +* (T165846) SECURITY: BotPassword login attempts weren't throttled. = MediaWiki 1.29 = diff --git a/RELEASE-NOTES-1.30 b/RELEASE-NOTES-1.30 deleted file mode 100644 index 1449dab47e..0000000000 --- a/RELEASE-NOTES-1.30 +++ /dev/null @@ -1,304 +0,0 @@ -== MediaWiki 1.30 == - -THIS IS NOT A RELEASE YET - -MediaWiki 1.30 is an alpha-quality branch and is not recommended for use in -production. - -=== MySQL version requirement in 1.30 === -As of 1.30, MediaWiki now requires MySQL 5.5.8 or higher (see Compatibility -section). - -=== Configuration changes in 1.30 === -* The "C.UTF-8" locale should be used for $wgShellLocale, if available, to avoid - unexpected behavior when code uses locale-sensitive string comparisons. For - example, the Scribunto extension considers "bar" < "Foo" in most locales - since it ignores case. -* $wgShellLocale now affects LC_ALL rather than only LC_CTYPE. See - documentation of $wgShellLocale for details. -* $wgShellLocale is now applied for all requests. wfInitShellLocale() is - deprecated and a no-op, as it is no longer needed. -* $wgJobClasses may now specify callback functions as an alternative to plain - class names. This is intended for extensions that want control over the - instantiation of their jobs, to allow for proper dependency injection. -* $wgResourceModules may now specify callback functions as an alternative - to plain class names, using the 'factory' key in the module description - array. This allows dependency injection to be used for ResourceLoader modules. -* $wgExceptionHooks has been removed. -* (T163562) $wgRangeContributionsCIDRLimit was introduced to control the size - of IP ranges that can be queried at Special:Contributions. -* (T45547) $wgUsePigLatinVariant added (off by default). -* (T152540) MediaWiki now supports a section ID escaping style that allows to display - non-Latin characters verbatim on many modern browsers. This is controlled by the - new configuration setting, $wgFragmentMode. -* $wgExperimentalHtmlIds is now deprecated and will be removed in a future version, - use $wgFragmentMode to migrate off it to a modern alternative. -* $wgExternalInterwikiFragmentMode was introduced to control how fragments in - sinterwikis going outside of current wiki farm are encoded. -* (T120333) Soft-deprecated the use of PHP extension 'mysql' in favor of 'mysqli'. - This PHP extension was deprecated in PHP 5.5 and removed in PHP 7.0. MediaWiki - auto-selects the 'mysqli' driver since MediaWiki 1.22, except if explicitly - requested through the configuration parameter $wgDBservers. -* $wgOOUIEditPage was removed, as it is now the default. This was documented as a - temporary variable during the migration period. - -=== New features in 1.30 === -* (T37247) Output from Parser::parse() will now be wrapped in a div with - class="mw-parser-output" by default. This may be changed or disabled using - ParserOptions::setWrapOutputClass(). -* (T163562) Added ability to search for contributions within an IP ranges - at Special:Contributions. -* Added 'ChangeTagsAllowedAdd' hook, enabling extensions to allow software- - specific tags to be added by users. -* Added a 'ParserOptionsRegister' hook to allow extensions to register - additional parser options. -* (T45547) Included Pig Latin, a language game in English, as a - LanguageConverter variant. This allows English-speaking developers - to develop and test LanguageConverter more easily. Pig Latin can be - enabled by setting $wgUsePigLatinVariant to true. -* Added RecentChangesPurgeRows hook to allow extensions to purge data that - depends on the recentchanges table. -* Added JS config values wgDiffOldId/wgDiffNewId to the output of diff pages. -* (T2424) Added direct unwatch links to entries in Special:Watchlist (if the - 'watchlistunwatchlinks' preference option is enabled). With JavaScript - enabled, these links toggle so the user can also re-watch pages that have - just been unwatched. -* Added $wgParserTestMediaHandlers, where mock media handlers can be passed to - MediaHandlerFactory for parser tests. -* Edit summaries, block reasons, and other "comments" are now stored in a - separate database table. Use the CommentFormatter class to access them. -** This is currently gated by $wgCommentTableSchemaMigrationStage. Most wikis - can set this to MIGRATION_NEW and run maintenance/migrateComments.php as - soon as any necessary extensions are updated. -* (T138166) Added ability for users to prohibit other users from sending them - emails with Special:Emailuser. Can be enabled by setting - $wgEnableUserEmailBlacklist to true. -* (T67297) $wgBrowserBlacklist is deprecated, and changing it will have no effect. - Instead, users using browsers that do not support Unicode will be unable to edit - and should upgrade to a modern browser instead. - -=== External library changes in 1.30 === - -==== Upgraded external libraries ==== -* Updated justinrainbow/json-schema from v3.0 to v5.2. -* Updated mediawiki/mediawiki-codesniffer from v0.7.2 to v0.12.0. -* Updated wikimedia/composer-merge-plugin from v1.4.0 to v1.4.1. -* Updated wikimedia/relpath from v1.0.3 to v2.0.0. -* Updated OOjs from v2.0.0 to v2.1.0. -* Updated OOUI from v0.21.1 to v0.23.0. -* Updated QUnit from v1.23.1 to v2.4.0. -* Updated phpunit/phpunit from v4.8.35 to v4.8.36. - -==== New external libraries ==== -* The class \TestingAccessWrapper has been moved to the external library - wikimedia/testing-access-wrapper and renamed \Wikimedia\TestingAccessWrapper. -* Purtle, a fast, lightweight RDF generator. - -==== Removed and replaced external libraries ==== -* … - -=== Bug fixes in 1.30 === -* (T151633) Ordered list items use now Devanagari digits in Nepalese - (thanks to Sfic) - -=== Action API changes in 1.30 === -* (T37247) action=parse output will be wrapped in a div with - class="mw-parser-output" by default. This may be changed or disabled using - the new 'wrapoutputclass' parameter. -* When errorformat is not 'bc', abort reasons from action=login will be - formatted as specified by the error formatter parameters. -* action=compare can now handle arbitrary text, deleted revisions, and - returning users and edit comments. -* (T164106) The 'rvdifftotext', 'rvdifftotextpst', 'rvdiffto', - 'rvexpandtemplates', 'rvgeneratexml', 'rvparse', and 'rvprop=parsetree' - parameters to prop=revisions are deprecated, as are the similarly named - parameters to prop=deletedrevisions, list=allrevisions, and - list=alldeletedrevisions. Use action=compare, action=parse, or - action=expandtemplates instead. - -=== Action API internal changes in 1.30 === -* ApiBase::getDescriptionMessage() and the "apihelp-*-description" messages are - deprecated. The existing message should be split between "apihelp-*-summary" - and "apihelp-*-extended-description". -* (T123931) Individual values of multi-valued parameters can now be marked as - deprecated. - -=== Languages updated in 1.30 === -MediaWiki supports over 350 languages. Many localisations are updated -regularly. Below only new and removed languages are listed, as well as -changes to languages because of Phabricator reports. - -* Added: kbp (Kabɩyɛ / Kabiyè) -* Added: skr (Saraiki, سرائیکی) -* Added: tay (Tayal / Atayal) -* Removed: tokipona (Toki Pona) - -==== Pig Latin added ==== -* (T45547) Added Pig Latin, a made-up English variant (en-x-piglatin), - for easier variant development and testing. Disabled by default. It can be - enabled by setting $wgUsePigLatinVariant to true. - -=== Other changes in 1.30 === -* The use of an associative array for $wgProxyList, where the IP address is in - the key instead of the value, is deprecated (e.g. [ '127.0.0.1' => 'value' ]). - Please convert these arrays to indexed/sequential ones (e.g. [ '127.0.0.1' ]). -* mw.user.bucket (deprecated in 1.23) was removed. -* LoadBalancer::getServerInfo() and LoadBalancer::setServerInfo() are - deprecated. There are no known callers. -* File::getStreamHeaders() was deprecated. -* MediaHandler::getStreamHeaders() was deprecated. -* Title::canTalk() was deprecated. The new Title::canHaveTalkPage() should be - used instead. -* MWNamespace::canTalk() was deprecated. The new MWNamespace::hasTalkNamespace() - should be used instead. -* The ExtractThumbParameters hook (deprecated in 1.21) was removed. -* The OutputPage::addParserOutputNoText and ::getHeadLinks methods (both - deprecated in 1.24) were removed. -* wfMemcKey() and wfGlobalCacheKey() were deprecated. BagOStuff::makeKey() and - BagOStuff::makeGlobalKey() should be used instead. -* (T146304) Preprocessor handling of LanguageConverter markup has been improved. - As a result of the new uniform handling, '-{' may need to be escaped - (for example, as '-{') where it occurs inside template arguments - or wikilinks. -* (T163966) Page moves are now counted as edits for the purposes of - autopromotion, i.e., they increment the user_editcount field in the database. -* Two new hooks, LogEventsListLineEnding and NewPagesLineEnding, were added for - manipulating Special:Log and Special:NewPages lines. -* The OldChangesListRecentChangesLine, EnhancedChangesListModifyLineData, - PageHistoryLineEnding, ContributionsLineEnding and DeletedContributionsLineEnding - hooks have an additional parameter, for manipulating HTML data attributes of - RC/history lines. EnhancedChangesListModifyBlockLineData can do that via the - $data['attribs'] subarray. -* (T130632) The OutputPage::enableTOC() method was removed. -* WikiPage::getParserOutput() will now throw an exception if passed - ParserOptions that would pollute the parser cache. Callers should use - WikiPage::makeParserOptions() to create the ParserOptions object and only - change options that affect the parser cache key. -* Article::viewRedirect() is deprecated. -* IP::isValidBlock() was deprecated. Use the equivalent IP::isValidRange(). -* DeprecatedGlobal no longer supports passing in a direct value, it requires a - callable factory function or a class name. -* The $parserMemc global, wfGetParserCacheStorage(), and ParserCache::singleton() - are all deprecated. The main ParserCache instance should be obtained from - MediaWikiServices instead. Access to the underlying BagOStuff is possible - through the new ParserCache::getCacheStorage() method. -* .mw-ui-constructive CSS class (deprecated in 1.27) was removed. -* Sanitizer::escapeId() was deprecated, use escapeIdForAttribute(), - escapeIdForLink() or escapeIdForExternalInterwiki() instead. -* Title::escapeFragmentForURL() was deprecated, use one of the aforementioned - Sanitizer functions or, if possible, Title::getFragmentForURL(). -* Second parameter to Sanitizer::escapeIdReferenceList() ($options) now does - nothing and is deprecated. -* mw.util.escapeId() was deprecated, use escapeIdForAttribute() or - escapeIdForLink(). -* MagicWord::replaceMultiple() (deprecated in 1.25) was removed. -* WikiImporter now requires the second parameter to be an instance of the Config, - class. Prior to that, the Config parameter was optional (a behavior deprecated in - 1.25). -* Removed 'jquery.mwExtension' module. (deprecated since 1.26) -* mediawiki.ui: Deprecate greys, which are not part of WikimediaUI color palette - any more. -* CdbReader, CdbWriter, CdbException classes (deprecated in 1.25) were removed. - The namespaced classes in the Cdb namespace should be used instead. -* IPSet class (deprecated in 1.26) was removed. The namespaced IPSet\IPSet - should be used instead. -* RunningStat class (deprecated in 1.27) was removed. The namespaced - RunningStat\RunningStat should be used instead. -* MWMemcached and MemCachedClientforWiki classes (deprecated in 1.27) were removed. - The MemcachedClient class should be used instead. -* EditPage underwent some refactoring and deprecations: - * EditPage::isOouiEnabled() is deprecated and will always return true. - * EditPage::getSummaryInput() and ::getSummaryInputOOUI() are deprecated. Please - use ::getSummaryInputWidget() instead. - * EditPage::getCheckboxes() and ::getCheckboxesOOUI() are deprecated. Please - use ::getCheckboxesWidget() instead. - * Creating an EditPage instance without calling EditPage::setContextTitle() should - be avoided and will be deprecated in a future release. - * EditPage::safeUnicodeInput() and ::safeUnicodeOutput() are deprecated and no-ops. - * EditPage::$isCssJsSubpage, ::$isCssSubpage, and ::$isJsSubpage are deprecated. The - corresponding methods from Title should be used instead. - * EditPage::$isWrongCaseCssJsPage is deprecated. There is no replacement. - * EditPage::$mArticle and ::$mTitle are deprecated for public usage. The getters - ::getArticle() and ::getTitle() should be used instead. - * Trying to control or fake EditPage context by overriding $wgUser, $wgRequest, $wgOut, - and $wgLang is no longer supported and won't work. The IContextSource returned from - EditPage::getContext() must be modified instead. -* Parser::getRandomString() (deprecated in 1.26) was removed. -* Parser::uniqPrefix() (deprecated in 1.26) was removed. -* Parser::extractTagsAndParams() now only accepts three arguments. The fourth, - $uniq_prefix was deprecated in 1.26 and has now been removed. -* (T172514) The following tables have had their UNIQUE indexes turned into proper - PRIMARY KEYs for increased maintainability: categorylinks, imagelinks, iwlinks, - langlinks, log_search, module_deps, objectcache, pagelinks, query_cache, site_stats, - templatelinks, text, transcache, user_former_groups, user_properties. -* IDatabase::nextSequenceValue() is no longer needed by any database backends - (formerly it was needed by PostgreSQL and Oracle), and is now deprecated. -* (T146591) The lc_lang_key index on the l10n_cache table has been changed into a - PRIMARY KEY. -* (T157227) bot_password.bp_user, change_tag.ct_log_id, change_tag.ct_rev_id, - page_restrictions.pr_user, tag_summary.ts_log_id, tag_summary.ts_rev_id and - user_properties.up_user have all been made unsigned on MySQL. -* DB_SLAVE is deprecated. DB_REPLICA should be used instead. -* wfUsePHP() is deprecated. -* wfFixSessionID() was removed. -* wfShellExec() and related functions are deprecated, use Shell::command(). This also - slightly changes the behavior of how execution time limits are calculated when only - some of defaults are overridden per-call. When in doubt, always override both wall - clock and CPU time. -* (T138166) SpecialEmailUser::getTarget() now requires a second argument, the sending - user object. Using the method without the second argument is deprecated. -* (T67297) Browsers that don't support Unicode will have their edits rejected. -* (T178450) The module 'jquery.badge' is deprecated and will be removed in a future - release. For notifying the user of an event, the Notifications ("Echo") system - should be used instead. - -== Compatibility == -MediaWiki 1.30 requires PHP 5.5.9 or later. There is experimental support for -HHVM 3.6.5 or later. - -MySQL/MariaDB is the recommended DBMS. PostgreSQL or SQLite can also be used, -but support for them is somewhat less mature. There is experimental support for -Oracle and Microsoft SQL Server. - -The supported versions are: - -* MySQL 5.5.8 or later -* PostgreSQL 8.3 or later -* SQLite 3.3.7 or later -* Oracle 9.0.1 or later -* Microsoft SQL Server 2005 (9.00.1399) - -== Upgrading == -1.30 has several database changes since 1.29, and will not work without schema -updates. Note that due to changes to some very large tables like the revision -table, the schema update may take a long time (minutes on a medium sized site, -many hours on a large site). - -Don't forget to always back up your database before upgrading! - -See the file UPGRADE for more detailed upgrade instructions, including -important information when upgrading from versions prior to 1.11. - -For notes on 1.29.x and older releases, see HISTORY. - -== Online documentation == -Documentation for both end-users and site administrators is available on -MediaWiki.org, and is covered under the GNU Free Documentation License (except -for pages that explicitly state that their contents are in the public domain): - - https://www.mediawiki.org/wiki/Special:MyLanguage/Documentation - -== Mailing list == -A mailing list is available for MediaWiki user support and discussion: - - https://lists.wikimedia.org/mailman/listinfo/mediawiki-l - -A low-traffic announcements-only list is also available: - - https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce - -It's highly recommended that you sign up for one of these lists if you're -going to run a public MediaWiki, so you can be notified of security fixes. - -== IRC help == -There's usually someone online in #mediawiki on irc.freenode.net. diff --git a/RELEASE-NOTES-1.31 b/RELEASE-NOTES-1.31 index 4a2876d500..a496b02da9 100644 --- a/RELEASE-NOTES-1.31 +++ b/RELEASE-NOTES-1.31 @@ -19,6 +19,8 @@ production. maintenance/cleanupUsersWithNoId.php. * $wgResourceLoaderMinifierStatementsOnOwnLine and $wgResourceLoaderMinifierMaxLineLength were removed (deprecated since 1.27). +* (T180921) $wgReferrerPolicy now supports having fallbacks for browsers that are not + using the latest version of the Referrer Policy specification. === New features in 1.31 === * Wikimedia\Rdbms\IDatabase->select() and similar methods now support @@ -39,6 +41,7 @@ production. === External library changes in 1.31 === ==== Upgraded external libraries ==== +* Updated jquery.chosen from v0.9.14 to v1.8.2. * … ==== New external libraries ==== @@ -71,6 +74,10 @@ changes to languages because of Phabricator reports. * (T180052) Mirandese (mwl) now supports gendered NS_USER/NS_USER_TALK namespaces. === Other changes in 1.31 === +* Introducing multi-content-revision capability into the storage layer. For details, + see . +* The Revision class was deprecated in favor of RevisionStore, BlobStore, and + RevisionRecord and its subclasses. * MessageBlobStore::insertMessageBlob() (deprecated in 1.27) was removed. * The global function wfBCP47 was renamed to LanguageCode::bcp47. * The global function wfBCP47 is now deprecated. @@ -123,6 +130,9 @@ changes to languages because of Phabricator reports. * The Block class will no longer accept usable-but-missing usernames for 'byText' or ->setBlocker(). Callers should either ensure the blocker exists locally or use a new interwiki-format username like "iw>Example". +* The RevisionInsertComplete hook is now deprecated, use RevisionRecordInserted instead. + RevisionInsertComplete is still called, but the second and third parameter will always be null. + Hard deprecation is scheduled for 1.32. * The following methods that get and set ParserOutput state are deprecated. Callers should use the new stateless $options parameter to ParserOutput::getText() instead. @@ -135,6 +145,20 @@ changes to languages because of Phabricator reports. * OutputPage::enableSectionEditLinks() * OutputPage::sectionEditLinksEnabled() * The public ParserOutput state fields $mTOCEnabled and $mEditSectionTokens are also deprecated. +* The following methods and constants from the WatchedItem class were deprecated in + 1.27 have been removed. + * WatchedItem::getTitle() + * WatchedItem::fromUserTitle() + * WatchedItem::addWatch() + * WatchedItem::removeWatch() + * WatchedItem::isWatched() + * WatchedItem::duplicateEntries() + * WatchedItem::IGNORE_USER_RIGHTS + * WatchedItem::CHECK_USER_RIGHTS + * WatchedItem::DEPRECATED_USAGE_TIMESTAMP +* The $statementsOnOwnLine parameter of JavaScriptMinifier::minify was removed. + The corresponding configuration variable ($wgResourceLoaderMinifierStatementsOnOwnLine) + has been deprecated since 1.27 and was removed as well. == Compatibility == MediaWiki 1.31 requires PHP 5.5.9 or later. There is experimental support for diff --git a/autoload.php b/autoload.php index 2661fd7ed3..c37d9f71f3 100644 --- a/autoload.php +++ b/autoload.php @@ -449,7 +449,7 @@ $wgAutoloadLocalClasses = [ 'Exif' => __DIR__ . '/includes/media/Exif.php', 'ExifBitmapHandler' => __DIR__ . '/includes/media/ExifBitmap.php', 'ExplodeIterator' => __DIR__ . '/includes/libs/ExplodeIterator.php', - 'ExportProgressFilter' => __DIR__ . '/maintenance/backup.inc', + 'ExportProgressFilter' => __DIR__ . '/includes/export/ExportProgressFilter.php', 'ExportSites' => __DIR__ . '/maintenance/exportSites.php', 'ExtensionJsonValidationError' => __DIR__ . '/includes/registration/ExtensionJsonValidationError.php', 'ExtensionJsonValidator' => __DIR__ . '/includes/registration/ExtensionJsonValidator.php', @@ -892,9 +892,6 @@ $wgAutoloadLocalClasses = [ 'MediaWiki\\Languages\\Data\\CrhExceptions' => __DIR__ . '/languages/data/CrhExceptions.php', 'MediaWiki\\Languages\\Data\\Names' => __DIR__ . '/languages/data/Names.php', 'MediaWiki\\Languages\\Data\\ZhConversion' => __DIR__ . '/languages/data/ZhConversion.php', - 'MediaWiki\\Linker\\LinkRenderer' => __DIR__ . '/includes/linker/LinkRenderer.php', - 'MediaWiki\\Linker\\LinkRendererFactory' => __DIR__ . '/includes/linker/LinkRendererFactory.php', - 'MediaWiki\\Linker\\LinkTarget' => __DIR__ . '/includes/linker/LinkTarget.php', 'MediaWiki\\Logger\\ConsoleLogger' => __DIR__ . '/includes/debug/logger/ConsoleLogger.php', 'MediaWiki\\Logger\\ConsoleSpi' => __DIR__ . '/includes/debug/logger/ConsoleSpi.php', 'MediaWiki\\Logger\\LegacyLogger' => __DIR__ . '/includes/debug/logger/LegacyLogger.php', @@ -945,6 +942,23 @@ $wgAutoloadLocalClasses = [ 'MediaWiki\\Shell\\Result' => __DIR__ . '/includes/shell/Result.php', 'MediaWiki\\Shell\\Shell' => __DIR__ . '/includes/shell/Shell.php', 'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php', + 'MediaWiki\\Storage\\BlobAccessException' => __DIR__ . '/includes/Storage/BlobAccessException.php', + 'MediaWiki\\Storage\\BlobStore' => __DIR__ . '/includes/Storage/BlobStore.php', + 'MediaWiki\\Storage\\BlobStoreFactory' => __DIR__ . '/includes/Storage/BlobStoreFactory.php', + '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\\RevisionAccessException' => __DIR__ . '/includes/Storage/RevisionAccessException.php', + 'MediaWiki\\Storage\\RevisionArchiveRecord' => __DIR__ . '/includes/Storage/RevisionArchiveRecord.php', + 'MediaWiki\\Storage\\RevisionFactory' => __DIR__ . '/includes/Storage/RevisionFactory.php', + 'MediaWiki\\Storage\\RevisionLookup' => __DIR__ . '/includes/Storage/RevisionLookup.php', + 'MediaWiki\\Storage\\RevisionRecord' => __DIR__ . '/includes/Storage/RevisionRecord.php', + 'MediaWiki\\Storage\\RevisionSlots' => __DIR__ . '/includes/Storage/RevisionSlots.php', + 'MediaWiki\\Storage\\RevisionStore' => __DIR__ . '/includes/Storage/RevisionStore.php', + 'MediaWiki\\Storage\\RevisionStoreRecord' => __DIR__ . '/includes/Storage/RevisionStoreRecord.php', + 'MediaWiki\\Storage\\SlotRecord' => __DIR__ . '/includes/Storage/SlotRecord.php', + 'MediaWiki\\Storage\\SqlBlobStore' => __DIR__ . '/includes/Storage/SqlBlobStore.php', + 'MediaWiki\\Storage\\SuppressedDataException' => __DIR__ . '/includes/Storage/SuppressedDataException.php', 'MediaWiki\\Tidy\\BalanceActiveFormattingElements' => __DIR__ . '/includes/tidy/Balancer.php', 'MediaWiki\\Tidy\\BalanceElement' => __DIR__ . '/includes/tidy/Balancer.php', 'MediaWiki\\Tidy\\BalanceMarker' => __DIR__ . '/includes/tidy/Balancer.php', @@ -964,6 +978,7 @@ $wgAutoloadLocalClasses = [ 'MediaWiki\\Tidy\\RemexMungerData' => __DIR__ . '/includes/tidy/RemexMungerData.php', 'MediaWiki\\Tidy\\TidyDriverBase' => __DIR__ . '/includes/tidy/TidyDriverBase.php', 'MediaWiki\\User\\UserIdentity' => __DIR__ . '/includes/user/UserIdentity.php', + 'MediaWiki\\User\\UserIdentityValue' => __DIR__ . '/includes/user/UserIdentityValue.php', 'MediaWiki\\Widget\\ComplexNamespaceInputWidget' => __DIR__ . '/includes/widget/ComplexNamespaceInputWidget.php', 'MediaWiki\\Widget\\ComplexTitleInputWidget' => __DIR__ . '/includes/widget/ComplexTitleInputWidget.php', 'MediaWiki\\Widget\\DateInputWidget' => __DIR__ . '/includes/widget/DateInputWidget.php', @@ -1040,6 +1055,7 @@ $wgAutoloadLocalClasses = [ 'NewPagesPager' => __DIR__ . '/includes/specials/pagers/NewPagesPager.php', 'NewUsersLogFormatter' => __DIR__ . '/includes/logging/NewUsersLogFormatter.php', 'NolinesImageGallery' => __DIR__ . '/includes/gallery/NolinesImageGallery.php', + 'NorthernSamiUppercaseCollation' => __DIR__ . '/includes/collation/NorthernSamiUppercaseCollation.php', 'NotRecursiveIterator' => __DIR__ . '/includes/libs/iterators/NotRecursiveIterator.php', 'NukeNS' => __DIR__ . '/maintenance/nukeNS.php', 'NukePage' => __DIR__ . '/maintenance/nukePage.php', diff --git a/docs/extension.schema.v1.json b/docs/extension.schema.v1.json index 7cfebcafa4..ddf82e8d9e 100644 --- a/docs/extension.schema.v1.json +++ b/docs/extension.schema.v1.json @@ -567,6 +567,10 @@ "type": "object", "description": "SpecialPages implemented in this extension (mapping of page name to class name)" }, + "AutoloadNamespaces": { + "type": "object", + "description": "Mapping of PSR-4 compliant namespace to directory for autoloading" + }, "AutoloadClasses": { "type": "object" }, diff --git a/docs/extension.schema.v2.json b/docs/extension.schema.v2.json index 75a4f2c6fc..0bdf97d41e 100644 --- a/docs/extension.schema.v2.json +++ b/docs/extension.schema.v2.json @@ -588,6 +588,10 @@ "type": "object", "description": "SpecialPages implemented in this extension (mapping of page name to class name)" }, + "AutoloadNamespaces": { + "type": "object", + "description": "Mapping of PSR-4 compliant namespace to directory for autoloading" + }, "AutoloadClasses": { "type": "object" }, diff --git a/docs/hooks.txt b/docs/hooks.txt index 29883b25a6..45387a386f 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -951,7 +951,7 @@ $id: the page ID (original ID in case of page deletions) in a Category page. Gives extensions the opportunity to batch load any related data about the pages. $type: The category type. Either 'page', 'file' or 'subcat' -$res: Query result from DatabaseBase::select() +$res: Query result from Wikimedia\Rdbms\IDatabase::select() 'CategoryViewer::generateLink': Before generating an output link allow extensions opportunity to generate a more specific or relevant link. @@ -1840,7 +1840,7 @@ $revisionInfo: Array of revision information Return false to stop further processing of the tag $reader: XMLReader object -'ImportHandleUnknownUser': When a user does exist locally, this hook is called +'ImportHandleUnknownUser': When a user doesn't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name: User name @@ -2810,14 +2810,14 @@ called after the addition of 'qunit' and MediaWiki testing resources. added to any module. &$ResourceLoader: object -'RevisionInsertComplete': Called after a revision is inserted into the database. -&$revision: the Revision -$data: the data stored in old_text. The meaning depends on $flags: if external - is set, it's the URL of the revision text in external storage; otherwise, - it's the revision text itself. In either case, if gzip is set, the revision - text is gzipped. -$flags: a comma-delimited list of strings representing the options used. May - include: utf8 (this will always be set for new revisions); gzip; external. +'RevisionRecordInserted': Called after a revision is inserted into the database. +$revisionRecord: the RevisionRecord that has just been inserted. + +'RevisionInsertComplete': DEPRECATED! Use RevisionRecordInserted hook instead. +Called after a revision is inserted into the database. +$revision: the Revision +$data: DEPRECATED! Always null! +$flags: DEPRECATED! Always null! 'SearchableNamespaces': An option to modify which namespaces are searchable. &$arr: Array of namespaces ($nsId => $name) which will be used. diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 8dc7d4094a..52410fede8 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -30,6 +30,12 @@ require_once __DIR__ . '/../autoload.php'; class AutoLoader { static protected $autoloadLocalClassesLower = null; + /** + * @private Only public for ExtensionRegistry + * @var string[] Namespace (ends with \) => Path (ends with /) + */ + static public $psr4Namespaces = []; + /** * autoload - take a class name and attempt to load it * @@ -67,6 +73,28 @@ class AutoLoader { } } + if ( !$filename && strpos( $className, '\\' ) !== false ) { + // This class is namespaced, so try looking at the namespace map + $prefix = $className; + while ( false !== $pos = strrpos( $prefix, '\\' ) ) { + // Check to see if this namespace prefix is in the map + $prefix = substr( $className, 0, $pos + 1 ); + if ( isset( self::$psr4Namespaces[$prefix] ) ) { + $relativeClass = substr( $className, $pos + 1 ); + // Build the expected filename, and see if it exists + $file = self::$psr4Namespaces[$prefix] . + str_replace( '\\', '/', $relativeClass ) . '.php'; + if ( file_exists( $file ) ) { + $filename = $file; + break; + } + } + + // Remove trailing separator for next iteration + $prefix = rtrim( $prefix, '\\' ); + } + } + if ( !$filename ) { // Class not found; let the next autoloader try to find it return; @@ -88,6 +116,22 @@ class AutoLoader { static function resetAutoloadLocalClassesLower() { self::$autoloadLocalClassesLower = null; } + + /** + * Get a mapping of namespace => file path + * The namespaces should follow the PSR-4 standard for autoloading + * + * @see + * @private Only public for usage in AutoloadGenerator + * @since 1.31 + * @return string[] + */ + public static function getAutoloadNamespaces() { + return [ + 'MediaWiki\\Linker\\' => __DIR__ .'/linker/' + ]; + } } +AutoLoader::$psr4Namespaces = AutoLoader::getAutoloadNamespaces(); spl_autoload_register( [ 'AutoLoader', 'autoload' ] ); diff --git a/includes/CategoryFinder.php b/includes/CategoryFinder.php index 2a70f5f3fd..3561f7f586 100644 --- a/includes/CategoryFinder.php +++ b/includes/CategoryFinder.php @@ -56,6 +56,9 @@ class CategoryFinder { /** @var array Array of article/category IDs */ protected $next = []; + /** @var int Max layer depth **/ + protected $maxdepth = -1; + /** @var array Array of DBKEY category names */ protected $targets = []; @@ -73,12 +76,17 @@ class CategoryFinder { * @param array $articleIds Array of article IDs * @param array $categories FIXME * @param string $mode FIXME, default 'AND'. + * @param int $maxdepth Maximum layer depth. Where: + * -1 means deep recursion (default); + * 0 means no-parents; + * 1 means one parent layer, etc. * @todo FIXME: $categories/$mode */ - public function seed( $articleIds, $categories, $mode = 'AND' ) { + public function seed( $articleIds, $categories, $mode = 'AND', $maxdepth = -1 ) { $this->articles = $articleIds; $this->next = $articleIds; $this->mode = $mode; + $this->maxdepth = $maxdepth; # Set the list of target categories; convert them to DBKEY form first $this->targets = []; @@ -98,8 +106,17 @@ class CategoryFinder { */ public function run() { $this->dbr = wfGetDB( DB_REPLICA ); - while ( count( $this->next ) > 0 ) { + + $i = 0; + $dig = true; + while ( count( $this->next ) && $dig ) { $this->scanNextLayer(); + + // Is there any depth limit? + if ( $this->maxdepth !== -1 ) { + $dig = $i < $this->maxdepth; + $i++; + } } # Now check if this applies to the individual articles diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index c17bf7e452..8091428970 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -316,10 +316,20 @@ $wgAppleTouchIcon = false; /** * Value for the referrer policy meta tag. - * One of 'never', 'default', 'origin', 'always'. Setting it to false just - * prevents the meta tag from being output. - * See https://www.w3.org/TR/referrer-policy/ for details. - * + * One or more of the values defined in the Referrer Policy specification: + * https://w3c.github.io/webappsec-referrer-policy/ + * ('no-referrer', 'no-referrer-when-downgrade', 'same-origin', + * 'origin', 'strict-origin', 'origin-when-cross-origin', + * 'strict-origin-when-cross-origin', or 'unsafe-url') + * Setting it to false prevents the meta tag from being output + * (which results in falling back to the Referrer-Policy header, + * or 'no-referrer-when-downgrade' if that's not set either.) + * Setting it to an array (supported since 1.31) will create a meta tag for + * each value, in the reverse of the order (meaning that the first array element + * will be the default and the others used as fallbacks for browsers which do not + * understand it). + * + * @var array|string|bool * @since 1.25 */ $wgReferrerPolicy = false; @@ -1806,7 +1816,7 @@ $wgDBtype = 'mysql'; /** * Whether to use SSL in DB connection. * - * This setting is only used $wgLBFactoryConf['class'] is set to + * This setting is only used if $wgLBFactoryConf['class'] is set to * 'LBFactorySimple' and $wgDBservers is an empty array; otherwise * the DBO_SSL flag must be set in the 'flags' option of the database * connection to achieve the same functionality. @@ -3763,7 +3773,7 @@ $wgResourceLoaderValidateStaticJS = false; * @code * $wgResourceLoaderLESSVars = [ * 'exampleFontSize' => '1em', - * 'exampleBlue' => '#eee', + * 'exampleBlue' => '#36c', * ]; * @endcode * @since 1.22 @@ -4851,6 +4861,7 @@ $wgDefaultUserOptions = [ 'editfont' => 'monospace', 'editondblclick' => 0, 'editsectiononrightclick' => 0, + 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, @@ -5147,6 +5158,7 @@ $wgGroupPermissions['user']['sendemail'] = true; $wgGroupPermissions['user']['applychangetags'] = true; $wgGroupPermissions['user']['changetags'] = true; $wgGroupPermissions['user']['editcontentmodel'] = true; +$wgGroupPermissions['user']['sendemail-new-users'] = true; // Implicit group for accounts that pass $wgAutoConfirmAge $wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true; @@ -6951,6 +6963,7 @@ $wgUseTagFilter = true; * - 'mw-blank': Edit completely blanks the page * - 'mw-replace': Edit removes more than 90% of the content * - 'mw-rollback': Edit is a rollback, made through the rollback link or rollback API + * - 'mw-undo': Edit made through an undo link * * @var array * @since 1.31 @@ -6962,7 +6975,8 @@ $wgSoftwareTags = [ 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, - 'mw-rollback' => true + 'mw-rollback' => true, + 'mw-undo' => true, ]; /** diff --git a/includes/Feed.php b/includes/Feed.php index 35f2ce9438..0e715df2ff 100644 --- a/includes/Feed.php +++ b/includes/Feed.php @@ -84,13 +84,23 @@ class FeedItem { } /** - * Get the unique id of this item - * + * Get the unique id of this item; already xml-encoded + * @return string + */ + public function getUniqueID() { + $id = $this->getUniqueIDUnescaped(); + if ( $id ) { + return $this->xmlEncode( $id ); + } + } + + /** + * Get the unique id of this item, without any escaping * @return string */ - public function getUniqueId() { + public function getUniqueIdUnescaped() { if ( $this->uniqueId ) { - return $this->xmlEncode( wfExpandUrl( $this->uniqueId, PROTO_CURRENT ) ); + return wfExpandUrl( $this->uniqueId, PROTO_CURRENT ); } } @@ -123,6 +133,14 @@ class FeedItem { return $this->xmlEncode( $this->url ); } + /** Get the URL of this item without any escaping + * + * @return string + */ + public function getUrlUnescaped() { + return $this->url; + } + /** * Get the description of this item; already xml-encoded * @@ -132,6 +150,14 @@ class FeedItem { return $this->xmlEncode( $this->description ); } + /** + * Get the description of this item without any escaping + * + */ + public function getDescriptionUnescaped() { + return $this->description; + } + /** * Get the language of this item * @@ -160,6 +186,15 @@ class FeedItem { return $this->xmlEncode( $this->author ); } + /** + * Get the author of this item without any escaping + * + * @return string + */ + public function getAuthorUnescaped() { + return $this->author; + } + /** * Get the comment of this item; already xml-encoded * @@ -169,6 +204,15 @@ class FeedItem { return $this->xmlEncode( $this->comments ); } + /** + * Get the comment of this item without any escaping + * + * @return string + */ + public function getCommentsUnescaped() { + return $this->comments; + } + /** * Quickie hack... strip out wikilinks to more legible form from the comment. * @@ -187,6 +231,23 @@ class FeedItem { * @ingroup Feed */ abstract class ChannelFeed extends FeedItem { + + /** @var TemplateParser */ + protected $templateParser; + + /** + * @param string|Title $title Feed's title + * @param string $description + * @param string $url URL uniquely designating the feed. + * @param string $date Feed's date + * @param string $author Author's user name + * @param string $comments + */ + function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) { + parent::__construct( $title, $description, $url, $date, $author, $comments ); + $this->templateParser = new TemplateParser(); + } + /** * Generate Header of the feed * @par Example: @@ -279,13 +340,15 @@ abstract class ChannelFeed extends FeedItem { class RSSFeed extends ChannelFeed { /** - * Format a date given a timestamp + * Format a date given a timestamp. If a timestamp is not given, nothing is returned * - * @param int $ts Timestamp - * @return string Date string + * @param int|null $ts Timestamp + * @return string|null Date string */ function formatTime( $ts ) { - return gmdate( 'D, d M Y H:i:s \G\M\T', wfTimestamp( TS_UNIX, $ts ) ); + if ( $ts ) { + return gmdate( 'D, d M Y H:i:s \G\M\T', wfTimestamp( TS_UNIX, $ts ) ); + } } /** @@ -295,15 +358,17 @@ class RSSFeed extends ChannelFeed { global $wgVersion; $this->outXmlHeader(); - ?> - - <?php print $this->getTitle() ?> - getUrl(), PROTO_CURRENT ) ?> - getDescription() ?> - getLanguage() ?> - MediaWiki - formatTime( wfTimestampNow() ) ?> - $this->getTitle(), + 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ), + 'description' => $this->getDescription(), + 'language' => $this->xmlEncode( $this->getLanguage() ), + 'version' => $this->xmlEncode( $wgVersion ), + 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) ) + ]; + print $this->templateParser->processTemplate( 'RSSHeader', $templateParams ); } /** @@ -311,28 +376,30 @@ class RSSFeed extends ChannelFeed { * @param FeedItem $item Item to be output */ function outItem( $item ) { - // @codingStandardsIgnoreStart Ignore long lines and formatting issues. - ?> - - <?php print $item->getTitle(); ?> - getUrl(), PROTO_CURRENT ); ?> - rssIsPermalink ) { print ' isPermaLink="false"'; } ?>>getUniqueId(); ?> - getDescription() ?> - getDate() ) { ?>formatTime( $item->getDate() ); ?> - getAuthor() ) { ?>getAuthor(); ?> - getComments() ) { ?>getComments(), PROTO_CURRENT ); ?> - - $item->getTitle(), + "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ), + "permalink" => $item->rssIsPermalink, + "uniqueID" => $item->getUniqueId(), + "description" => $item->getDescription(), + "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ), + "author" => $item->getAuthor() + ]; + $comments = $item->getCommentsUnescaped(); + if ( $comments ) { + $commentsEscaped = $this->xmlEncode( wfExpandUrl( $comments, PROTO_CURRENT ) ); + $templateParams["comments"] = $commentsEscaped; + } + print $this->templateParser->processTemplate( 'RSSItem', $templateParams ); } /** * Output an RSS 2.0 footer */ function outFooter() { - ?> - -"; } } @@ -343,14 +410,16 @@ class RSSFeed extends ChannelFeed { */ class AtomFeed extends ChannelFeed { /** - * Format a date given timestamp. + * Format a date given timestamp, if one is given. * - * @param string|int $timestamp - * @return string + * @param string|int|null $timestamp + * @return string|null */ function formatTime( $timestamp ) { - // need to use RFC 822 time format at least for rss2.0 - return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $timestamp ) ); + if ( $timestamp ) { + // need to use RFC 822 time format at least for rss2.0 + return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $timestamp ) ); + } } /** @@ -358,20 +427,20 @@ class AtomFeed extends ChannelFeed { */ function outHeader() { global $wgVersion; - $this->outXmlHeader(); - // @codingStandardsIgnoreStart Ignore long lines and formatting issues. - ?> - getFeedId() ?> - <?php print $this->getTitle() ?> - - - formatTime( wfTimestampNow() ) ?>Z - getDescription() ?> - MediaWiki - - $this->xmlEncode( $this->getLanguage() ), + 'feedID' => $this->getFeedID(), + 'title' => $this->getTitle(), + 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ), + 'selfUrl' => $this->getSelfUrl(), + 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) ), + 'description' => $this->getDescription(), + 'version' => $this->xmlEncode( $wgVersion ), + ]; + print $this->templateParser->processTemplate( 'AtomHeader', $templateParams ); } /** @@ -401,30 +470,24 @@ class AtomFeed extends ChannelFeed { */ function outItem( $item ) { global $wgMimeType; - // @codingStandardsIgnoreStart Ignore long lines and formatting issues. - ?> - - getUniqueId(); ?> - <?php print $item->getTitle(); ?> - - getDate() ) { ?> - formatTime( $item->getDate() ); ?>Z - - - getDescription() ?> - getAuthor() ) { ?>getAuthor(); ?> - - -getComments() ) { ?>getComments() ?> - */ + // Manually escaping rather than letting Mustache do it because Mustache + // uses htmlentities, which does not work with XML + $templateParams = [ + "uniqueID" => $item->getUniqueId(), + "title" => $item->getTitle(), + "mimeType" => $this->xmlEncode( $wgMimeType ), + "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ), + "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ), + "description" => $item->getDescription(), + "author" => $item->getAuthor() + ]; + print $this->templateParser->processTemplate( 'AtomItem', $templateParams ); } /** * Outputs the footer for Atom 1.0 feed (basically '\'). */ - function outFooter() {?> - "; } } diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php index 14d22ec404..5193168bb0 100644 --- a/includes/HistoryBlob.php +++ b/includes/HistoryBlob.php @@ -560,26 +560,26 @@ class DiffHistoryBlob implements HistoryBlob { $op = $x['op']; ++$p; switch ( $op ) { - case self::XDL_BDOP_INS: - $x = unpack( 'Csize', substr( $diff, $p, 1 ) ); - $p++; - $out .= substr( $diff, $p, $x['size'] ); - $p += $x['size']; - break; - case self::XDL_BDOP_INSB: - $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) ); - $p += 4; - $out .= substr( $diff, $p, $x['csize'] ); - $p += $x['csize']; - break; - case self::XDL_BDOP_CPY: - $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) ); - $p += 8; - $out .= substr( $base, $x['off'], $x['csize'] ); - break; - default: - wfDebug( __METHOD__ . ": invalid op\n" ); - return false; + case self::XDL_BDOP_INS: + $x = unpack( 'Csize', substr( $diff, $p, 1 ) ); + $p++; + $out .= substr( $diff, $p, $x['size'] ); + $p += $x['size']; + break; + case self::XDL_BDOP_INSB: + $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) ); + $p += 4; + $out .= substr( $diff, $p, $x['csize'] ); + $p += $x['csize']; + break; + case self::XDL_BDOP_CPY: + $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) ); + $p += 8; + $out .= substr( $base, $x['off'], $x['csize'] ); + break; + default: + wfDebug( __METHOD__ . ": invalid op\n" ); + return false; } } return $out; diff --git a/includes/Linker.php b/includes/Linker.php index a0332cf615..84e3103c65 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1192,12 +1192,12 @@ class Linker { $section = str_replace( '[[', '', $section ); $section = str_replace( ']]', '', $section ); - $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # T24784 + $section = substr( Parser::guessSectionNameFromStrippedText( $section ), 1 ); if ( $local ) { - $sectionTitle = Title::newFromText( '#' . $section ); + $sectionTitle = Title::makeTitleSafe( NS_MAIN, '', $section ); } else { $sectionTitle = Title::makeTitleSafe( $title->getNamespace(), - $title->getDBkey(), Sanitizer::decodeCharReferences( $section ) ); + $title->getDBkey(), $section ); } if ( $sectionTitle ) { $link = Linker::makeCommentLink( $sectionTitle, $wgLang->getArrow(), $wikiId, 'noclasses' ); @@ -1595,7 +1595,12 @@ class Linker { $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped(); return '
' - . '

' . $title . "

\n" + . Html::openElement( 'div', [ + 'class' => 'toctitle', + 'lang' => $lang->getHtmlCode(), + 'dir' => $lang->getDir(), + ] ) + . '

' . $title . "

\n" . $toc . "\n\n"; } diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php index 19b71f12e1..04c67fb297 100644 --- a/includes/MediaWikiServices.php +++ b/includes/MediaWikiServices.php @@ -11,6 +11,9 @@ use GlobalVarConfig; use Hooks; use IBufferingStatsdDataFactory; use MediaWiki\Shell\CommandFactory; +use MediaWiki\Storage\BlobStore; +use MediaWiki\Storage\BlobStoreFactory; +use MediaWiki\Storage\RevisionStore; use Wikimedia\Rdbms\LBFactory; use LinkCache; use Wikimedia\Rdbms\LoadBalancer; @@ -698,6 +701,30 @@ class MediaWikiServices extends ServiceContainer { return $this->getService( 'ExternalStoreFactory' ); } + /** + * @since 1.31 + * @return BlobStoreFactory + */ + public function getBlobStoreFactory() { + return $this->getService( 'BlobStoreFactory' ); + } + + /** + * @since 1.31 + * @return BlobStore + */ + public function getBlobStore() { + return $this->getService( '_SqlBlobStore' ); + } + + /** + * @since 1.31 + * @return RevisionStore + */ + public function getRevisionStore() { + return $this->getService( 'RevisionStore' ); + } + /////////////////////////////////////////////////////////////////////////// // NOTE: When adding a service getter here, don't forget to add a test // case for it in MediaWikiServicesTest::provideGetters() and in diff --git a/includes/MergeHistory.php b/includes/MergeHistory.php index 9d63869641..b969e0394c 100644 --- a/includes/MergeHistory.php +++ b/includes/MergeHistory.php @@ -24,6 +24,7 @@ * * @file */ +use MediaWiki\MediaWikiServices; use Wikimedia\Timestamp\TimestampException; use Wikimedia\Rdbms\IDatabase; @@ -335,6 +336,10 @@ class MergeHistory { } $this->dest->invalidateCache(); // update histories + // Duplicate watchers of the old article to the new article on history merge + $store = MediaWikiServices::getInstance()->getWatchedItemStore(); + $store->duplicateAllAssociatedEntries( $this->source, $this->dest ); + // Update our logs $logEntry = new ManualLogEntry( 'merge', 'merge' ); $logEntry->setPerformer( $user ); diff --git a/includes/Message.php b/includes/Message.php index 16ae839e82..e55eaaf646 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -1308,16 +1308,15 @@ class Message implements MessageSpecifier, Serializable { */ protected function formatPlaintext( $plaintext, $format ) { switch ( $format ) { - case self::FORMAT_TEXT: - case self::FORMAT_PLAIN: - return $plaintext; - - case self::FORMAT_PARSE: - case self::FORMAT_BLOCK_PARSE: - case self::FORMAT_ESCAPED: - default: - return htmlspecialchars( $plaintext, ENT_QUOTES ); - + case self::FORMAT_TEXT: + case self::FORMAT_PLAIN: + return $plaintext; + + case self::FORMAT_PARSE: + case self::FORMAT_BLOCK_PARSE: + case self::FORMAT_ESCAPED: + default: + return htmlspecialchars( $plaintext, ENT_QUOTES ); } } diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 92963fd18b..1c2c29dca3 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -3243,6 +3243,8 @@ class OutputPage extends ContextSource { && ( $relevantTitle->exists() || $relevantTitle->quickUserCan( 'create', $user ) ); foreach ( $title->getRestrictionTypes() as $type ) { + // Following keys are set in $vars: + // wgRestrictionCreate, wgRestrictionEdit, wgRestrictionMove, wgRestrictionUpload $vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type ); } @@ -3331,10 +3333,14 @@ class OutputPage extends ContextSource { ] ); if ( $config->get( 'ReferrerPolicy' ) !== false ) { - $tags['meta-referrer'] = Html::element( 'meta', [ - 'name' => 'referrer', - 'content' => $config->get( 'ReferrerPolicy' ) - ] ); + // Per https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values + // fallbacks should come before the primary value so we need to reverse the array. + foreach ( array_reverse( (array)$config->get( 'ReferrerPolicy' ) ) as $i => $policy ) { + $tags["meta-referrer-$i"] = Html::element( 'meta', [ + 'name' => 'referrer', + 'content' => $policy, + ] ); + } } $p = "{$this->mIndexPolicy},{$this->mFollowPolicy}"; diff --git a/includes/Preferences.php b/includes/Preferences.php index cab1e1f3e1..33a975d104 100644 --- a/includes/Preferences.php +++ b/includes/Preferences.php @@ -534,12 +534,22 @@ class Preferences { if ( $config->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) { $defaultPreferences['disablemail'] = [ + 'id' => 'wpAllowEmail', 'type' => 'toggle', 'invert' => true, 'section' => 'personal/email', 'label-message' => 'allowemail', 'disabled' => $disableEmailPrefs, ]; + + $defaultPreferences['email-allow-new-users'] = [ + 'id' => 'wpAllowEmailFromNewUsers', + 'type' => 'toggle', + 'section' => 'personal/email', + 'label-message' => 'email-allow-new-users-label', + 'disabled' => $disableEmailPrefs, + ]; + $defaultPreferences['ccmeonemails'] = [ 'type' => 'toggle', 'section' => 'personal/email', @@ -547,10 +557,7 @@ class Preferences { 'disabled' => $disableEmailPrefs, ]; - if ( $config->get( 'EnableUserEmailBlacklist' ) - && !$disableEmailPrefs - && !(bool)$user->getOption( 'disablemail' ) - ) { + if ( $config->get( 'EnableUserEmailBlacklist' ) ) { $lookup = CentralIdLookup::factory(); $ids = $user->getOption( 'email-blacklist', [] ); $names = $ids ? $lookup->namesFromCentralIds( $ids, $user ) : []; @@ -560,6 +567,7 @@ class Preferences { 'label-message' => 'email-blacklist-label', 'section' => 'personal/email', 'default' => implode( "\n", $names ), + 'disabled' => $disableEmailPrefs, ]; } } @@ -1171,21 +1179,31 @@ class Preferences { # Only show skins that aren't disabled in $wgSkipSkins $validSkinNames = Skin::getAllowedSkins(); - # Sort by UI skin name. First though need to update validSkinNames as sometimes - # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI). foreach ( $validSkinNames as $skinkey => &$skinname ) { $msg = $context->msg( "skinname-{$skinkey}" ); if ( $msg->exists() ) { $skinname = htmlspecialchars( $msg->text() ); } } - asort( $validSkinNames ); $config = $context->getConfig(); $defaultSkin = $config->get( 'DefaultSkin' ); $allowUserCss = $config->get( 'AllowUserCss' ); $allowUserJs = $config->get( 'AllowUserJs' ); + # Sort by the internal name, so that the ordering is the same for each display language, + # especially if some skin names are translated to use a different alphabet and some are not. + uksort( $validSkinNames, function ( $a, $b ) use ( $defaultSkin ) { + # Display the default first in the list by comparing it as lesser than any other. + if ( strcasecmp( $a, $defaultSkin ) === 0 ) { + return -1; + } + if ( strcasecmp( $b, $defaultSkin ) === 0 ) { + return 1; + } + return strcasecmp( $a, $b ); + } ); + $foundDefault = false; foreach ( $validSkinNames as $skinkey => $sn ) { $linkTools = []; diff --git a/includes/Revision.php b/includes/Revision.php index 25c89c26ec..8f36e88fbe 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -20,7 +20,14 @@ * @file */ -use Wikimedia\Rdbms\Database; +use MediaWiki\Storage\MutableRevisionRecord; +use MediaWiki\Storage\RevisionAccessException; +use MediaWiki\Storage\RevisionRecord; +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; @@ -28,78 +35,54 @@ use Wikimedia\Rdbms\ResultWrapper; use Wikimedia\Rdbms\FakeResultWrapper; /** - * @todo document + * @deprecated since 1.31, use RevisionRecord, RevisionStore, and BlobStore instead. */ class Revision implements IDBAccessObject { - /** @var int|null */ - protected $mId; - /** @var int|null */ - protected $mPage; - /** @var string */ - protected $mUserText; - /** @var string */ - protected $mOrigUserText; - /** @var int */ - protected $mUser; - /** @var bool */ - protected $mMinorEdit; - /** @var string */ - protected $mTimestamp; - /** @var int */ - protected $mDeleted; - /** @var int */ - protected $mSize; - /** @var string */ - protected $mSha1; - /** @var int */ - protected $mParentId; - /** @var string */ - protected $mComment; - /** @var string */ - protected $mText; - /** @var int */ - protected $mTextId; - /** @var int */ - protected $mUnpatrolled; - - /** @var stdClass|null */ - protected $mTextRow; - - /** @var null|Title */ - protected $mTitle; - /** @var bool */ - protected $mCurrent; - /** @var string */ - protected $mContentModel; - /** @var string */ - protected $mContentFormat; - - /** @var Content|null|bool */ - protected $mContent; - /** @var null|ContentHandler */ - protected $mContentHandler; - - /** @var int */ - protected $mQueryFlags = 0; - /** @var bool Used for cached values to reload user text and rev_deleted */ - protected $mRefreshMutableFields = false; - /** @var string Wiki ID; false means the current wiki */ - protected $mWiki = false; + + /** @var RevisionRecord */ + protected $mRecord; // Revision deletion constants - const DELETED_TEXT = 1; - const DELETED_COMMENT = 2; - const DELETED_USER = 4; - const DELETED_RESTRICTED = 8; - const SUPPRESSED_USER = 12; // convenience - const SUPPRESSED_ALL = 15; // convenience + const DELETED_TEXT = RevisionRecord::DELETED_TEXT; + const DELETED_COMMENT = RevisionRecord::DELETED_COMMENT; + const DELETED_USER = RevisionRecord::DELETED_USER; + const DELETED_RESTRICTED = RevisionRecord::DELETED_RESTRICTED; + const SUPPRESSED_USER = RevisionRecord::SUPPRESSED_USER; + const SUPPRESSED_ALL = RevisionRecord::SUPPRESSED_ALL; // Audience options for accessors - const FOR_PUBLIC = 1; - const FOR_THIS_USER = 2; - const RAW = 3; + const FOR_PUBLIC = RevisionRecord::FOR_PUBLIC; + const FOR_THIS_USER = RevisionRecord::FOR_THIS_USER; + const RAW = RevisionRecord::RAW; + + const TEXT_CACHE_GROUP = SqlBlobStore::TEXT_CACHE_GROUP; + + /** + * @return RevisionStore + */ + protected static function getRevisionStore() { + return MediaWikiServices::getInstance()->getRevisionStore(); + } + + /** + * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki. + * + * @return SqlBlobStore + */ + protected static function getBlobStore( $wiki = false ) { + $store = MediaWikiServices::getInstance() + ->getBlobStoreFactory() + ->newSqlBlobStore( $wiki ); - const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count + if ( !$store instanceof SqlBlobStore ) { + throw new RuntimeException( + 'The backwards compatibility code in Revision currently requires the BlobStore ' + . 'service to be an SqlBlobStore instance, but it is a ' . get_class( $store ) + ); + } + + return $store; + } /** * Load a page revision from a given revision ID number. @@ -111,10 +94,54 @@ class Revision implements IDBAccessObject { * * @param int $id * @param int $flags (optional) + * @param Title $title (optional) If known you can pass the Title in here. + * Passing no Title may result in another DB query if there are recent writes. * @return Revision|null */ - public static function newFromId( $id, $flags = 0 ) { - return self::newFromConds( [ 'rev_id' => intval( $id ) ], $flags ); + public static function newFromId( $id, $flags = 0, Title $title = null ) { + /** + * MCR RevisionStore Compat + * + * If the title is not passed in as a param (already known) then select it here. + * + * Do the selection with MASTER if $flags includes READ_LATEST or recent changes + * have happened on our load balancer. + * + * If we select the title here and pass it down it will results in fewer queries + * further down the stack. + */ + if ( !$title ) { + if ( + $flags & self::READ_LATEST || + wfGetLB()->hasOrMadeRecentMasterChanges() + ) { + $dbr = wfGetDB( DB_MASTER ); + } else { + $dbr = wfGetDB( DB_REPLICA ); + } + $row = $dbr->selectRow( + [ 'revision', 'page' ], + [ + 'page_namespace', + 'page_title', + 'page_id', + 'page_latest', + 'page_is_redirect', + 'page_len', + ], + [ 'rev_id' => $id ], + __METHOD__, + [], + [ 'page' => [ 'JOIN', 'page_id=rev_page' ] ] + ); + if ( $row ) { + $title = Title::newFromRow( $row ); + } + wfGetLB()->reuseConnection( $dbr ); + } + + $rec = self::getRevisionStore()->getRevisionById( $id, $flags, $title ); + return $rec === null ? null : new Revision( $rec, $flags, $title ); } /** @@ -132,20 +159,8 @@ class Revision implements IDBAccessObject { * @return Revision|null */ public static function newFromTitle( LinkTarget $linkTarget, $id = 0, $flags = 0 ) { - $conds = [ - 'page_namespace' => $linkTarget->getNamespace(), - 'page_title' => $linkTarget->getDBkey() - ]; - if ( $id ) { - // Use the specified ID - $conds['rev_id'] = $id; - return self::newFromConds( $conds, $flags ); - } else { - // Use a join to get the latest revision - $conds[] = 'rev_id=page_latest'; - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); - return self::loadFromConds( $db, $conds, $flags ); - } + $rec = self::getRevisionStore()->getRevisionByTitle( $linkTarget, $id, $flags ); + return $rec === null ? null : new Revision( $rec, $flags ); } /** @@ -163,92 +178,72 @@ class Revision implements IDBAccessObject { * @return Revision|null */ public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) { - $conds = [ 'page_id' => $pageId ]; - if ( $revId ) { - $conds['rev_id'] = $revId; - return self::newFromConds( $conds, $flags ); - } else { - // Use a join to get the latest revision - $conds[] = 'rev_id = page_latest'; - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); - return self::loadFromConds( $db, $conds, $flags ); - } + $rec = self::getRevisionStore()->getRevisionByPageId( $pageId, $revId, $flags ); + return $rec === null ? null : new Revision( $rec, $flags ); } /** * Make a fake revision object from an archive table row. This is queried * for permissions or even inserted (as in Special:Undelete) - * @todo FIXME: Should be a subclass for RevisionDelete. [TS] * * @param object $row * @param array $overrides + * @param Title $title (optional) * * @throws MWException * @return Revision */ - public static function newFromArchiveRow( $row, $overrides = [] ) { - global $wgContentHandlerUseDB; - - $attribs = $overrides + [ - 'page' => isset( $row->ar_page_id ) ? $row->ar_page_id : null, - 'id' => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null, - 'comment' => CommentStore::newKey( 'ar_comment' ) - // Legacy because $row may have come from self::selectArchiveFields() - ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text, - 'user' => $row->ar_user, - 'user_text' => $row->ar_user_text, - 'timestamp' => $row->ar_timestamp, - 'minor_edit' => $row->ar_minor_edit, - 'text_id' => isset( $row->ar_text_id ) ? $row->ar_text_id : null, - 'deleted' => $row->ar_deleted, - 'len' => $row->ar_len, - 'sha1' => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null, - 'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null, - 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null, - ]; - - if ( !$wgContentHandlerUseDB ) { - unset( $attribs['content_model'] ); - unset( $attribs['content_format'] ); + public static function newFromArchiveRow( $row, $overrides = [], Title $title = null ) { + /** + * MCR Migration: https://phabricator.wikimedia.org/T183564 + * This method used to overwrite attributes, then passed to Revision::__construct + * RevisionStore::newRevisionFromArchiveRow instead overrides row field names + * So do a conversion here. + */ + if ( array_key_exists( 'page', $overrides ) ) { + $overrides['page_id'] = $overrides['page']; + unset( $overrides['page'] ); } - if ( !isset( $attribs['title'] ) - && isset( $row->ar_namespace ) - && isset( $row->ar_title ) - ) { - $attribs['title'] = Title::makeTitle( $row->ar_namespace, $row->ar_title ); - } - - if ( isset( $row->ar_text ) && !$row->ar_text_id ) { - // Pre-1.5 ar_text row - $attribs['text'] = self::getRevisionText( $row, 'ar_' ); - if ( $attribs['text'] === false ) { - throw new MWException( 'Unable to load text from archive row (possibly T24624)' ); - } - } - return new self( $attribs ); + $rec = self::getRevisionStore()->newRevisionFromArchiveRow( $row, 0, $title, $overrides ); + return new Revision( $rec, self::READ_NORMAL, $title ); } /** * @since 1.19 * - * @param object $row + * MCR migration note: replaced by RevisionStore::newRevisionFromRow(). Note that + * newFromRow() also accepts arrays, while newRevisionFromRow() does not. Instead, + * a MutableRevisionRecord should be constructed directly. RevisionStore::newRevisionFromArray() + * can be used as a temporary replacement, but should be avoided. + * + * @param object|array $row * @return Revision */ public static function newFromRow( $row ) { - return new self( $row ); + if ( is_array( $row ) ) { + $rec = self::getRevisionStore()->newMutableRevisionFromArray( $row ); + } else { + $rec = self::getRevisionStore()->newRevisionFromRow( $row ); + } + + return new Revision( $rec ); } /** * Load a page revision from a given revision ID number. * Returns null if no such revision can be found. * + * @deprecated since 1.31, use RevisionStore::getRevisionById() instead. + * * @param IDatabase $db * @param int $id * @return Revision|null */ public static function loadFromId( $db, $id ) { - return self::loadFromConds( $db, [ 'rev_id' => intval( $id ) ] ); + wfDeprecated( __METHOD__, '1.31' ); // no known callers + $rec = self::getRevisionStore()->loadRevisionFromId( $db, $id ); + return $rec === null ? null : new Revision( $rec ); } /** @@ -256,19 +251,16 @@ class Revision implements IDBAccessObject { * that's attached to a given page. If not attached * to that page, will return null. * + * @deprecated since 1.31, use RevisionStore::getRevisionByPageId() instead. + * * @param IDatabase $db * @param int $pageid * @param int $id * @return Revision|null */ public static function loadFromPageId( $db, $pageid, $id = 0 ) { - $conds = [ 'rev_page' => intval( $pageid ), 'page_id' => intval( $pageid ) ]; - if ( $id ) { - $conds['rev_id'] = intval( $id ); - } else { - $conds[] = 'rev_id=page_latest'; - } - return self::loadFromConds( $db, $conds ); + $rec = self::getRevisionStore()->loadRevisionFromPageId( $db, $pageid, $id ); + return $rec === null ? null : new Revision( $rec ); } /** @@ -276,24 +268,16 @@ class Revision implements IDBAccessObject { * that's attached to a given page. If not attached * to that page, will return null. * + * @deprecated since 1.31, use RevisionStore::getRevisionByTitle() instead. + * * @param IDatabase $db * @param Title $title * @param int $id * @return Revision|null */ public static function loadFromTitle( $db, $title, $id = 0 ) { - if ( $id ) { - $matchId = intval( $id ); - } else { - $matchId = 'page_latest'; - } - return self::loadFromConds( $db, - [ - "rev_id=$matchId", - 'page_namespace' => $title->getNamespace(), - 'page_title' => $title->getDBkey() - ] - ); + $rec = self::getRevisionStore()->loadRevisionFromTitle( $db, $title, $id ); + return $rec === null ? null : new Revision( $rec ); } /** @@ -301,73 +285,17 @@ class Revision implements IDBAccessObject { * WARNING: Timestamps may in some circumstances not be unique, * so this isn't the best key to use. * + * @deprecated since 1.31, use RevisionStore::loadRevisionFromTimestamp() instead. + * * @param IDatabase $db * @param Title $title * @param string $timestamp * @return Revision|null */ public static function loadFromTimestamp( $db, $title, $timestamp ) { - return self::loadFromConds( $db, - [ - 'rev_timestamp' => $db->timestamp( $timestamp ), - 'page_namespace' => $title->getNamespace(), - 'page_title' => $title->getDBkey() - ] - ); - } - - /** - * Given a set of conditions, fetch a revision - * - * This method is used then a revision ID is qualified and - * will incorporate some basic replica DB/master fallback logic - * - * @param array $conditions - * @param int $flags (optional) - * @return Revision|null - */ - private static function newFromConds( $conditions, $flags = 0 ) { - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); - - $rev = self::loadFromConds( $db, $conditions, $flags ); - // Make sure new pending/committed revision are visibile later on - // within web requests to certain avoid bugs like T93866 and T94407. - if ( !$rev - && !( $flags & self::READ_LATEST ) - && wfGetLB()->getServerCount() > 1 - && wfGetLB()->hasOrMadeRecentMasterChanges() - ) { - $flags = self::READ_LATEST; - $db = wfGetDB( DB_MASTER ); - $rev = self::loadFromConds( $db, $conditions, $flags ); - } - - if ( $rev ) { - $rev->mQueryFlags = $flags; - } - - return $rev; - } - - /** - * Given a set of conditions, fetch a revision from - * the given database connection. - * - * @param IDatabase $db - * @param array $conditions - * @param int $flags (optional) - * @return Revision|null - */ - private static function loadFromConds( $db, $conditions, $flags = 0 ) { - $row = self::fetchFromConds( $db, $conditions, $flags ); - if ( $row ) { - $rev = new Revision( $row ); - $rev->mWiki = $db->getDomainID(); - - return $rev; - } - - return null; + // XXX: replace loadRevisionFromTimestamp by getRevisionByTimestamp? + $rec = self::getRevisionStore()->loadRevisionFromTimestamp( $db, $title, $timestamp ); + return $rec === null ? null : new Revision( $rec ); } /** @@ -377,52 +305,18 @@ class Revision implements IDBAccessObject { * * @param LinkTarget $title * @return ResultWrapper - * @deprecated Since 1.28 + * @deprecated Since 1.28, no callers in core nor in known extensions. No-op since 1.31. */ public static function fetchRevision( LinkTarget $title ) { - $row = self::fetchFromConds( - wfGetDB( DB_REPLICA ), - [ - 'rev_id=page_latest', - 'page_namespace' => $title->getNamespace(), - 'page_title' => $title->getDBkey() - ] - ); - - return new FakeResultWrapper( $row ? [ $row ] : [] ); - } - - /** - * Given a set of conditions, return a ResultWrapper - * which will return matching database rows with the - * fields necessary to build Revision objects. - * - * @param IDatabase $db - * @param array $conditions - * @param int $flags (optional) - * @return stdClass - */ - private static function fetchFromConds( $db, $conditions, $flags = 0 ) { - $revQuery = self::getQueryInfo( [ 'page', 'user' ] ); - $options = []; - if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) { - $options[] = 'FOR UPDATE'; - } - return $db->selectRow( - $revQuery['tables'], - $revQuery['fields'], - $conditions, - __METHOD__, - $options, - $revQuery['joins'] - ); + wfDeprecated( __METHOD__, '1.31' ); + return new FakeResultWrapper( [] ); } /** * Return the value of a select() JOIN conds array for the user table. * This will get user table rows for logged-in users. * @since 1.19 - * @deprecated since 1.31, use self::getQueryInfo( [ 'user' ] ) instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead. * @return array */ public static function userJoinCond() { @@ -434,7 +328,7 @@ class Revision implements IDBAccessObject { * Return the value of a select() page conds array for the page table. * This will assure that the revision(s) are not orphaned from live pages. * @since 1.19 - * @deprecated since 1.31, use self::getQueryInfo( [ 'page' ] ) instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead. * @return array */ public static function pageJoinCond() { @@ -445,7 +339,7 @@ class Revision implements IDBAccessObject { /** * Return the list of revision fields that should be selected to create * a new revision. - * @deprecated since 1.31, use self::getQueryInfo() instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo() instead. * @return array */ public static function selectFields() { @@ -480,7 +374,7 @@ class Revision implements IDBAccessObject { /** * Return the list of revision fields that should be selected to create * a new revision from an archive row. - * @deprecated since 1.31, use self::getArchiveQueryInfo() instead. + * @deprecated since 1.31, use RevisionStore::getArchiveQueryInfo() instead. * @return array */ public static function selectArchiveFields() { @@ -516,7 +410,7 @@ class Revision implements IDBAccessObject { /** * Return the list of text fields that should be selected to read the * revision text - * @deprecated since 1.31, use self::getQueryInfo( [ 'text' ] ) instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'text' ] ) instead. * @return array */ public static function selectTextFields() { @@ -529,7 +423,7 @@ class Revision implements IDBAccessObject { /** * Return the list of page fields that should be selected from page table - * @deprecated since 1.31, use self::getQueryInfo( [ 'page' ] ) instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead. * @return array */ public static function selectPageFields() { @@ -546,7 +440,7 @@ class Revision implements IDBAccessObject { /** * Return the list of user fields that should be selected from user table - * @deprecated since 1.31, use self::getQueryInfo( [ 'user' ] ) instead. + * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead. * @return array */ public static function selectUserFields() { @@ -558,6 +452,7 @@ class Revision implements IDBAccessObject { * Return the tables, fields, and join conditions to be selected to create * a new revision object. * @since 1.31 + * @deprecated since 1.31, use RevisionStore::getQueryInfo() instead. * @param array $options Any combination of the following strings * - 'page': Join with the page table, and select fields to identify the page * - 'user': Join with the user table, and select the user name @@ -568,104 +463,21 @@ class Revision implements IDBAccessObject { * - joins: (array) to include in the `$join_conds` to `IDatabase->select()` */ public static function getQueryInfo( $options = [] ) { - global $wgContentHandlerUseDB; - - $commentQuery = CommentStore::newKey( 'rev_comment' )->getJoin(); - $ret = [ - 'tables' => [ 'revision' ] + $commentQuery['tables'], - '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', - ] + $commentQuery['fields'], - 'joins' => $commentQuery['joins'], - ]; - - if ( $wgContentHandlerUseDB ) { - $ret['fields'][] = 'rev_content_format'; - $ret['fields'][] = 'rev_content_model'; - } - - if ( in_array( 'page', $options, true ) ) { - $ret['tables'][] = 'page'; - $ret['fields'] = array_merge( $ret['fields'], [ - 'page_namespace', - 'page_title', - 'page_id', - 'page_latest', - 'page_is_redirect', - 'page_len', - ] ); - $ret['joins']['page'] = [ 'INNER JOIN', [ 'page_id = rev_page' ] ]; - } - - if ( in_array( 'user', $options, true ) ) { - $ret['tables'][] = 'user'; - $ret['fields'] = array_merge( $ret['fields'], [ - 'user_name', - ] ); - $ret['joins']['user'] = [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ]; - } - - if ( in_array( 'text', $options, true ) ) { - $ret['tables'][] = 'text'; - $ret['fields'] = array_merge( $ret['fields'], [ - 'old_text', - 'old_flags' - ] ); - $ret['joins']['text'] = [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ]; - } - - return $ret; + return self::getRevisionStore()->getQueryInfo( $options ); } /** * Return the tables, fields, and join conditions to be selected to create * a new archived revision object. * @since 1.31 + * @deprecated since 1.31, use RevisionStore::getArchiveQueryInfo() instead. * @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()` */ public static function getArchiveQueryInfo() { - global $wgContentHandlerUseDB; - - $commentQuery = CommentStore::newKey( 'ar_comment' )->getJoin(); - $ret = [ - 'tables' => [ 'archive' ] + $commentQuery['tables'], - 'fields' => [ - 'ar_id', - 'ar_page_id', - 'ar_rev_id', - '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'], - ]; - - if ( $wgContentHandlerUseDB ) { - $ret['fields'][] = 'ar_content_format'; - $ret['fields'][] = 'ar_content_model'; - } - - return $ret; + return self::getRevisionStore()->getArchiveQueryInfo(); } /** @@ -675,203 +487,49 @@ class Revision implements IDBAccessObject { * @return array */ public static function getParentLengths( $db, array $revIds ) { - $revLens = []; - if ( !$revIds ) { - return $revLens; // empty - } - $res = $db->select( 'revision', - [ 'rev_id', 'rev_len' ], - [ 'rev_id' => $revIds ], - __METHOD__ ); - foreach ( $res as $row ) { - $revLens[$row->rev_id] = $row->rev_len; - } - return $revLens; + return self::getRevisionStore()->listRevisionSizes( $db, $revIds ); } /** - * @param object|array $row Either a database row or an array - * @throws MWException + * @param object|array|RevisionRecord $row Either a database row or an array + * @param int $queryFlags + * @param Title|null $title + * * @access private */ - public function __construct( $row ) { - if ( is_object( $row ) ) { - $this->constructFromDbRowObject( $row ); - } elseif ( is_array( $row ) ) { - $this->constructFromRowArray( $row ); - } else { - throw new MWException( 'Revision constructor passed invalid row format.' ); - } - $this->mUnpatrolled = null; - } - - /** - * @param object $row - */ - private function constructFromDbRowObject( $row ) { - $this->mId = intval( $row->rev_id ); - $this->mPage = intval( $row->rev_page ); - $this->mTextId = intval( $row->rev_text_id ); - $this->mComment = CommentStore::newKey( 'rev_comment' ) - // Legacy because $row may have come from self::selectFields() - ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text; - $this->mUser = intval( $row->rev_user ); - $this->mMinorEdit = intval( $row->rev_minor_edit ); - $this->mTimestamp = $row->rev_timestamp; - $this->mDeleted = intval( $row->rev_deleted ); - - if ( !isset( $row->rev_parent_id ) ) { - $this->mParentId = null; - } else { - $this->mParentId = intval( $row->rev_parent_id ); - } - - if ( !isset( $row->rev_len ) ) { - $this->mSize = null; - } else { - $this->mSize = intval( $row->rev_len ); - } - - if ( !isset( $row->rev_sha1 ) ) { - $this->mSha1 = null; - } else { - $this->mSha1 = $row->rev_sha1; - } + function __construct( $row, $queryFlags = 0, Title $title = null ) { + global $wgUser; - if ( isset( $row->page_latest ) ) { - $this->mCurrent = ( $row->rev_id == $row->page_latest ); - $this->mTitle = Title::newFromRow( $row ); - } else { - $this->mCurrent = false; - $this->mTitle = null; - } - - if ( !isset( $row->rev_content_model ) ) { - $this->mContentModel = null; # determine on demand if needed - } else { - $this->mContentModel = strval( $row->rev_content_model ); - } - - if ( !isset( $row->rev_content_format ) ) { - $this->mContentFormat = null; # determine on demand if needed - } else { - $this->mContentFormat = strval( $row->rev_content_format ); - } + if ( $row instanceof RevisionRecord ) { + $this->mRecord = $row; + } elseif ( is_array( $row ) ) { + if ( !isset( $row['user'] ) && !isset( $row['user_text'] ) ) { + $row['user'] = $wgUser; + } - // Lazy extraction... - $this->mText = null; - if ( isset( $row->old_text ) ) { - $this->mTextRow = $row; + $this->mRecord = self::getRevisionStore()->newMutableRevisionFromArray( + $row, + $queryFlags, + $title + ); + } elseif ( is_object( $row ) ) { + $this->mRecord = self::getRevisionStore()->newRevisionFromRow( + $row, + $queryFlags, + $title + ); } else { - // 'text' table row entry will be lazy-loaded - $this->mTextRow = null; - } - - // Use user_name for users and rev_user_text for IPs... - $this->mUserText = null; // lazy load if left null - if ( $this->mUser == 0 ) { - $this->mUserText = $row->rev_user_text; // IP user - } elseif ( isset( $row->user_name ) ) { - $this->mUserText = $row->user_name; // logged-in user + throw new InvalidArgumentException( + '$row must be a row object, an associative array, or a RevisionRecord' + ); } - $this->mOrigUserText = $row->rev_user_text; } /** - * @param array $row - * - * @throws MWException + * @return RevisionRecord */ - private function constructFromRowArray( array $row ) { - // Build a new revision to be saved... - global $wgUser; // ugh - - # if we have a content object, use it to set the model and type - if ( !empty( $row['content'] ) ) { - if ( !( $row['content'] instanceof Content ) ) { - throw new MWException( '`content` field must contain a Content object.' ); - } - - // @todo when is that set? test with external store setup! check out insertOn() [dk] - if ( !empty( $row['text_id'] ) ) { - throw new MWException( "Text already stored in external store (id {$row['text_id']}), " . - "can't serialize content object" ); - } - - $row['content_model'] = $row['content']->getModel(); - # note: mContentFormat is initializes later accordingly - # note: content is serialized later in this method! - # also set text to null? - } - - $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null; - $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null; - $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null; - $this->mUserText = isset( $row['user_text'] ) - ? strval( $row['user_text'] ) : $wgUser->getName(); - $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId(); - $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0; - $this->mTimestamp = isset( $row['timestamp'] ) - ? strval( $row['timestamp'] ) : wfTimestampNow(); - $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0; - $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null; - $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null; - $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null; - - $this->mContentModel = isset( $row['content_model'] ) - ? strval( $row['content_model'] ) : null; - $this->mContentFormat = isset( $row['content_format'] ) - ? strval( $row['content_format'] ) : null; - - // Enforce spacing trimming on supplied text - $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null; - $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null; - $this->mTextRow = null; - - $this->mTitle = isset( $row['title'] ) ? $row['title'] : null; - - // if we have a Content object, override mText and mContentModel - if ( !empty( $row['content'] ) ) { - $handler = $this->getContentHandler(); - $this->mContent = $row['content']; - - $this->mContentModel = $this->mContent->getModel(); - $this->mContentHandler = null; - - $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() ); - } elseif ( $this->mText !== null ) { - $handler = $this->getContentHandler(); - $this->mContent = $handler->unserializeContent( $this->mText ); - } - - // If we have a Title object, make sure it is consistent with mPage. - if ( $this->mTitle && $this->mTitle->exists() ) { - if ( $this->mPage === null ) { - // if the page ID wasn't known, set it now - $this->mPage = $this->mTitle->getArticleID(); - } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) { - // Got different page IDs. This may be legit (e.g. during undeletion), - // but it seems worth mentioning it in the log. - wfDebug( "Page ID " . $this->mPage . " mismatches the ID " . - $this->mTitle->getArticleID() . " provided by the Title object." ); - } - } - - $this->mCurrent = false; - - // If we still have no length, see it we have the text to figure it out - if ( !$this->mSize && $this->mContent !== null ) { - $this->mSize = $this->mContent->getSize(); - } - - // Same for sha1 - if ( $this->mSha1 === null ) { - $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText ); - } - - // force lazy init - $this->getContentModel(); - $this->getContentFormat(); + public function getRevisionRecord() { + return $this->mRecord; } /** @@ -880,19 +538,27 @@ class Revision implements IDBAccessObject { * @return int|null */ public function getId() { - return $this->mId; + return $this->mRecord->getId(); } /** * Set the revision ID * - * This should only be used for proposed revisions that turn out to be null edits + * This should only be used for proposed revisions that turn out to be null edits. + * + * @note Only supported on Revisions that were constructed based on associative arrays, + * since they are mutable. * * @since 1.19 - * @param int $id + * @param int|string $id + * @throws MWException */ public function setId( $id ) { - $this->mId = (int)$id; + if ( $this->mRecord instanceof MutableRevisionRecord ) { + $this->mRecord->setId( intval( $id ) ); + } else { + throw new MWException( __METHOD__ . ' is not supported on this instance' ); + } } /** @@ -900,106 +566,107 @@ class Revision implements IDBAccessObject { * * This should only be used for proposed revisions that turn out to be null edits * + * @note Only supported on Revisions that were constructed based on associative arrays, + * since they are mutable. + * * @since 1.28 * @deprecated since 1.31, please reuse old Revision object * @param int $id User ID * @param string $name User name + * @throws MWException */ public function setUserIdAndName( $id, $name ) { - $this->mUser = (int)$id; - $this->mUserText = $name; - $this->mOrigUserText = $name; + if ( $this->mRecord instanceof MutableRevisionRecord ) { + $user = new UserIdentityValue( intval( $id ), $name ); + $this->mRecord->setUser( $user ); + } else { + throw new MWException( __METHOD__ . ' is not supported on this instance' ); + } } /** - * Get text row ID + * @return SlotRecord + */ + private function getMainSlotRaw() { + return $this->mRecord->getSlot( 'main', RevisionRecord::RAW ); + } + + /** + * Get the ID of the row of the text table that contains the content of the + * revision's main slot, if that content is stored in the text table. + * + * If the content is stored elsewhere, this returns null. + * + * @deprecated since 1.31, use RevisionRecord()->getSlot()->getContentAddress() to + * get that actual address that can be used with BlobStore::getBlob(); or use + * RevisionRecord::hasSameContent() to check if two revisions have the same content. * * @return int|null */ public function getTextId() { - return $this->mTextId; + $slot = $this->getMainSlotRaw(); + return $slot->hasAddress() + ? self::getBlobStore()->getTextIdFromAddress( $slot->getAddress() ) + : null; } /** * Get parent revision ID (the original previous page revision) * - * @return int|null + * @return int|null The ID of the parent revision. 0 indicates that there is no + * parent revision. Null indicates that the parent revision is not known. */ public function getParentId() { - return $this->mParentId; + return $this->mRecord->getParentId(); } /** * Returns the length of the text in this revision, or null if unknown. * - * @return int|null + * @return int */ public function getSize() { - return $this->mSize; + return $this->mRecord->getSize(); } /** - * Returns the base36 sha1 of the text in this revision, or null if unknown. + * Returns the base36 sha1 of the content in this revision, or null if unknown. * - * @return string|null + * @return string */ public function getSha1() { - return $this->mSha1; + // XXX: we may want to drop all the hashing logic, it's not worth the overhead. + return $this->mRecord->getSha1(); } /** - * Returns the title of the page associated with this entry or null. + * Returns the title of the page associated with this entry. + * Since 1.31, this will never return null. * * Will do a query, when title is not set and id is given. * - * @return Title|null + * @return Title */ public function getTitle() { - if ( $this->mTitle !== null ) { - return $this->mTitle; - } - // rev_id is defined as NOT NULL, but this revision may not yet have been inserted. - if ( $this->mId !== null ) { - $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki ); - // @todo: Title::getSelectFields(), or Title::getQueryInfo(), or something like that - $row = $dbr->selectRow( - [ 'revision', 'page' ], - [ - 'page_namespace', - 'page_title', - 'page_id', - 'page_latest', - 'page_is_redirect', - 'page_len', - ], - [ 'rev_id' => $this->mId ], - __METHOD__, - [], - [ 'page' => [ 'JOIN', 'page_id=rev_page' ] ] - ); - if ( $row ) { - // @TODO: better foreign title handling - $this->mTitle = Title::newFromRow( $row ); - } - } - - if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) { - // Loading by ID is best, though not possible for foreign titles - if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) { - $this->mTitle = Title::newFromID( $this->mPage ); - } - } - - return $this->mTitle; + $linkTarget = $this->mRecord->getPageAsLinkTarget(); + return Title::newFromLinkTarget( $linkTarget ); } /** * Set the title of the revision * + * @deprecated: since 1.31, this is now a noop. Pass the Title to the constructor instead. + * * @param Title $title */ public function setTitle( $title ) { - $this->mTitle = $title; + if ( !$title->equals( $this->getTitle() ) ) { + throw new InvalidArgumentException( + $title->getPrefixedText() + . ' is not the same as ' + . $this->mRecord->getPageAsLinkTarget()->__toString() + ); + } } /** @@ -1008,7 +675,7 @@ class Revision implements IDBAccessObject { * @return int|null */ public function getPage() { - return $this->mPage; + return $this->mRecord->getPageId(); } /** @@ -1025,13 +692,14 @@ class Revision implements IDBAccessObject { * @return int */ public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) { - if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) { - return 0; - } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) { - return 0; - } else { - return $this->mUser; + global $wgUser; + + if ( $audience === self::FOR_THIS_USER && !$user ) { + $user = $wgUser; } + + $user = $this->mRecord->getUser( $audience, $user ); + return $user ? $user->getId() : 0; } /** @@ -1059,23 +727,14 @@ class Revision implements IDBAccessObject { * @return string */ public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) { - $this->loadMutableFields(); + global $wgUser; - if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) { - return ''; - } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) { - return ''; - } else { - if ( $this->mUserText === null ) { - $this->mUserText = User::whoIs( $this->mUser ); // load on demand - if ( $this->mUserText === false ) { - # This shouldn't happen, but it can if the wiki was recovered - # via importing revs and there is no user table entry yet. - $this->mUserText = $this->mOrigUserText; - } - } - return $this->mUserText; + if ( $audience === self::FOR_THIS_USER && !$user ) { + $user = $wgUser; } + + $user = $this->mRecord->getUser( $audience, $user ); + return $user ? $user->getName() : ''; } /** @@ -1103,13 +762,14 @@ class Revision implements IDBAccessObject { * @return string */ function getComment( $audience = self::FOR_PUBLIC, User $user = null ) { - if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) { - return ''; - } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $user ) ) { - return ''; - } else { - return $this->mComment; + global $wgUser; + + if ( $audience === self::FOR_THIS_USER && !$user ) { + $user = $wgUser; } + + $comment = $this->mRecord->getComment( $audience, $user ); + return $comment === null ? null : $comment->text; } /** @@ -1127,23 +787,14 @@ class Revision implements IDBAccessObject { * @return bool */ public function isMinor() { - return (bool)$this->mMinorEdit; + return $this->mRecord->isMinor(); } /** * @return int Rcid of the unpatrolled row, zero if there isn't one */ public function isUnpatrolled() { - if ( $this->mUnpatrolled !== null ) { - return $this->mUnpatrolled; - } - $rc = $this->getRecentChange(); - if ( $rc && $rc->getAttribute( 'rc_patrolled' ) == 0 ) { - $this->mUnpatrolled = $rc->getAttribute( 'rc_id' ); - } else { - $this->mUnpatrolled = 0; - } - return $this->mUnpatrolled; + return self::getRevisionStore()->isUnpatrolled( $this->mRecord ); } /** @@ -1156,19 +807,7 @@ class Revision implements IDBAccessObject { * @return RecentChange|null */ public function getRecentChange( $flags = 0 ) { - $dbr = wfGetDB( DB_REPLICA ); - - list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags ); - - return RecentChange::newFromConds( - [ - 'rc_user_text' => $this->getUserText( self::RAW ), - 'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ), - 'rc_this_oldid' => $this->getId() - ], - __METHOD__, - $dbType - ); + return self::getRevisionStore()->getRecentChange( $this->mRecord, $flags ); } /** @@ -1177,14 +816,7 @@ class Revision implements IDBAccessObject { * @return bool */ public function isDeleted( $field ) { - if ( $this->isCurrent() && $field === self::DELETED_TEXT ) { - // Current revisions of pages cannot have the content hidden. Skipping this - // check is very useful for Parser as it fetches templates using newKnownCurrent(). - // Calling getVisibility() in that case triggers a verification database query. - return false; // no need to check - } - - return ( $this->getVisibility() & $field ) == $field; + return $this->mRecord->isDeleted( $field ); } /** @@ -1193,19 +825,17 @@ class Revision implements IDBAccessObject { * @return int */ public function getVisibility() { - $this->loadMutableFields(); - - return (int)$this->mDeleted; + return $this->mRecord->getVisibility(); } /** * Fetch revision content if it's available to the specified audience. * If the specified audience does not have the ability to view this - * revision, null will be returned. + * revision, or the content could not be loaded, null will be returned. * * @param int $audience One of: * Revision::FOR_PUBLIC to be displayed to all users - * Revision::FOR_THIS_USER to be displayed to $wgUser + * Revision::FOR_THIS_USER to be displayed to $user * Revision::RAW get the text regardless of permissions * @param User $user User object to check for, only if FOR_THIS_USER is passed * to the $audience parameter @@ -1213,12 +843,17 @@ class Revision implements IDBAccessObject { * @return Content|null */ public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) { - if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) { - return null; - } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) { + global $wgUser; + + if ( $audience === self::FOR_THIS_USER && !$user ) { + $user = $wgUser; + } + + try { + return $this->mRecord->getContent( 'main', $audience, $user ); + } + catch ( RevisionAccessException $e ) { return null; - } else { - return $this->getContentInternal(); } } @@ -1226,86 +861,51 @@ class Revision implements IDBAccessObject { * Get original serialized data (without checking view restrictions) * * @since 1.21 + * @deprecated since 1.31, use BlobStore::getBlob instead. + * * @return string */ public function getSerializedData() { - if ( $this->mText === null ) { - // Revision is immutable. Load on demand. - $this->mText = $this->loadText(); - } - - return $this->mText; + $slot = $this->getMainSlotRaw(); + return $slot->getContent()->serialize(); } /** - * Gets the content object for the revision (or null on failure). - * - * Note that for mutable Content objects, each call to this method will return a - * fresh clone. - * - * @since 1.21 - * @return Content|null The Revision's content, or null on failure. - */ - protected function getContentInternal() { - if ( $this->mContent === null ) { - $text = $this->getSerializedData(); - - if ( $text !== null && $text !== false ) { - // Unserialize content - $handler = $this->getContentHandler(); - $format = $this->getContentFormat(); - - $this->mContent = $handler->unserializeContent( $text, $format ); - } - } - - // NOTE: copy() will return $this for immutable content objects - return $this->mContent ? $this->mContent->copy() : null; - } - - /** - * Returns the content model for this revision. + * Returns the content model for the main slot of this revision. * * If no content model was stored in the database, the default content model for the title is * used to determine the content model to use. If no title is know, CONTENT_MODEL_WIKITEXT * is used as a last resort. * + * @todo: drop this, with MCR, there no longer is a single model associated with a revision. + * * @return string The content model id associated with this revision, * see the CONTENT_MODEL_XXX constants. */ public function getContentModel() { - if ( !$this->mContentModel ) { - $title = $this->getTitle(); - if ( $title ) { - $this->mContentModel = ContentHandler::getDefaultModelFor( $title ); - } else { - $this->mContentModel = CONTENT_MODEL_WIKITEXT; - } - - assert( !empty( $this->mContentModel ) ); - } - - return $this->mContentModel; + return $this->getMainSlotRaw()->getModel(); } /** - * Returns the content format for this revision. + * Returns the content format for the main slot of this revision. * * If no content format was stored in the database, the default format for this * revision's content model is returned. * + * @todo: drop this, the format is irrelevant to the revision! + * * @return string The content format id associated with this revision, * see the CONTENT_FORMAT_XXX constants. */ public function getContentFormat() { - if ( !$this->mContentFormat ) { - $handler = $this->getContentHandler(); - $this->mContentFormat = $handler->getDefaultFormat(); + $format = $this->getMainSlotRaw()->getFormat(); - assert( !empty( $this->mContentFormat ) ); + if ( $format === null ) { + // if no format was stored along with the blob, fall back to default format + $format = $this->getContentHandler()->getDefaultFormat(); } - return $this->mContentFormat; + return $format; } /** @@ -1315,33 +915,21 @@ class Revision implements IDBAccessObject { * @return ContentHandler */ public function getContentHandler() { - if ( !$this->mContentHandler ) { - $model = $this->getContentModel(); - $this->mContentHandler = ContentHandler::getForModelID( $model ); - - $format = $this->getContentFormat(); - - if ( !$this->mContentHandler->isSupportedFormat( $format ) ) { - throw new MWException( "Oops, the content format $format is not supported for " - . "this content model, $model" ); - } - } - - return $this->mContentHandler; + return ContentHandler::getForModelID( $this->getContentModel() ); } /** * @return string */ public function getTimestamp() { - return wfTimestamp( TS_MW, $this->mTimestamp ); + return $this->mRecord->getTimestamp(); } /** * @return bool */ public function isCurrent() { - return $this->mCurrent; + return ( $this->mRecord instanceof RevisionStoreRecord ) && $this->mRecord->isCurrent(); } /** @@ -1350,13 +938,10 @@ class Revision implements IDBAccessObject { * @return Revision|null */ public function getPrevious() { - if ( $this->getTitle() ) { - $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() ); - if ( $prev ) { - return self::newFromTitle( $this->getTitle(), $prev ); - } - } - return null; + $rec = self::getRevisionStore()->getPreviousRevision( $this->mRecord, $this->getTitle() ); + return $rec === null + ? null + : new Revision( $rec, self::READ_NORMAL, $this->getTitle() ); } /** @@ -1365,38 +950,10 @@ class Revision implements IDBAccessObject { * @return Revision|null */ public function getNext() { - if ( $this->getTitle() ) { - $next = $this->getTitle()->getNextRevisionID( $this->getId() ); - if ( $next ) { - return self::newFromTitle( $this->getTitle(), $next ); - } - } - return null; - } - - /** - * Get previous revision Id for this page_id - * This is used to populate rev_parent_id on save - * - * @param IDatabase $db - * @return int - */ - private function getPreviousRevisionId( $db ) { - if ( $this->mPage === null ) { - return 0; - } - # Use page_latest if ID is not given - if ( !$this->mId ) { - $prevId = $db->selectField( 'page', 'page_latest', - [ 'page_id' => $this->mPage ], - __METHOD__ ); - } else { - $prevId = $db->selectField( 'revision', 'rev_id', - [ 'rev_page' => $this->mPage, 'rev_id < ' . $this->mId ], - __METHOD__, - [ 'ORDER BY' => 'rev_id DESC' ] ); - } - return intval( $prevId ); + $rec = self::getRevisionStore()->getNextRevision( $this->mRecord, $this->getTitle() ); + return $rec === null + ? null + : new Revision( $rec, self::READ_NORMAL, $this->getTitle() ); } /** @@ -1429,35 +986,9 @@ class Revision implements IDBAccessObject { return false; } - // Use external methods for external objects, text in table is URL-only then - if ( in_array( 'external', $flags ) ) { - $url = $text; - $parts = explode( '://', $url, 2 ); - if ( count( $parts ) == 1 || $parts[1] == '' ) { - return false; - } + $cacheKey = isset( $row->old_id ) ? ( 'tt:' . $row->old_id ) : null; - if ( isset( $row->old_id ) && $wiki === false ) { - // Make use of the wiki-local revision text cache - $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); - // The cached value should be decompressed, so handle that and return here - return $cache->getWithSetCallback( - $cache->makeKey( 'revisiontext', 'textid', $row->old_id ), - self::getCacheTTL( $cache ), - function () use ( $url, $wiki, $flags ) { - // No negative caching per Revision::loadText() - $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] ); - - return self::decompressRevisionText( $text, $flags ); - }, - [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ] - ); - } else { - $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] ); - } - } - - return self::decompressRevisionText( $text, $flags ); + return self::getBlobStore( $wiki )->expandBlob( $text, $flags, $cacheKey ); } /** @@ -1471,28 +1002,7 @@ class Revision implements IDBAccessObject { * @return string */ public static function compressRevisionText( &$text ) { - global $wgCompressRevisions; - $flags = []; - - # Revisions not marked this way will be converted - # on load if $wgLegacyCharset is set in the future. - $flags[] = 'utf-8'; - - if ( $wgCompressRevisions ) { - if ( function_exists( 'gzdeflate' ) ) { - $deflated = gzdeflate( $text ); - - if ( $deflated === false ) { - wfLogWarning( __METHOD__ . ': gzdeflate() failed' ); - } else { - $text = $deflated; - $flags[] = 'gzip'; - } - } else { - wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" ); - } - } - return implode( ',', $flags ); + return self::getBlobStore()->compressData( $text ); } /** @@ -1503,46 +1013,7 @@ class Revision implements IDBAccessObject { * @return string|bool Decompressed text, or false on failure */ public static function decompressRevisionText( $text, $flags ) { - global $wgLegacyEncoding, $wgContLang; - - if ( $text === false ) { - // Text failed to be fetched; nothing to do - return false; - } - - if ( in_array( 'gzip', $flags ) ) { - # Deal with optional compression of archived pages. - # This can be done periodically via maintenance/compressOld.php, and - # as pages are saved if $wgCompressRevisions is set. - $text = gzinflate( $text ); - - if ( $text === false ) { - wfLogWarning( __METHOD__ . ': gzinflate() failed' ); - return false; - } - } - - if ( in_array( 'object', $flags ) ) { - # Generic compressed storage - $obj = unserialize( $text ); - if ( !is_object( $obj ) ) { - // Invalid object - return false; - } - $text = $obj->getText(); - } - - if ( $text !== false && $wgLegacyEncoding - && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags ) - ) { - # Old revisions kept around in a legacy encoding? - # Upconvert on demand. - # ("utf8" checked for compatibility with some broken - # conversion scripts 2008-12-30) - $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text ); - } - - return $text; + return self::getBlobStore()->decompressData( $text, $flags ); } /** @@ -1554,192 +1025,29 @@ class Revision implements IDBAccessObject { * @return int The revision ID */ public function insertOn( $dbw ) { - global $wgDefaultExternalStore, $wgContentHandlerUseDB; - - // We're inserting a new revision, so we have to use master anyway. - // If it's a null revision, it may have references to rows that - // are not in the replica yet (the text row). - $this->mQueryFlags |= self::READ_LATEST; - - // Not allowed to have rev_page equal to 0, false, etc. - if ( !$this->mPage ) { - $title = $this->getTitle(); - if ( $title instanceof Title ) { - $titleText = ' for page ' . $title->getPrefixedText(); - } else { - $titleText = ''; - } - throw new MWException( "Cannot insert revision$titleText: page ID must be nonzero" ); - } - - $this->checkContentModel(); - - $data = $this->mText; - $flags = self::compressRevisionText( $data ); - - # Write to external storage if required - if ( $wgDefaultExternalStore ) { - // Store and get the URL - $data = ExternalStore::insertToDefault( $data ); - if ( !$data ) { - throw new MWException( "Unable to store text to external storage" ); - } - if ( $flags ) { - $flags .= ','; - } - $flags .= 'external'; - } - - # Record the text (or external storage URL) to the text table - if ( $this->mTextId === null ) { - $dbw->insert( 'text', - [ - 'old_text' => $data, - 'old_flags' => $flags, - ], __METHOD__ - ); - $this->mTextId = $dbw->insertId(); - } - - if ( $this->mComment === null ) { - $this->mComment = ""; - } - - # Record the edit in revisions - $row = [ - 'rev_page' => $this->mPage, - 'rev_text_id' => $this->mTextId, - 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0, - 'rev_user' => $this->mUser, - 'rev_user_text' => $this->mUserText, - 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ), - 'rev_deleted' => $this->mDeleted, - 'rev_len' => $this->mSize, - 'rev_parent_id' => $this->mParentId === null - ? $this->getPreviousRevisionId( $dbw ) - : $this->mParentId, - 'rev_sha1' => $this->mSha1 === null - ? self::base36Sha1( $this->mText ) - : $this->mSha1, - ]; - if ( $this->mId !== null ) { - $row['rev_id'] = $this->mId; - } - - list( $commentFields, $commentCallback ) = - CommentStore::newKey( 'rev_comment' )->insertWithTempTable( $dbw, $this->mComment ); - $row += $commentFields; - - if ( $wgContentHandlerUseDB ) { - // NOTE: Store null for the default model and format, to save space. - // XXX: Makes the DB sensitive to changed defaults. - // Make this behavior optional? Only in miser mode? - - $model = $this->getContentModel(); - $format = $this->getContentFormat(); + global $wgUser; - $title = $this->getTitle(); + // Note that $this->mRecord->getId() will typically return null here, but not always, + // e.g. not when restoring a revision. - if ( $title === null ) { - throw new MWException( "Insufficient information to determine the title of the " - . "revision's page!" ); + if ( $this->mRecord->getUser( RevisionRecord::RAW ) === null ) { + if ( $this->mRecord instanceof MutableRevisionRecord ) { + $this->mRecord->setUser( $wgUser ); + } else { + throw new MWException( 'Cannot insert revision with no associated user.' ); } - - $defaultModel = ContentHandler::getDefaultModelFor( $title ); - $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat(); - - $row['rev_content_model'] = ( $model === $defaultModel ) ? null : $model; - $row['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format; - } - - $dbw->insert( 'revision', $row, __METHOD__ ); - - if ( $this->mId === null ) { - // Only if auto-increment was used - $this->mId = $dbw->insertId(); } - $commentCallback( $this->mId ); - // Assertion to try to catch T92046 - if ( (int)$this->mId === 0 ) { - throw new UnexpectedValueException( - 'After insert, Revision mId is ' . var_export( $this->mId, 1 ) . ': ' . - var_export( $row, 1 ) - ); - } + $rec = self::getRevisionStore()->insertRevisionOn( $this->mRecord, $dbw ); - // Insert IP revision into ip_changes for use when querying for a range. - if ( $this->mUser === 0 && IP::isValid( $this->mUserText ) ) { - $ipcRow = [ - 'ipc_rev_id' => $this->mId, - 'ipc_rev_timestamp' => $row['rev_timestamp'], - 'ipc_hex' => IP::toHex( $row['rev_user_text'] ), - ]; - $dbw->insert( 'ip_changes', $ipcRow, __METHOD__ ); - } + $this->mRecord = $rec; // Avoid PHP 7.1 warning of passing $this by reference $revision = $this; - Hooks::run( 'RevisionInsertComplete', [ &$revision, $data, $flags ] ); + // TODO: hard-deprecate in 1.32 (or even 1.31?) + Hooks::run( 'RevisionInsertComplete', [ &$revision, null, null ] ); - return $this->mId; - } - - protected function checkContentModel() { - global $wgContentHandlerUseDB; - - // Note: may return null for revisions that have not yet been inserted - $title = $this->getTitle(); - - $model = $this->getContentModel(); - $format = $this->getContentFormat(); - $handler = $this->getContentHandler(); - - if ( !$handler->isSupportedFormat( $format ) ) { - $t = $title->getPrefixedDBkey(); - - throw new MWException( "Can't use format $format with content model $model on $t" ); - } - - if ( !$wgContentHandlerUseDB && $title ) { - // if $wgContentHandlerUseDB is not set, - // all revisions must use the default content model and format. - - $defaultModel = ContentHandler::getDefaultModelFor( $title ); - $defaultHandler = ContentHandler::getForModelID( $defaultModel ); - $defaultFormat = $defaultHandler->getDefaultFormat(); - - if ( $this->getContentModel() != $defaultModel ) { - $t = $title->getPrefixedDBkey(); - - throw new MWException( "Can't save non-default content model with " - . "\$wgContentHandlerUseDB disabled: model is $model, " - . "default for $t is $defaultModel" ); - } - - if ( $this->getContentFormat() != $defaultFormat ) { - $t = $title->getPrefixedDBkey(); - - throw new MWException( "Can't use non-default content format with " - . "\$wgContentHandlerUseDB disabled: format is $format, " - . "default for $t is $defaultFormat" ); - } - } - - $content = $this->getContent( self::RAW ); - $prefixedDBkey = $title->getPrefixedDBkey(); - $revId = $this->mId; - - if ( !$content ) { - throw new MWException( - "Content of revision $revId ($prefixedDBkey) could not be loaded for validation!" - ); - } - if ( !$content->isValid() ) { - throw new MWException( - "Content of revision $revId ($prefixedDBkey) is not valid! Content model is $model" - ); - } + return $rec->getId(); } /** @@ -1748,103 +1056,7 @@ class Revision implements IDBAccessObject { * @return string */ public static function base36Sha1( $text ) { - return Wikimedia\base_convert( sha1( $text ), 16, 36, 31 ); - } - - /** - * Get the text cache TTL - * - * @param WANObjectCache $cache - * @return int - */ - private static function getCacheTTL( WANObjectCache $cache ) { - global $wgRevisionCacheExpiry; - - if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) { - // Do not cache RDBMs blobs in...the RDBMs store - $ttl = $cache::TTL_UNCACHEABLE; - } else { - $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE; - } - - return $ttl; - } - - /** - * Lazy-load the revision's text. - * Currently hardcoded to the 'text' table storage engine. - * - * @return string|bool The revision's text, or false on failure - */ - private function loadText() { - $cache = ObjectCache::getMainWANInstance(); - - // No negative caching; negative hits on text rows may be due to corrupted replica DBs - return $cache->getWithSetCallback( - $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ), - self::getCacheTTL( $cache ), - function () { - return $this->fetchText(); - }, - [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ] - ); - } - - private function fetchText() { - $textId = $this->getTextId(); - - // If we kept data for lazy extraction, use it now... - if ( $this->mTextRow !== null ) { - $row = $this->mTextRow; - $this->mTextRow = null; - } else { - $row = null; - } - - // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables - // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases. - $flags = $this->mQueryFlags; - $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) - ? self::READ_LATEST_IMMUTABLE - : 0; - - list( $index, $options, $fallbackIndex, $fallbackOptions ) = - DBAccessObjectUtils::getDBOptions( $flags ); - - if ( !$row ) { - // Text data is immutable; check replica DBs first. - $row = wfGetDB( $index )->selectRow( - 'text', - [ 'old_text', 'old_flags' ], - [ 'old_id' => $textId ], - __METHOD__, - $options - ); - } - - // Fallback to DB_MASTER in some cases if the row was not found - if ( !$row && $fallbackIndex !== null ) { - // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row - // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided. - $row = wfGetDB( $fallbackIndex )->selectRow( - 'text', - [ 'old_text', 'old_flags' ], - [ 'old_id' => $textId ], - __METHOD__, - $fallbackOptions - ); - } - - if ( !$row ) { - wfDebugLog( 'Revision', "No text row with ID '$textId' (revision {$this->getId()})." ); - } - - $text = self::getRevisionText( $row ); - if ( $row && $text === false ) { - wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." ); - } - - return is_string( $text ) ? $text : false; + return SlotRecord::base36Sha1( $text ); } /** @@ -1863,58 +1075,17 @@ class Revision implements IDBAccessObject { * @return Revision|null Revision or null on error */ public static function newNullRevision( $dbw, $pageId, $summary, $minor, $user = null ) { - global $wgContentHandlerUseDB; - - $fields = [ 'page_latest', 'page_namespace', 'page_title', - 'rev_text_id', 'rev_len', 'rev_sha1' ]; - - if ( $wgContentHandlerUseDB ) { - $fields[] = 'rev_content_model'; - $fields[] = 'rev_content_format'; + global $wgUser; + if ( !$user ) { + $user = $wgUser; } - $current = $dbw->selectRow( - [ 'page', 'revision' ], - $fields, - [ - 'page_id' => $pageId, - 'page_latest=rev_id', - ], - __METHOD__, - [ 'FOR UPDATE' ] // T51581 - ); - - if ( $current ) { - if ( !$user ) { - global $wgUser; - $user = $wgUser; - } - - $row = [ - 'page' => $pageId, - 'user_text' => $user->getName(), - 'user' => $user->getId(), - 'comment' => $summary, - 'minor_edit' => $minor, - 'text_id' => $current->rev_text_id, - 'parent_id' => $current->page_latest, - 'len' => $current->rev_len, - 'sha1' => $current->rev_sha1 - ]; - - if ( $wgContentHandlerUseDB ) { - $row['content_model'] = $current->rev_content_model; - $row['content_format'] = $current->rev_content_format; - } + $comment = CommentStoreComment::newUnsavedComment( $summary, null ); - $row['title'] = Title::makeTitle( $current->page_namespace, $current->page_title ); - - $revision = new Revision( $row ); - } else { - $revision = null; - } + $title = Title::newFromID( $pageId ); + $rec = self::getRevisionStore()->newNullRevision( $dbw, $title, $comment, $minor, $user ); - return $revision; + return new Revision( $rec ); } /** @@ -1948,35 +1119,13 @@ class Revision implements IDBAccessObject { public static function userCanBitfield( $bitfield, $field, User $user = null, Title $title = null ) { - if ( $bitfield & $field ) { // aspect is deleted - if ( $user === null ) { - global $wgUser; - $user = $wgUser; - } - if ( $bitfield & self::DELETED_RESTRICTED ) { - $permissions = [ 'suppressrevision', 'viewsuppressed' ]; - } elseif ( $field & self::DELETED_TEXT ) { - $permissions = [ 'deletedtext' ]; - } else { - $permissions = [ 'deletedhistory' ]; - } - $permissionlist = implode( ', ', $permissions ); - if ( $title === null ) { - wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" ); - return call_user_func_array( [ $user, 'isAllowedAny' ], $permissions ); - } else { - $text = $title->getPrefixedText(); - wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" ); - foreach ( $permissions as $perm ) { - if ( $title->userCan( $perm, $user ) ) { - return true; - } - } - return false; - } - } else { - return true; + global $wgUser; + + if ( !$user ) { + $user = $wgUser; } + + return RevisionRecord::userCanBitfield( $bitfield, $field, $user, $title ); } /** @@ -1988,18 +1137,7 @@ class Revision implements IDBAccessObject { * @return string|bool False if not found */ static function getTimestampFromId( $title, $id, $flags = 0 ) { - $db = ( $flags & self::READ_LATEST ) - ? wfGetDB( DB_MASTER ) - : wfGetDB( DB_REPLICA ); - // Casting fix for databases that can't take '' for rev_id - if ( $id == '' ) { - $id = 0; - } - $conds = [ 'rev_id' => $id ]; - $conds['rev_page'] = $title->getArticleID(); - $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ ); - - return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false; + return self::getRevisionStore()->getTimestampFromId( $title, $id, $flags ); } /** @@ -2010,12 +1148,7 @@ class Revision implements IDBAccessObject { * @return int */ static function countByPageId( $db, $id ) { - $row = $db->selectRow( 'revision', [ 'revCount' => 'COUNT(*)' ], - [ 'rev_page' => $id ], __METHOD__ ); - if ( $row ) { - return $row->revCount; - } - return 0; + return self::getRevisionStore()->countRevisionsByPageId( $db, $id ); } /** @@ -2026,11 +1159,7 @@ class Revision implements IDBAccessObject { * @return int */ static function countByTitle( $db, $title ) { - $id = $title->getArticleID(); - if ( $id ) { - return self::countByPageId( $db, $id ); - } - return 0; + return self::getRevisionStore()->countRevisionsByTitle( $db, $title ); } /** @@ -2050,28 +1179,11 @@ class Revision implements IDBAccessObject { * @return bool True if the given user was the only one to edit since the given timestamp */ public static function userWasLastToEdit( $db, $pageId, $userId, $since ) { - if ( !$userId ) { - return false; - } - if ( is_int( $db ) ) { $db = wfGetDB( $db ); } - $res = $db->select( 'revision', - 'rev_user', - [ - 'rev_page' => $pageId, - 'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) ) - ], - __METHOD__, - [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] ); - foreach ( $res as $row ) { - if ( $row->rev_user != $userId ) { - return false; - } - } - return true; + return self::getRevisionStore()->userWasLastToEdit( $db, $pageId, $userId, $since ); } /** @@ -2079,54 +1191,20 @@ class Revision implements IDBAccessObject { * * This method allows for the use of caching, though accessing anything that normally * requires permission checks (aside from the text) will trigger a small DB lookup. - * The title will also be lazy loaded, though setTitle() can be used to preload it. + * The title will also be loaded if $pageIdOrTitle is an integer ID. * - * @param IDatabase $db - * @param int $pageId Page ID - * @param int $revId Known current revision of this page + * @param IDatabase $db ignored! + * @param int|Title $pageIdOrTitle Page ID or Title object + * @param int $revId Known current revision of this page. Determined automatically if not given. * @return Revision|bool Returns false if missing * @since 1.28 */ - public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) { - $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); - return $cache->getWithSetCallback( - // Page/rev IDs passed in from DB to reflect history merges - $cache->makeGlobalKey( 'revision', $db->getDomainID(), $pageId, $revId ), - $cache::TTL_WEEK, - function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) { - $setOpts += Database::getCacheSetOptions( $db ); - - $rev = Revision::loadFromPageId( $db, $pageId, $revId ); - // Reflect revision deletion and user renames - if ( $rev ) { - $rev->mTitle = null; // mutable; lazy-load - $rev->mRefreshMutableFields = true; - } - - return $rev ?: false; // don't cache negatives - } - ); - } - - /** - * For cached revisions, make sure the user name and rev_deleted is up-to-date - */ - private function loadMutableFields() { - if ( !$this->mRefreshMutableFields ) { - return; // not needed - } + public static function newKnownCurrent( IDatabase $db, $pageIdOrTitle, $revId = 0 ) { + $title = $pageIdOrTitle instanceof Title + ? $pageIdOrTitle + : Title::newFromID( $pageIdOrTitle ); - $this->mRefreshMutableFields = false; - $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki ); - $row = $dbr->selectRow( - [ 'revision', 'user' ], - [ 'rev_deleted', 'user_name' ], - [ 'rev_id' => $this->mId, 'user_id = rev_user' ], - __METHOD__ - ); - if ( $row ) { // update values - $this->mDeleted = (int)$row->rev_deleted; - $this->mUserText = $row->user_name; - } + $record = self::getRevisionStore()->getKnownCurrentRevision( $title, $revId ); + return $record ? new Revision( $record ) : false; } } diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index dad0630edf..0d266fb710 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -42,6 +42,9 @@ use MediaWiki\Linker\LinkRendererFactory; use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; use MediaWiki\Shell\CommandFactory; +use MediaWiki\Storage\BlobStoreFactory; +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\SqlBlobStore; return [ 'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) { @@ -456,6 +459,40 @@ return [ ); }, + 'RevisionStore' => function ( MediaWikiServices $services ) { + /** @var SqlBlobStore $blobStore */ + $blobStore = $services->getService( '_SqlBlobStore' ); + + $store = new RevisionStore( + $services->getDBLoadBalancer(), + $blobStore, + $services->getMainWANObjectCache() + ); + + $config = $services->getMainConfig(); + $store->setContentHandlerUseDB( $config->get( 'ContentHandlerUseDB' ) ); + + return $store; + }, + + 'BlobStoreFactory' => function ( MediaWikiServices $services ) { + global $wgContLang; + return new BlobStoreFactory( + $services->getDBLoadBalancer(), + $services->getMainWANObjectCache(), + $services->getMainConfig(), + $wgContLang + ); + }, + + 'BlobStore' => function ( MediaWikiServices $services ) { + return $services->getService( '_SqlBlobStore' ); + }, + + '_SqlBlobStore' => function ( MediaWikiServices $services ) { + return $services->getBlobStoreFactory()->newSqlBlobStore(); + }, + /////////////////////////////////////////////////////////////////////////// // NOTE: When adding a service here, don't forget to add a getter function // in the MediaWikiServices class. The convenience getter should just call diff --git a/includes/Storage/BlobAccessException.php b/includes/Storage/BlobAccessException.php new file mode 100644 index 0000000000..ffc5ecabf4 --- /dev/null +++ b/includes/Storage/BlobAccessException.php @@ -0,0 +1,34 @@ +loadBalancer = $loadBalancer; + $this->cache = $cache; + $this->config = $mainConfig; + $this->contLang = $contLang; + } + + /** + * @since 1.31 + * + * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki. + * + * @return BlobStore + */ + public function newBlobStore( $wikiId = false ) { + return $this->newSqlBlobStore( $wikiId ); + } + + /** + * @internal Please call newBlobStore and use the BlobStore interface. + * + * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki. + * + * @return SqlBlobStore + */ + public function newSqlBlobStore( $wikiId = false ) { + $store = new SqlBlobStore( + $this->loadBalancer, + $this->cache, + $wikiId + ); + + $store->setCompressBlobs( $this->config->get( 'CompressRevisions' ) ); + $store->setCacheExpiry( $this->config->get( 'RevisionCacheExpiry' ) ); + $store->setUseExternalStore( $this->config->get( 'DefaultExternalStore' ) !== false ); + + if ( $this->config->get( 'LegacyEncoding' ) ) { + $store->setLegacyEncoding( $this->config->get( 'LegacyEncoding' ), $this->contLang ); + } + + return $store; + } + +} diff --git a/includes/Storage/IncompleteRevisionException.php b/includes/Storage/IncompleteRevisionException.php new file mode 100644 index 0000000000..bf45b012d2 --- /dev/null +++ b/includes/Storage/IncompleteRevisionException.php @@ -0,0 +1,32 @@ +getPageAsLinkTarget() ); + $rev = new MutableRevisionRecord( $title, $parent->getWikiId() ); + + $rev->setComment( $comment ); + $rev->setUser( $user ); + $rev->setTimestamp( $timestamp ); + + foreach ( $parent->getSlotRoles() as $role ) { + $slot = $parent->getSlot( $role, self::RAW ); + $rev->inheritSlot( $slot ); + } + + $rev->setPageId( $parent->getPageId() ); + $rev->setParentId( $parent->getId() ); + + return $rev; + } + + /** + * @note Avoid calling this constructor directly. Use the appropriate methods + * in RevisionStore instead. + * + * @param Title $title The title of the page this Revision is associated with. + * @param bool|string $wikiId the wiki ID of the site this Revision belongs to, + * or false for the local site. + * + * @throws MWException + */ + function __construct( Title $title, $wikiId = false ) { + $slots = new MutableRevisionSlots(); + + parent::__construct( $title, $slots, $wikiId ); + + $this->mSlots = $slots; // redundant, but nice for static analysis + } + + /** + * @param int $parentId + */ + public function setParentId( $parentId ) { + Assert::parameterType( 'integer', $parentId, '$parentId' ); + + $this->mParentId = $parentId; + } + + /** + * Sets the given slot. If a slot with the same role is already present in the revision, + * it is replaced. + * + * @note This can only be used with a fresh "unattached" SlotRecord. Calling code that has a + * SlotRecord from another revision should use inheritSlot(). Calling code that has access to + * a Content object can use setContent(). + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @note Calling this method will cause the revision size and hash to be re-calculated upon + * the next call to getSize() and getSha1(), respectively. + * + * @param SlotRecord $slot + */ + public function setSlot( SlotRecord $slot ) { + if ( $slot->hasRevision() && $slot->getRevision() !== $this->getId() ) { + throw new InvalidArgumentException( + 'The given slot must be an unsaved, unattached one. ' + . 'This slot is already attached to revision ' . $slot->getRevision() . '. ' + . 'Use inheritSlot() instead to preserve a slot from a previous revision.' + ); + } + + $this->mSlots->setSlot( $slot ); + $this->resetAggregateValues(); + } + + /** + * "Inherits" the given slot's content. + * + * If a slot with the same role is already present in the revision, it is replaced. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @param SlotRecord $parentSlot + */ + public function inheritSlot( SlotRecord $parentSlot ) { + $slot = SlotRecord::newInherited( $parentSlot ); + $this->setSlot( $slot ); + } + + /** + * Sets the content for the slot with the given role. + * + * If a slot with the same role is already present in the revision, it is replaced. + * Calling code that has access to a SlotRecord can use inheritSlot() instead. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @note Calling this method will cause the revision size and hash to be re-calculated upon + * the next call to getSize() and getSha1(), respectively. + * + * @param string $role + * @param Content $content + */ + public function setContent( $role, Content $content ) { + $this->mSlots->setContent( $role, $content ); + $this->resetAggregateValues(); + } + + /** + * Removes the slot with the given role from this revision. + * This effectively ends the "stream" with that role on the revision's page. + * Future revisions will no longer inherit this slot, unless it is added back explicitly. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @note Calling this method will cause the revision size and hash to be re-calculated upon + * the next call to getSize() and getSha1(), respectively. + * + * @param string $role + */ + public function removeSlot( $role ) { + $this->mSlots->removeSlot( $role ); + $this->resetAggregateValues(); + } + + /** + * @param CommentStoreComment $comment + */ + public function setComment( CommentStoreComment $comment ) { + $this->mComment = $comment; + } + + /** + * Set revision hash, for optimization. Prevents getSha1() from re-calculating the hash. + * + * @note This should only be used if the calling code is sure that the given hash is correct + * for the revision's content, and there is no chance of the content being manipulated + * later. When in doubt, this method should not be called. + * + * @param string $sha1 SHA1 hash as a base36 string. + */ + public function setSha1( $sha1 ) { + Assert::parameterType( 'string', $sha1, '$sha1' ); + + $this->mSha1 = $sha1; + } + + /** + * Set nominal revision size, for optimization. Prevents getSize() from re-calculating the size. + * + * @note This should only be used if the calling code is sure that the given size is correct + * for the revision's content, and there is no chance of the content being manipulated + * later. When in doubt, this method should not be called. + * + * @param int $size nominal size in bogo-bytes + */ + public function setSize( $size ) { + Assert::parameterType( 'integer', $size, '$size' ); + + $this->mSize = $size; + } + + /** + * @param int $visibility + */ + public function setVisibility( $visibility ) { + Assert::parameterType( 'integer', $visibility, '$visibility' ); + + $this->mDeleted = $visibility; + } + + /** + * @param string $timestamp A timestamp understood by wfTimestamp + */ + public function setTimestamp( $timestamp ) { + Assert::parameterType( 'string', $timestamp, '$timestamp' ); + + $this->mTimestamp = wfTimestamp( TS_MW, $timestamp ); + } + + /** + * @param bool $minorEdit + */ + public function setMinorEdit( $minorEdit ) { + Assert::parameterType( 'boolean', $minorEdit, '$minorEdit' ); + + $this->mMinorEdit = $minorEdit; + } + + /** + * Set the revision ID. + * + * MCR migration note: this replaces Revision::setId() + * + * @warning Use this with care, especially when preparing a revision for insertion + * into the database! The revision ID should only be fixed in special cases + * like preserving the original ID when restoring a revision. + * + * @param int $id + */ + public function setId( $id ) { + Assert::parameterType( 'integer', $id, '$id' ); + + $this->mId = $id; + } + + /** + * Sets the user identity associated with the revision + * + * @param UserIdentity $user + */ + public function setUser( UserIdentity $user ) { + $this->mUser = $user; + } + + /** + * @param int $pageId + */ + public function setPageId( $pageId ) { + Assert::parameterType( 'integer', $pageId, '$pageId' ); + + if ( $this->mTitle->exists() && $pageId !== $this->mTitle->getArticleID() ) { + throw new InvalidArgumentException( + 'The given Title does not belong to page ID ' . $this->mPageId + ); + } + + $this->mPageId = $pageId; + } + + /** + * Returns the nominal size of this revision. + * + * MCR migration note: this replaces Revision::getSize + * + * @return int The nominal size, may be computed on the fly if not yet known. + */ + public function getSize() { + // If not known, re-calculate and remember. Will be reset when slots change. + if ( $this->mSize === null ) { + $this->mSize = $this->mSlots->computeSize(); + } + + return $this->mSize; + } + + /** + * Returns the base36 sha1 of this revision. + * + * MCR migration note: this replaces Revision::getSha1 + * + * @return string The revision hash, may be computed on the fly if not yet known. + */ + public function getSha1() { + // If not known, re-calculate and remember. Will be reset when slots change. + if ( $this->mSha1 === null ) { + $this->mSha1 = $this->mSlots->computeSha1(); + } + + return $this->mSha1; + } + + /** + * Invalidate cached aggregate values such as hash and size. + */ + private function resetAggregateValues() { + $this->mSize = null; + $this->mSha1 = null; + } + +} diff --git a/includes/Storage/MutableRevisionSlots.php b/includes/Storage/MutableRevisionSlots.php new file mode 100644 index 0000000000..2e675c8937 --- /dev/null +++ b/includes/Storage/MutableRevisionSlots.php @@ -0,0 +1,137 @@ +getRole(); + $inherited[$role] = SlotRecord::newInherited( $slot ); + } + + return new MutableRevisionSlots( $inherited ); + } + + /** + * @param SlotRecord[] $slots An array of SlotRecords. + */ + public function __construct( array $slots = [] ) { + parent::__construct( $slots ); + } + + /** + * Sets the given slot. + * If a slot with the same role is already present, it is replaced. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @param SlotRecord $slot + */ + public function setSlot( SlotRecord $slot ) { + if ( !is_array( $this->slots ) ) { + $this->getSlots(); // initialize $this->slots + } + + $role = $slot->getRole(); + $this->slots[$role] = $slot; + } + + /** + * Sets the content for the slot with the given role. + * If a slot with the same role is already present, it is replaced. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @param string $role + * @param Content $content + */ + public function setContent( $role, Content $content ) { + $slot = SlotRecord::newUnsaved( $role, $content ); + $this->setSlot( $slot ); + } + + /** + * Remove the slot for the given role, discontinue the corresponding stream. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @param string $role + */ + public function removeSlot( $role ) { + if ( !is_array( $this->slots ) ) { + $this->getSlots(); // initialize $this->slots + } + + unset( $this->slots[$role] ); + } + + /** + * Return all slots that are not inherited. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @return SlotRecord[] + */ + public function getTouchedSlots() { + return array_filter( + $this->getSlots(), + function ( SlotRecord $slot ) { + return !$slot->isInherited(); + } + ); + } + + /** + * Return all slots that are inherited. + * + * @note This may cause the slot meta-data for the revision to be lazy-loaded. + * + * @return SlotRecord[] + */ + public function getInheritedSlots() { + return array_filter( + $this->getSlots(), + function ( SlotRecord $slot ) { + return $slot->isInherited(); + } + ); + } + +} diff --git a/includes/Storage/RevisionAccessException.php b/includes/Storage/RevisionAccessException.php new file mode 100644 index 0000000000..ee6efc0a0c --- /dev/null +++ b/includes/Storage/RevisionAccessException.php @@ -0,0 +1,34 @@ +mArchiveId = intval( $row->ar_id ); + + // NOTE: ar_page_id may be different from $this->mTitle->getArticleID() in some cases, + // notably when a partially restored page has been moved, and a new page has been created + // with the same title. Archive rows for that title will then have the wrong page id. + $this->mPageId = isset( $row->ar_page_id ) ? intval( $row->ar_page_id ) : $title->getArticleID(); + + // NOTE: ar_parent_id = 0 indicates that there is no parent revision, while null + // indicates that the parent revision is unknown. As per MW 1.31, the database schema + // allows ar_parent_id to be NULL. + $this->mParentId = isset( $row->ar_parent_id ) ? intval( $row->ar_parent_id ) : null; + $this->mId = isset( $row->ar_rev_id ) ? intval( $row->ar_rev_id ) : null; + $this->mComment = $comment; + $this->mUser = $user; + $this->mTimestamp = wfTimestamp( TS_MW, $row->ar_timestamp ); + $this->mMinorEdit = boolval( $row->ar_minor_edit ); + $this->mDeleted = intval( $row->ar_deleted ); + $this->mSize = intval( $row->ar_len ); + $this->mSha1 = isset( $row->ar_sha1 ) ? $row->ar_sha1 : null; + } + + /** + * Get archive row ID + * + * @return int + */ + public function getArchiveId() { + return $this->mId; + } + + /** + * @return int|null The revision id, or null if the original revision ID + * was not recorded in the archive table. + */ + public function getId() { + // overwritten just to refine the contract specification. + return parent::getId(); + } + + /** + * @return int The nominal revision size, never null. May be computed on the fly. + */ + public function getSize() { + // If length is null, calculate and remember it (potentially SLOW!). + // This is for compatibility with old database rows that don't have the field set. + if ( $this->mSize === null ) { + $this->mSize = $this->mSlots->computeSize(); + } + + return $this->mSize; + } + + /** + * @return string The revision hash, never null. May be computed on the fly. + */ + public function getSha1() { + // If hash is null, calculate it and remember (potentially SLOW!) + // This is for compatibility with old database rows that don't have the field set. + if ( $this->mSha1 === null ) { + $this->mSha1 = $this->mSlots->computeSha1(); + } + + return $this->mSha1; + } + + /** + * @param int $audience + * @param User|null $user + * + * @return UserIdentity The identity of the revision author, null if access is forbidden. + */ + public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) { + // overwritten just to add a guarantee to the contract + return parent::getUser( $audience, $user ); + } + + /** + * @param int $audience + * @param User|null $user + * + * @return CommentStoreComment The revision comment, null if access is forbidden. + */ + public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) { + // overwritten just to add a guarantee to the contract + return parent::getComment( $audience, $user ); + } + + /** + * @return string never null + */ + public function getTimestamp() { + // overwritten just to add a guarantee to the contract + return parent::getTimestamp(); + } + +} diff --git a/includes/Storage/RevisionFactory.php b/includes/Storage/RevisionFactory.php new file mode 100644 index 0000000000..86e8c06fbb --- /dev/null +++ b/includes/Storage/RevisionFactory.php @@ -0,0 +1,94 @@ +ar_user, etc. + * + * @return RevisionRecord + */ + public function newRevisionFromArchiveRow( + $row, + $queryFlags = 0, + Title $title = null, + array $overrides = [] + ); + +} diff --git a/includes/Storage/RevisionLookup.php b/includes/Storage/RevisionLookup.php new file mode 100644 index 0000000000..5cd157ba07 --- /dev/null +++ b/includes/Storage/RevisionLookup.php @@ -0,0 +1,118 @@ +mTitle = $title; + $this->mSlots = $slots; + $this->mWiki = $wikiId; + + // XXX: this is a sensible default, but we may not have a Title object here in the future. + $this->mPageId = $title->getArticleID(); + } + + /** + * Implemented to defy serialization. + * + * @throws LogicException always + */ + public function __sleep() { + throw new LogicException( __CLASS__ . ' is not serializable.' ); + } + + /** + * @param RevisionRecord $rec + * + * @return bool True if this RevisionRecord is known to have same content as $rec. + * False if the content is different (or not known to be the same). + */ + public function hasSameContent( RevisionRecord $rec ) { + if ( $rec === $this ) { + return true; + } + + if ( $this->getId() !== null && $this->getId() === $rec->getId() ) { + return true; + } + + // check size before hash, since size is quicker to compute + if ( $this->getSize() !== $rec->getSize() ) { + return false; + } + + // instead of checking the hash, we could also check the content addresses of all slots. + + if ( $this->getSha1() === $rec->getSha1() ) { + return true; + } + + return false; + } + + /** + * Returns the Content of the given slot of this revision. + * Call getSlotNames() to get a list of available slots. + * + * Note that for mutable Content objects, each call to this method will return a + * fresh clone. + * + * MCR migration note: this replaces Revision::getContent + * + * @param string $role The role name of the desired slot + * @param int $audience + * @param User|null $user + * + * @throws RevisionAccessException if the slot does not exist or slot data + * could not be lazy-loaded. + * @return Content|null The content of the given slot, or null if access is forbidden. + */ + public function getContent( $role, $audience = self::FOR_PUBLIC, User $user = null ) { + // XXX: throwing an exception would be nicer, but would a further + // departure from the signature of Revision::getContent(), and thus + // more complex and error prone refactoring. + if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) { + return null; + } + + $content = $this->getSlot( $role, $audience, $user )->getContent(); + return $content->copy(); + } + + /** + * Returns meta-data for the given slot. + * + * @param string $role The role name of the desired slot + * @param int $audience + * @param User|null $user + * + * @throws RevisionAccessException if the slot does not exist or slot data + * could not be lazy-loaded. + * @return SlotRecord The slot meta-data. If access to the slot content is forbidden, + * calling getContent() on the SlotRecord will throw an exception. + */ + public function getSlot( $role, $audience = self::FOR_PUBLIC, User $user = null ) { + $slot = $this->mSlots->getSlot( $role ); + + if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) { + return SlotRecord::newWithSuppressedContent( $slot ); + } + + return $slot; + } + + /** + * Returns the slot names (roles) of all slots present in this revision. + * getContent() will succeed only for the names returned by this method. + * + * @return string[] + */ + public function getSlotRoles() { + return $this->mSlots->getSlotRoles(); + } + + /** + * Get revision ID. Depending on the concrete subclass, this may return null if + * the revision ID is not known (e.g. because the revision does not yet exist + * in the database). + * + * MCR migration note: this replaces Revision::getId + * + * @return int|null + */ + public function getId() { + return $this->mId; + } + + /** + * Get parent revision ID (the original previous page revision). + * If there is no parent revision, this returns 0. + * If the parent revision is undefined or unknown, this returns null. + * + * @note As of MW 1.31, the database schema allows the parent ID to be + * NULL to indicate that it is unknown. + * + * MCR migration note: this replaces Revision::getParentId + * + * @return int|null + */ + public function getParentId() { + return $this->mParentId; + } + + /** + * Returns the nominal size of this revision, in bogo-bytes. + * May be calculated on the fly if not known, which may in the worst + * case may involve loading all content. + * + * MCR migration note: this replaces Revision::getSize + * + * @return int + */ + abstract public function getSize(); + + /** + * Returns the base36 sha1 of this revision. This hash is derived from the + * hashes of all slots associated with the revision. + * May be calculated on the fly if not known, which may in the worst + * case may involve loading all content. + * + * MCR migration note: this replaces Revision::getSha1 + * + * @return string + */ + abstract public function getSha1(); + + /** + * Get the page ID. If the page does not yet exist, the page ID is 0. + * + * MCR migration note: this replaces Revision::getPage + * + * @return int + */ + public function getPageId() { + return $this->mPageId; + } + + /** + * Get the ID of the wiki this revision belongs to. + * + * @return string|false The wiki's logical name, of false to indicate the local wiki. + */ + public function getWikiId() { + return $this->mWiki; + } + + /** + * Returns the title of the page this revision is associated with as a LinkTarget object. + * + * MCR migration note: this replaces Revision::getTitle + * + * @return LinkTarget + */ + public function getPageAsLinkTarget() { + return $this->mTitle; + } + + /** + * Fetch revision's author's user identity, if it's available to the specified audience. + * If the specified audience does not have access to it, null will be + * returned. Depending on the concrete subclass, null may also be returned if the user is + * not yet specified. + * + * MCR migration note: this replaces Revision::getUser + * + * @param int $audience One of: + * RevisionRecord::FOR_PUBLIC to be displayed to all users + * RevisionRecord::FOR_THIS_USER to be displayed to the given user + * RevisionRecord::RAW get the ID regardless of permissions + * @param User|null $user User object to check for, only if FOR_THIS_USER is passed + * to the $audience parameter + * @return UserIdentity|null + */ + public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) { + if ( !$this->audienceCan( self::DELETED_USER, $audience, $user ) ) { + return null; + } else { + return $this->mUser; + } + } + + /** + * Fetch revision comment, if it's available to the specified audience. + * If the specified audience does not have access to the comment, + * this will return null. Depending on the concrete subclass, null may also be returned + * if the comment is not yet specified. + * + * MCR migration note: this replaces Revision::getComment + * + * @param int $audience One of: + * RevisionRecord::FOR_PUBLIC to be displayed to all users + * RevisionRecord::FOR_THIS_USER to be displayed to the given user + * RevisionRecord::RAW get the text regardless of permissions + * @param User|null $user User object to check for, only if FOR_THIS_USER is passed + * to the $audience parameter + * + * @return CommentStoreComment|null + */ + public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) { + if ( !$this->audienceCan( self::DELETED_COMMENT, $audience, $user ) ) { + return null; + } else { + return $this->mComment; + } + } + + /** + * MCR migration note: this replaces Revision::isMinor + * + * @return bool + */ + public function isMinor() { + return (bool)$this->mMinorEdit; + } + + /** + * MCR migration note: this replaces Revision::isDeleted + * + * @param int $field One of DELETED_* bitfield constants + * + * @return bool + */ + public function isDeleted( $field ) { + return ( $this->getVisibility() & $field ) == $field; + } + + /** + * Get the deletion bitfield of the revision + * + * MCR migration note: this replaces Revision::getVisibility + * + * @return int + */ + public function getVisibility() { + return (int)$this->mDeleted; + } + + /** + * MCR migration note: this replaces Revision::getTimestamp. + * + * May return null if the timestamp was not specified. + * + * @return string|null + */ + public function getTimestamp() { + return $this->mTimestamp; + } + + /** + * Check that the given audience has access to the given field. + * + * MCR migration note: this corresponds to Revision::userCan + * + * @param int $field One of self::DELETED_TEXT, + * self::DELETED_COMMENT, + * self::DELETED_USER + * @param int $audience One of: + * RevisionRecord::FOR_PUBLIC to be displayed to all users + * RevisionRecord::FOR_THIS_USER to be displayed to the given user + * RevisionRecord::RAW get the text regardless of permissions + * @param User|null $user User object to check. Required if $audience is FOR_THIS_USER, + * ignored otherwise. + * + * @return bool + */ + protected function audienceCan( $field, $audience, User $user = null ) { + if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) { + return false; + } elseif ( $audience == self::FOR_THIS_USER ) { + if ( !$user ) { + throw new InvalidArgumentException( + 'A User object must be given when checking FOR_THIS_USER audience.' + ); + } + + if ( !$this->userCan( $field, $user ) ) { + return false; + } + } + + return true; + } + + /** + * Determine if the current user is allowed to view a particular + * field of this revision, if it's marked as deleted. + * + * MCR migration note: this corresponds to Revision::userCan + * + * @param int $field One of self::DELETED_TEXT, + * self::DELETED_COMMENT, + * self::DELETED_USER + * @param User $user User object to check + * @return bool + */ + protected function userCan( $field, User $user ) { + // TODO: use callback for permission checks, so we don't need to know a Title object! + return self::userCanBitfield( $this->getVisibility(), $field, $user, $this->mTitle ); + } + + /** + * Determine if the current user is allowed to view a particular + * field of this revision, if it's marked as deleted. This is used + * by various classes to avoid duplication. + * + * MCR migration note: this replaces Revision::userCanBitfield + * + * @param int $bitfield Current field + * @param int $field One of self::DELETED_TEXT = File::DELETED_FILE, + * self::DELETED_COMMENT = File::DELETED_COMMENT, + * self::DELETED_USER = File::DELETED_USER + * @param User $user User object to check + * @param Title|null $title A Title object to check for per-page restrictions on, + * instead of just plain userrights + * @return bool + */ + public static function userCanBitfield( $bitfield, $field, User $user, Title $title = null ) { + if ( $bitfield & $field ) { // aspect is deleted + if ( $bitfield & self::DELETED_RESTRICTED ) { + $permissions = [ 'suppressrevision', 'viewsuppressed' ]; + } elseif ( $field & self::DELETED_TEXT ) { + $permissions = [ 'deletedtext' ]; + } else { + $permissions = [ 'deletedhistory' ]; + } + $permissionlist = implode( ', ', $permissions ); + if ( $title === null ) { + wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" ); + return call_user_func_array( [ $user, 'isAllowedAny' ], $permissions ); + } else { + $text = $title->getPrefixedText(); + wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" ); + foreach ( $permissions as $perm ) { + if ( $title->userCan( $perm, $user ) ) { + return true; + } + } + return false; + } + } else { + return true; + } + } + +} diff --git a/includes/Storage/RevisionSlots.php b/includes/Storage/RevisionSlots.php new file mode 100644 index 0000000000..8d3d7e3d70 --- /dev/null +++ b/includes/Storage/RevisionSlots.php @@ -0,0 +1,189 @@ +slots = $slots; + } else { + $this->setSlotsInternal( $slots ); + } + } + + /** + * @param SlotRecord[] $slots + */ + private function setSlotsInternal( array $slots ) { + $this->slots = []; + + // re-key the slot array + foreach ( $slots as $slot ) { + $role = $slot->getRole(); + $this->slots[$role] = $slot; + } + } + + /** + * Implemented to defy serialization. + * + * @throws LogicException always + */ + public function __sleep() { + throw new LogicException( __CLASS__ . ' is not serializable.' ); + } + + /** + * Returns the Content of the given slot. + * Call getSlotNames() to get a list of available slots. + * + * Note that for mutable Content objects, each call to this method will return a + * fresh clone. + * + * @param string $role The role name of the desired slot + * + * @throws RevisionAccessException if the slot does not exist or slot data + * could not be lazy-loaded. + * @return Content + */ + public function getContent( $role ) { + // Return a copy to be safe. Immutable content objects return $this from copy(). + return $this->getSlot( $role )->getContent()->copy(); + } + + /** + * Returns the SlotRecord of the given slot. + * Call getSlotNames() to get a list of available slots. + * + * @param string $role The role name of the desired slot + * + * @throws RevisionAccessException if the slot does not exist or slot data + * could not be lazy-loaded. + * @return SlotRecord + */ + public function getSlot( $role ) { + $slots = $this->getSlots(); + + if ( isset( $slots[$role] ) ) { + return $slots[$role]; + } else { + throw new RevisionAccessException( 'No such slot: ' . $role ); + } + } + + /** + * Returns the slot names (roles) of all slots present in this revision. + * getContent() will succeed only for the names returned by this method. + * + * @return string[] + */ + public function getSlotRoles() { + $slots = $this->getSlots(); + return array_keys( $slots ); + } + + /** + * Computes the total nominal size of the revision's slots, in bogo-bytes. + * + * @warn This is potentially expensive! It may cause all slot's content to be loaded + * and deserialized. + * + * @return int + */ + public function computeSize() { + return array_reduce( $this->getSlots(), function ( $accu, SlotRecord $slot ) { + return $accu + $slot->getSize(); + }, 0 ); + } + + /** + * Returns an associative array that maps role names to SlotRecords. Each SlotRecord + * represents the content meta-data of a slot, together they define the content of + * a revision. + * + * @note This may cause the content meta-data for the revision to be lazy-loaded. + * + * @return SlotRecord[] revision slot/content rows, keyed by slot role name. + */ + public function getSlots() { + if ( is_callable( $this->slots ) ) { + $slots = call_user_func( $this->slots ); + + Assert::postcondition( + is_array( $slots ), + 'Slots info callback should return an array of objects' + ); + + $this->setSlotsInternal( $slots ); + } + + return $this->slots; + } + + /** + * Computes the combined hash of the revisions's slots. + * + * @note For backwards compatibility, the combined hash of a single slot + * is that slot's hash. For consistency, the combined hash of an empty set of slots + * is the hash of the empty string. + * + * @warn This is potentially expensive! It may cause all slot's content to be loaded + * and deserialized, then re-serialized and hashed. + * + * @return string + */ + public function computeSha1() { + $slots = $this->getSlots(); + ksort( $slots ); + + if ( empty( $slots ) ) { + return SlotRecord::base36Sha1( '' ); + } + + return array_reduce( $slots, function ( $accu, SlotRecord $slot ) { + return $accu === null + ? $slot->getSha1() + : SlotRecord::base36Sha1( $accu . $slot->getSha1() ); + }, null ); + } + +} diff --git a/includes/Storage/RevisionStore.php b/includes/Storage/RevisionStore.php new file mode 100644 index 0000000000..44dab1368b --- /dev/null +++ b/includes/Storage/RevisionStore.php @@ -0,0 +1,1922 @@ +loadBalancer = $loadBalancer; + $this->blobStore = $blobStore; + $this->cache = $cache; + $this->wikiId = $wikiId; + } + + /** + * @return bool + */ + public function getContentHandlerUseDB() { + return $this->contentHandlerUseDB; + } + + /** + * @param bool $contentHandlerUseDB + */ + public function setContentHandlerUseDB( $contentHandlerUseDB ) { + $this->contentHandlerUseDB = $contentHandlerUseDB; + } + + /** + * @return LoadBalancer + */ + private function getDBLoadBalancer() { + return $this->loadBalancer; + } + + /** + * @param int $mode DB_MASTER or DB_REPLICA + * + * @return IDatabase + */ + private function getDBConnection( $mode ) { + $lb = $this->getDBLoadBalancer(); + return $lb->getConnection( $mode, [], $this->wikiId ); + } + + /** + * @param IDatabase $connection + */ + private function releaseDBConnection( IDatabase $connection ) { + $lb = $this->getDBLoadBalancer(); + $lb->reuseConnection( $connection ); + } + + /** + * @param int $mode DB_MASTER or DB_REPLICA + * + * @return DBConnRef + */ + private function getDBConnectionRef( $mode ) { + $lb = $this->getDBLoadBalancer(); + return $lb->getConnectionRef( $mode, [], $this->wikiId ); + } + + /** + * Determines the page Title based on the available information. + * + * MCR migration note: this corresponds to Revision::getTitle + * + * @param int|null $pageId + * @param int|null $revId + * @param int $queryFlags + * + * @return Title + * @throws RevisionAccessException + */ + private function getTitle( $pageId, $revId, $queryFlags = 0 ) { + if ( !$pageId && !$revId ) { + throw new InvalidArgumentException( '$pageId and $revId cannot both be 0 or null' ); + } + + $title = null; + + // Loading by ID is best, but Title::newFromID does not support that for foreign IDs. + if ( $pageId !== null && $pageId > 0 && $this->wikiId === false ) { + // TODO: better foreign title handling (introduce TitleFactory) + $title = Title::newFromID( $pageId, $queryFlags ); + } + + // rev_id is defined as NOT NULL, but this revision may not yet have been inserted. + if ( !$title && $revId !== null && $revId > 0 ) { + list( $dbMode, $dbOptions, , ) = DBAccessObjectUtils::getDBOptions( $queryFlags ); + + $dbr = $this->getDbConnectionRef( $dbMode ); + // @todo: Title::getSelectFields(), or Title::getQueryInfo(), or something like that + $row = $dbr->selectRow( + [ 'revision', 'page' ], + [ + 'page_namespace', + 'page_title', + 'page_id', + 'page_latest', + 'page_is_redirect', + 'page_len', + ], + [ 'rev_id' => $revId ], + __METHOD__, + $dbOptions, + [ 'page' => [ 'JOIN', 'page_id=rev_page' ] ] + ); + if ( $row ) { + // TODO: better foreign title handling (introduce TitleFactory) + $title = Title::newFromRow( $row ); + } + } + + if ( !$title ) { + throw new RevisionAccessException( + "Could not determine title for page ID $pageId and revision ID $revId" + ); + } + + return $title; + } + + /** + * @param mixed $value + * @param string $name + * + * @throw IncompleteRevisionException if $value is null + * @return mixed $value, if $value is not null + */ + private function failOnNull( $value, $name ) { + if ( $value === null ) { + throw new IncompleteRevisionException( + "$name must not be " . var_export( $value, true ) . "!" + ); + } + + return $value; + } + + /** + * @param mixed $value + * @param string $name + * + * @throw IncompleteRevisionException if $value is empty + * @return mixed $value, if $value is not null + */ + private function failOnEmpty( $value, $name ) { + if ( $value === null || $value === 0 || $value === '' ) { + throw new IncompleteRevisionException( + "$name must not be " . var_export( $value, true ) . "!" + ); + } + + return $value; + } + + /** + * Insert a new revision into the database, returning the new revision ID + * number on success and dies horribly on failure. + * + * MCR migration note: this replaces Revision::insertOn + * + * @param RevisionRecord $rev + * @param IDatabase $dbw (master connection) + * + * @throws InvalidArgumentException + * @return RevisionRecord the new revision record. + */ + public function insertRevisionOn( RevisionRecord $rev, IDatabase $dbw ) { + // TODO: pass in a DBTransactionContext instead of a database connection. + $this->checkDatabaseWikiId( $dbw ); + + if ( !$rev->getSlotRoles() ) { + throw new InvalidArgumentException( 'At least one slot needs to be defined!' ); + } + + if ( $rev->getSlotRoles() !== [ 'main' ] ) { + throw new InvalidArgumentException( 'Only the main slot is supported for now!' ); + } + + // TODO: we shouldn't need an actual Title here. + $title = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() ); + $pageId = $this->failOnEmpty( $rev->getPageId(), 'rev_page field' ); // check this early + + $parentId = $rev->getParentId() === null + ? $this->getPreviousRevisionId( $dbw, $rev ) + : $rev->getParentId(); + + // Record the text (or external storage URL) to the blob store + $slot = $rev->getSlot( 'main', RevisionRecord::RAW ); + + $size = $this->failOnNull( $rev->getSize(), 'size field' ); + $sha1 = $this->failOnEmpty( $rev->getSha1(), 'sha1 field' ); + + if ( !$slot->hasAddress() ) { + $content = $slot->getContent(); + $format = $content->getDefaultFormat(); + $model = $content->getModel(); + + $this->checkContentModel( $content, $title ); + + $data = $content->serialize( $format ); + + // Hints allow the blob store to optimize by "leaking" application level information to it. + // TODO: with the new MCR storage schema, we rev_id have this before storing the blobs. + // When we have it, add rev_id as a hint. Can be used with rev_parent_id for + // differential storage or compression of subsequent revisions. + $blobHints = [ + BlobStore::DESIGNATION_HINT => 'page-content', // BlobStore may be used for other things too. + BlobStore::PAGE_HINT => $pageId, + BlobStore::ROLE_HINT => $slot->getRole(), + BlobStore::PARENT_HINT => $parentId, + BlobStore::SHA1_HINT => $slot->getSha1(), + BlobStore::MODEL_HINT => $model, + BlobStore::FORMAT_HINT => $format, + ]; + + $blobAddress = $this->blobStore->storeBlob( $data, $blobHints ); + } else { + $blobAddress = $slot->getAddress(); + $model = $slot->getModel(); + $format = $slot->getFormat(); + } + + $textId = $this->blobStore->getTextIdFromAddress( $blobAddress ); + + if ( !$textId ) { + throw new LogicException( + 'Blob address not supported in 1.29 database schema: ' . $blobAddress + ); + } + + // getTextIdFromAddress() is free to insert something into the text table, so $textId + // may be a new value, not anything already contained in $blobAddress. + $blobAddress = 'tt:' . $textId; + + $comment = $this->failOnNull( $rev->getComment( RevisionRecord::RAW ), 'comment' ); + $user = $this->failOnNull( $rev->getUser( RevisionRecord::RAW ), 'user' ); + $timestamp = $this->failOnEmpty( $rev->getTimestamp(), 'timestamp 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, + 'rev_sha1' => $sha1, + ]; + + if ( $rev->getId() !== null ) { + // Needed to restore revisions with their original ID + $row['rev_id'] = $rev->getId(); + } + + list( $commentFields, $commentCallback ) = + CommentStore::newKey( 'rev_comment' )->insertWithTempTable( $dbw, $comment ); + $row += $commentFields; + + if ( $this->contentHandlerUseDB ) { + // MCR migration note: rev_content_model and rev_content_format will go away + + $defaultModel = ContentHandler::getDefaultModelFor( $title ); + $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat(); + + $row['rev_content_model'] = ( $model === $defaultModel ) ? null : $model; + $row['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format; + } + + $dbw->insert( 'revision', $row, __METHOD__ ); + + if ( !isset( $row['rev_id'] ) ) { + // only if auto-increment was used + $row['rev_id'] = intval( $dbw->insertId() ); + } + $commentCallback( $row['rev_id'] ); + + // Insert IP revision into ip_changes for use when querying for a range. + if ( $row['rev_user'] === 0 && IP::isValid( $row['rev_user_text'] ) ) { + $ipcRow = [ + 'ipc_rev_id' => $row['rev_id'], + 'ipc_rev_timestamp' => $row['rev_timestamp'], + 'ipc_hex' => IP::toHex( $row['rev_user_text'] ), + ]; + $dbw->insert( 'ip_changes', $ipcRow, __METHOD__ ); + } + + $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, + $comment, + (object)$row, + $slots, + $this->wikiId + ); + + $newSlot = $rev->getSlot( 'main', RevisionRecord::RAW ); + + // sanity checks + Assert::postcondition( $rev->getId() > 0, 'revision must have an ID' ); + Assert::postcondition( $rev->getPageId() > 0, 'revision must have a page ID' ); + Assert::postcondition( + $rev->getComment( RevisionRecord::RAW ) !== null, + 'revision must have a comment' + ); + Assert::postcondition( + $rev->getUser( RevisionRecord::RAW ) !== null, + 'revision must have a user' + ); + + Assert::postcondition( $newSlot !== null, 'revision must have a main slot' ); + Assert::postcondition( + $newSlot->getAddress() !== null, + 'main slot must have an addess' + ); + + Hooks::run( 'RevisionRecordInserted', [ $rev ] ); + + return $rev; + } + + /** + * MCR migration note: this corresponds to Revision::checkContentModel + * + * @param Content $content + * @param Title $title + * + * @throws MWException + * @throws MWUnknownContentModelException + */ + private function checkContentModel( Content $content, Title $title ) { + // Note: may return null for revisions that have not yet been inserted + + $model = $content->getModel(); + $format = $content->getDefaultFormat(); + $handler = $content->getContentHandler(); + + $name = "$title"; + + if ( !$handler->isSupportedFormat( $format ) ) { + throw new MWException( "Can't use format $format with content model $model on $name" ); + } + + if ( !$this->contentHandlerUseDB ) { + // if $wgContentHandlerUseDB is not set, + // all revisions must use the default content model and format. + + $defaultModel = ContentHandler::getDefaultModelFor( $title ); + $defaultHandler = ContentHandler::getForModelID( $defaultModel ); + $defaultFormat = $defaultHandler->getDefaultFormat(); + + if ( $model != $defaultModel ) { + throw new MWException( "Can't save non-default content model with " + . "\$wgContentHandlerUseDB disabled: model is $model, " + . "default for $name is $defaultModel" + ); + } + + if ( $format != $defaultFormat ) { + throw new MWException( "Can't use non-default content format with " + . "\$wgContentHandlerUseDB disabled: format is $format, " + . "default for $name is $defaultFormat" + ); + } + } + + if ( !$content->isValid() ) { + throw new MWException( + "New content for $name is not valid! Content model is $model" + ); + } + } + + /** + * Create a new null-revision for insertion into a page's + * history. This will not re-save the text, but simply refer + * to the text from the previous version. + * + * Such revisions can for instance identify page rename + * operations and other such meta-modifications. + * + * MCR migration note: this replaces Revision::newNullRevision + * + * @todo Introduce newFromParentRevision(). newNullRevision can then be based on that + * (or go away). + * + * @param IDatabase $dbw + * @param Title $title Title of the page to read from + * @param CommentStoreComment $comment RevisionRecord's summary + * @param bool $minor Whether the revision should be considered as minor + * @param User $user The user to attribute the revision to + * @return RevisionRecord|null RevisionRecord or null on error + */ + public function newNullRevision( + IDatabase $dbw, + Title $title, + CommentStoreComment $comment, + $minor, + User $user + ) { + $this->checkDatabaseWikiId( $dbw ); + + $fields = [ 'page_latest', 'page_namespace', 'page_title', + 'rev_id', 'rev_text_id', 'rev_len', 'rev_sha1' ]; + + if ( $this->contentHandlerUseDB ) { + $fields[] = 'rev_content_model'; + $fields[] = 'rev_content_format'; + } + + $current = $dbw->selectRow( + [ 'page', 'revision' ], + $fields, + [ + 'page_id' => $title->getArticleID(), + 'page_latest=rev_id', + ], + __METHOD__, + [ 'FOR UPDATE' ] // T51581 + ); + + if ( $current ) { + $fields = [ + 'page' => $title->getArticleID(), + 'user_text' => $user->getName(), + 'user' => $user->getId(), + 'comment' => $comment, + 'minor_edit' => $minor, + 'text_id' => $current->rev_text_id, + 'parent_id' => $current->page_latest, + 'len' => $current->rev_len, + 'sha1' => $current->rev_sha1 + ]; + + if ( $this->contentHandlerUseDB ) { + $fields['content_model'] = $current->rev_content_model; + $fields['content_format'] = $current->rev_content_format; + } + + $fields['title'] = Title::makeTitle( $current->page_namespace, $current->page_title ); + + $mainSlot = $this->emulateMainSlot_1_29( $fields, 0, $title ); + $revision = new MutableRevisionRecord( $title, $this->wikiId ); + $this->initializeMutableRevisionFromArray( $revision, $fields ); + $revision->setSlot( $mainSlot ); + } else { + $revision = null; + } + + return $revision; + } + + /** + * MCR migration note: this replaces Revision::isUnpatrolled + * + * @return int Rcid of the unpatrolled row, zero if there isn't one + */ + public function isUnpatrolled( RevisionRecord $rev ) { + $rc = $this->getRecentChange( $rev ); + if ( $rc && $rc->getAttribute( 'rc_patrolled' ) == 0 ) { + return $rc->getAttribute( 'rc_id' ); + } else { + return 0; + } + } + + /** + * Get the RC object belonging to the current revision, if there's one + * + * MCR migration note: this replaces Revision::getRecentChange + * + * @todo move this somewhere else? + * + * @param RevisionRecord $rev + * @param int $flags (optional) $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master + * + * @return null|RecentChange + */ + public function getRecentChange( RevisionRecord $rev, $flags = 0 ) { + $dbr = $this->getDBConnection( DB_REPLICA ); + + list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags ); + + $userIdentity = $rev->getUser( RevisionRecord::RAW ); + + if ( !$userIdentity ) { + // If the revision has no user identity, chances are it never went + // into the database, and doesn't have an RC entry. + return null; + } + + // TODO: Select by rc_this_oldid alone - but as of Nov 2017, there is no index on that! + $rc = RecentChange::newFromConds( + [ + 'rc_user_text' => $userIdentity->getName(), + 'rc_timestamp' => $dbr->timestamp( $rev->getTimestamp() ), + 'rc_this_oldid' => $rev->getId() + ], + __METHOD__, + $dbType + ); + + $this->releaseDBConnection( $dbr ); + + // XXX: cache this locally? Glue it to the RevisionRecord? + return $rc; + } + + /** + * Maps fields of the archive row to corresponding revision rows. + * + * @param object $archiveRow + * + * @return object a revision row object, corresponding to $archiveRow. + */ + private static function mapArchiveFields( $archiveRow ) { + $fieldMap = [ + // keep with ar prefix: + 'ar_id' => 'ar_id', + + // not the same suffix: + 'ar_page_id' => 'rev_page', + 'ar_rev_id' => 'rev_id', + + // same suffix: + 'ar_text_id' => 'rev_text_id', + 'ar_timestamp' => 'rev_timestamp', + 'ar_user_text' => 'rev_user_text', + 'ar_user' => 'rev_user', + 'ar_minor_edit' => 'rev_minor_edit', + 'ar_deleted' => 'rev_deleted', + 'ar_len' => 'rev_len', + 'ar_parent_id' => 'rev_parent_id', + 'ar_sha1' => 'rev_sha1', + 'ar_comment' => 'rev_comment', + 'ar_comment_cid' => 'rev_comment_cid', + 'ar_comment_id' => 'rev_comment_id', + 'ar_comment_text' => 'rev_comment_text', + 'ar_comment_data' => 'rev_comment_data', + 'ar_comment_old' => 'rev_comment_old', + 'ar_content_format' => 'rev_content_format', + 'ar_content_model' => 'rev_content_model', + ]; + + if ( empty( $archiveRow->ar_text_id ) ) { + $fieldMap['ar_text'] = 'old_text'; + $fieldMap['ar_flags'] = 'old_flags'; + } + + $revRow = new stdClass(); + foreach ( $fieldMap as $arKey => $revKey ) { + if ( property_exists( $archiveRow, $arKey ) ) { + $revRow->$revKey = $archiveRow->$arKey; + } + } + + return $revRow; + } + + /** + * Constructs a RevisionRecord for the revisions main slot, based on the MW1.29 schema. + * + * @param object|array $row Either a database row or an array + * @param int $queryFlags for callbacks + * @param Title $title + * + * @return SlotRecord The main slot, extracted from the MW 1.29 style row. + * @throws MWException + */ + private function emulateMainSlot_1_29( $row, $queryFlags, Title $title ) { + $mainSlotRow = new stdClass(); + $mainSlotRow->role_name = 'main'; + + $content = null; + $blobData = null; + $blobFlags = ''; + + if ( is_object( $row ) ) { + // archive row + if ( !isset( $row->rev_id ) && isset( $row->ar_user ) ) { + $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 ) ) { + // this happens when the text-table gets joined directly, in the pre-1.30 schema + $blobData = isset( $row->old_text ) ? strval( $row->old_text ) : null; + $blobFlags = isset( $row->old_flags ) ? strval( $row->old_flags ) : ''; + } + + $mainSlotRow->slot_revision = intval( $row->rev_id ); + + $mainSlotRow->cont_size = isset( $row->rev_len ) ? intval( $row->rev_len ) : null; + $mainSlotRow->cont_sha1 = isset( $row->rev_sha1 ) ? strval( $row->rev_sha1 ) : null; + $mainSlotRow->model_name = isset( $row->rev_content_model ) + ? strval( $row->rev_content_model ) + : null; + // XXX: in the future, we'll probably always use the default format, and drop content_format + $mainSlotRow->format_name = isset( $row->rev_content_format ) + ? strval( $row->rev_content_format ) + : null; + } elseif ( is_array( $row ) ) { + $mainSlotRow->slot_revision = isset( $row['id'] ) ? intval( $row['id'] ) : null; + + $mainSlotRow->cont_address = isset( $row['text_id'] ) + ? 'tt:' . intval( $row['text_id'] ) + : null; + $mainSlotRow->cont_size = isset( $row['len'] ) ? intval( $row['len'] ) : null; + $mainSlotRow->cont_sha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null; + + $mainSlotRow->model_name = isset( $row['content_model'] ) + ? strval( $row['content_model'] ) : null; // XXX: must be a string! + // XXX: in the future, we'll probably always use the default format, and drop content_format + $mainSlotRow->format_name = isset( $row['content_format'] ) + ? strval( $row['content_format'] ) : null; + $blobData = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null; + $blobFlags = isset( $row['flags'] ) ? trim( strval( $row['flags'] ) ) : ''; + + // if we have a Content object, override mText and mContentModel + if ( !empty( $row['content'] ) ) { + if ( !( $row['content'] instanceof Content ) ) { + throw new MWException( 'content field must contain a Content object.' ); + } + + /** @var Content $content */ + $content = $row['content']; + $handler = $content->getContentHandler(); + + $mainSlotRow->model_name = $content->getModel(); + + // XXX: in the future, we'll probably always use the default format. + if ( $mainSlotRow->format_name === null ) { + $mainSlotRow->format_name = $handler->getDefaultFormat(); + } + } + } else { + throw new MWException( 'Revision constructor passed invalid row format.' ); + } + + // With the old schema, the content changes with every revision. + // ...except for null-revisions. Would be nice if we could detect them. + $mainSlotRow->slot_inherited = 0; + + if ( $mainSlotRow->model_name === null ) { + $mainSlotRow->model_name = function ( SlotRecord $slot ) use ( $title ) { + // TODO: MCR: consider slot role in getDefaultModelFor()! Use LinkTarget! + // TODO: MCR: deprecate $title->getModel(). + return ContentHandler::getDefaultModelFor( $title ); + }; + } + + if ( !$content ) { + $content = function ( SlotRecord $slot ) + use ( $blobData, $blobFlags, $queryFlags, $mainSlotRow ) + { + return $this->loadSlotContent( + $slot, + $blobData, + $blobFlags, + $mainSlotRow->format_name, + $queryFlags + ); + }; + } + + return new SlotRecord( $mainSlotRow, $content ); + } + + /** + * Loads a Content object based on a slot row. + * + * This method does not call $slot->getContent(), and may be used as a callback + * called by $slot->getContent(). + * + * MCR migration note: this roughly corresponds to Revision::getContentInternal + * + * @param SlotRecord $slot The SlotRecord to load content for + * @param string|null $blobData The content blob, in the form indicated by $blobFlags + * @param string $blobFlags Flags indicating how $blobData needs to be processed + * @param string|null $blobFormat MIME type indicating how $dataBlob is encoded + * @param int $queryFlags + * + * @throw RevisionAccessException + * @return Content + */ + private function loadSlotContent( + SlotRecord $slot, + $blobData = null, + $blobFlags = '', + $blobFormat = null, + $queryFlags = 0 + ) { + if ( $blobData !== null ) { + Assert::parameterType( 'string', $blobData, '$blobData' ); + Assert::parameterType( 'string', $blobFlags, '$blobFlags' ); + + $cacheKey = $slot->hasAddress() ? $slot->getAddress() : null; + + $data = $this->blobStore->expandBlob( $blobData, $blobFlags, $cacheKey ); + + if ( $data === false ) { + throw new RevisionAccessException( + "Failed to expand blob data using flags $blobFlags (key: $cacheKey)" + ); + } + } else { + $address = $slot->getAddress(); + try { + $data = $this->blobStore->getBlob( $address, $queryFlags ); + } catch ( BlobAccessException $e ) { + throw new RevisionAccessException( + "Failed to load data blob from $address: " . $e->getMessage(), 0, $e + ); + } + } + + // Unserialize content + $handler = ContentHandler::getForModelID( $slot->getModel() ); + + $content = $handler->unserializeContent( $data, $blobFormat ); + return $content; + } + + /** + * Load a page revision from a given revision ID number. + * Returns null if no such revision can be found. + * + * MCR migration note: this replaces Revision::newFromId + * + * $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master + * IDBAccessObject::READ_LOCKING : Select & lock the data from the master + * + * @param int $id + * @param int $flags (optional) + * @param Title $title (optional) + * @return RevisionRecord|null + */ + public function getRevisionById( $id, $flags = 0, Title $title = null ) { + return $this->newRevisionFromConds( [ 'rev_id' => intval( $id ) ], $flags, $title ); + } + + /** + * Load either the current, or a specified, revision + * that's attached to a given link target. If not attached + * to that link target, will return null. + * + * MCR migration note: this replaces Revision::newFromTitle + * + * $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master + * IDBAccessObject::READ_LOCKING : Select & lock the data from the master + * + * @param LinkTarget $linkTarget + * @param int $revId (optional) + * @param int $flags Bitfield (optional) + * @return RevisionRecord|null + */ + public function getRevisionByTitle( LinkTarget $linkTarget, $revId = 0, $flags = 0 ) { + $conds = [ + 'page_namespace' => $linkTarget->getNamespace(), + 'page_title' => $linkTarget->getDBkey() + ]; + if ( $revId ) { + // Use the specified revision ID. + // Note that we use newRevisionFromConds here because we want to retry + // and fall back to master if the page is not found on a replica. + // Since the caller supplied a revision ID, we are pretty sure the revision is + // supposed to exist, so we should try hard to find it. + $conds['rev_id'] = $revId; + return $this->newRevisionFromConds( $conds, $flags ); + } else { + // Use a join to get the latest revision. + // Note that we don't use newRevisionFromConds here because we don't want to retry + // and fall back to master. The assumption is that we only want to force the fallback + // if we are quite sure the revision exists because the caller supplied a revision ID. + // If the page isn't found at all on a replica, it probably simply does not exist. + $db = $this->getDBConnection( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); + + $conds[] = 'rev_id=page_latest'; + $rev = $this->loadRevisionFromConds( $db, $conds, $flags ); + + $this->releaseDBConnection( $db ); + return $rev; + } + } + + /** + * Load either the current, or a specified, revision + * that's attached to a given page ID. + * Returns null if no such revision can be found. + * + * MCR migration note: this replaces Revision::newFromPageId + * + * $flags include: + * IDBAccessObject::READ_LATEST: Select the data from the master (since 1.20) + * IDBAccessObject::READ_LOCKING : Select & lock the data from the master + * + * @param int $pageId + * @param int $revId (optional) + * @param int $flags Bitfield (optional) + * @return RevisionRecord|null + */ + public function getRevisionByPageId( $pageId, $revId = 0, $flags = 0 ) { + $conds = [ 'page_id' => $pageId ]; + if ( $revId ) { + // Use the specified revision ID. + // Note that we use newRevisionFromConds here because we want to retry + // and fall back to master if the page is not found on a replica. + // Since the caller supplied a revision ID, we are pretty sure the revision is + // supposed to exist, so we should try hard to find it. + $conds['rev_id'] = $revId; + return $this->newRevisionFromConds( $conds, $flags ); + } else { + // Use a join to get the latest revision. + // Note that we don't use newRevisionFromConds here because we don't want to retry + // and fall back to master. The assumption is that we only want to force the fallback + // if we are quite sure the revision exists because the caller supplied a revision ID. + // If the page isn't found at all on a replica, it probably simply does not exist. + $db = $this->getDBConnection( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); + + $conds[] = 'rev_id=page_latest'; + $rev = $this->loadRevisionFromConds( $db, $conds, $flags ); + + $this->releaseDBConnection( $db ); + return $rev; + } + } + + /** + * Load the revision for the given title with the given timestamp. + * WARNING: Timestamps may in some circumstances not be unique, + * so this isn't the best key to use. + * + * MCR migration note: this replaces Revision::loadFromTimestamp + * + * @param Title $title + * @param string $timestamp + * @return RevisionRecord|null + */ + public function getRevisionFromTimestamp( $title, $timestamp ) { + return $this->newRevisionFromConds( + [ + 'rev_timestamp' => $timestamp, + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey() + ], + 0, + $title + ); + } + + /** + * Make a fake revision object from an archive table row. This is queried + * for permissions or even inserted (as in Special:Undelete) + * + * MCR migration note: this replaces Revision::newFromArchiveRow + * + * @param object $row + * @param int $queryFlags + * @param Title|null $title + * @param array $overrides associative array with fields of $row to override. This may be + * used e.g. to force the parent revision ID or page ID. Keys in the array are fields + * names from the archive table without the 'ar_' prefix, i.e. use 'parent_id' to + * override ar_parent_id. + * + * @return RevisionRecord + * @throws MWException + */ + public function newRevisionFromArchiveRow( + $row, + $queryFlags = 0, + Title $title = null, + array $overrides = [] + ) { + Assert::parameterType( 'object', $row, '$row' ); + + // check second argument, since Revision::newFromArchiveRow had $overrides in that spot. + Assert::parameterType( 'integer', $queryFlags, '$queryFlags' ); + + if ( !$title && isset( $overrides['title'] ) ) { + if ( !( $overrides['title'] instanceof Title ) ) { + throw new MWException( 'title field override must contain a Title object.' ); + } + + $title = $overrides['title']; + } + + if ( !isset( $title ) ) { + if ( isset( $row->ar_namespace ) && isset( $row->ar_title ) ) { + $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); + } else { + throw new InvalidArgumentException( + 'A Title or ar_namespace and ar_title must be given' + ); + } + } + + foreach ( $overrides as $key => $value ) { + $field = "ar_$key"; + $row->$field = $value; + } + + $user = $this->getUserIdentityFromRowObject( $row, 'ar_' ); + + $comment = CommentStore::newKey( 'ar_comment' ) + // Legacy because $row may have come from self::selectFields() + ->getCommentLegacy( $this->getDBConnection( DB_REPLICA ), $row, true ); + + $mainSlot = $this->emulateMainSlot_1_29( $row, $queryFlags, $title ); + $slots = new RevisionSlots( [ 'main' => $mainSlot ] ); + + 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 + * + * MCR migration note: this replaces Revision::newFromRow + * + * @param object $row + * @param int $queryFlags + * @param Title|null $title + * + * @return RevisionRecord + * @throws MWException + * @throws RevisionAccessException + */ + private function newRevisionFromRow_1_29( $row, $queryFlags = 0, Title $title = null ) { + Assert::parameterType( 'object', $row, '$row' ); + + if ( !$title ) { + $pageId = isset( $row->rev_page ) ? $row->rev_page : 0; // XXX: also check page_id? + $revId = isset( $row->rev_id ) ? $row->rev_id : 0; + + $title = $this->getTitle( $pageId, $revId ); + } + + if ( !isset( $row->page_latest ) ) { + $row->page_latest = $title->getLatestRevID(); + if ( $row->page_latest === 0 && $title->exists() ) { + wfWarn( 'Encountered title object in limbo: ID ' . $title->getArticleID() ); + } + } + + $user = $this->getUserIdentityFromRowObject( $row ); + + $comment = CommentStore::newKey( 'rev_comment' ) + // Legacy because $row may have come from self::selectFields() + ->getCommentLegacy( $this->getDBConnection( DB_REPLICA ), $row, true ); + + $mainSlot = $this->emulateMainSlot_1_29( $row, $queryFlags, $title ); + $slots = new RevisionSlots( [ 'main' => $mainSlot ] ); + + return new RevisionStoreRecord( $title, $user, $comment, $row, $slots, $this->wikiId ); + } + + /** + * @see RevisionFactory::newRevisionFromRow + * + * MCR migration note: this replaces Revision::newFromRow + * + * @param object $row + * @param int $queryFlags + * @param Title|null $title + * + * @return RevisionRecord + */ + public function newRevisionFromRow( $row, $queryFlags = 0, Title $title = null ) { + return $this->newRevisionFromRow_1_29( $row, $queryFlags, $title ); + } + + /** + * Constructs a new MutableRevisionRecord based on the given associative array following + * the MW1.29 convention for the Revision constructor. + * + * MCR migration note: this replaces Revision::newFromRow + * + * @param array $fields + * @param int $queryFlags + * @param Title|null $title + * + * @return MutableRevisionRecord + * @throws MWException + * @throws RevisionAccessException + */ + public function newMutableRevisionFromArray( + array $fields, + $queryFlags = 0, + Title $title = null + ) { + if ( !$title && isset( $fields['title'] ) ) { + if ( !( $fields['title'] instanceof Title ) ) { + throw new MWException( 'title field must contain a Title object.' ); + } + + $title = $fields['title']; + } + + if ( !$title ) { + $pageId = isset( $fields['page'] ) ? $fields['page'] : 0; + $revId = isset( $fields['id'] ) ? $fields['id'] : 0; + + $title = $this->getTitle( $pageId, $revId ); + } + + if ( !isset( $fields['page'] ) ) { + $fields['page'] = $title->getArticleID( $queryFlags ); + } + + // if we have a content object, use it to set the model and type + if ( !empty( $fields['content'] ) ) { + if ( !( $fields['content'] instanceof Content ) ) { + throw new MWException( 'content field must contain a Content object.' ); + } + + if ( !empty( $fields['text_id'] ) ) { + throw new MWException( + "Text already stored in external store (id {$fields['text_id']}), " . + "can't serialize content object" + ); + } + } + + // 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 ) + ) { + $commentData = isset( $fields['comment_data'] ) ? $fields['comment_data'] : null; + + if ( $fields['comment'] instanceof Message ) { + $fields['comment'] = CommentStoreComment::newUnsavedComment( + $fields['comment'], + $commentData + ); + } else { + $commentText = trim( strval( $fields['comment'] ) ); + $fields['comment'] = CommentStoreComment::newUnsavedComment( + $commentText, + $commentData + ); + } + } + + $mainSlot = $this->emulateMainSlot_1_29( $fields, $queryFlags, $title ); + + $revision = new MutableRevisionRecord( $title, $this->wikiId ); + $this->initializeMutableRevisionFromArray( $revision, $fields ); + $revision->setSlot( $mainSlot ); + + return $revision; + } + + /** + * @param MutableRevisionRecord $record + * @param array $fields + */ + private function initializeMutableRevisionFromArray( + MutableRevisionRecord $record, + array $fields + ) { + /** @var UserIdentity $user */ + $user = null; + + 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'] ); + } + } + + if ( $user ) { + $record->setUser( $user ); + } + + $timestamp = isset( $fields['timestamp'] ) + ? strval( $fields['timestamp'] ) + : wfTimestampNow(); // TODO: use a callback, so we can override it for testing. + + $record->setTimestamp( $timestamp ); + + if ( isset( $fields['page'] ) ) { + $record->setPageId( intval( $fields['page'] ) ); + } + + if ( isset( $fields['id'] ) ) { + $record->setId( intval( $fields['id'] ) ); + } + if ( isset( $fields['parent_id'] ) ) { + $record->setParentId( intval( $fields['parent_id'] ) ); + } + + if ( isset( $fields['sha1'] ) ) { + $record->setSha1( $fields['sha1'] ); + } + if ( isset( $fields['size'] ) ) { + $record->setSize( intval( $fields['size'] ) ); + } + + if ( isset( $fields['minor_edit'] ) ) { + $record->setMinorEdit( intval( $fields['minor_edit'] ) !== 0 ); + } + if ( isset( $fields['deleted'] ) ) { + $record->setVisibility( intval( $fields['deleted'] ) ); + } + + if ( isset( $fields['comment'] ) ) { + Assert::parameterType( + CommentStoreComment::class, + $fields['comment'], + '$row[\'comment\']' + ); + $record->setComment( $fields['comment'] ); + } + } + + /** + * Load a page revision from a given revision ID number. + * Returns null if no such revision can be found. + * + * MCR migration note: this corresponds to Revision::loadFromId + * + * @note direct use is deprecated! + * @todo remove when unused! there seem to be no callers of Revision::loadFromId + * + * @param IDatabase $db + * @param int $id + * + * @return RevisionRecord|null + */ + public function loadRevisionFromId( IDatabase $db, $id ) { + return $this->loadRevisionFromConds( $db, [ 'rev_id' => intval( $id ) ] ); + } + + /** + * Load either the current, or a specified, revision + * that's attached to a given page. If not attached + * to that page, will return null. + * + * MCR migration note: this replaces Revision::loadFromPageId + * + * @note direct use is deprecated! + * @todo remove when unused! + * + * @param IDatabase $db + * @param int $pageid + * @param int $id + * @return RevisionRecord|null + */ + public function loadRevisionFromPageId( IDatabase $db, $pageid, $id = 0 ) { + $conds = [ 'rev_page' => intval( $pageid ), 'page_id' => intval( $pageid ) ]; + if ( $id ) { + $conds['rev_id'] = intval( $id ); + } else { + $conds[] = 'rev_id=page_latest'; + } + return $this->loadRevisionFromConds( $db, $conds ); + } + + /** + * Load either the current, or a specified, revision + * that's attached to a given page. If not attached + * to that page, will return null. + * + * MCR migration note: this replaces Revision::loadFromTitle + * + * @note direct use is deprecated! + * @todo remove when unused! + * + * @param IDatabase $db + * @param Title $title + * @param int $id + * + * @return RevisionRecord|null + */ + public function loadRevisionFromTitle( IDatabase $db, $title, $id = 0 ) { + if ( $id ) { + $matchId = intval( $id ); + } else { + $matchId = 'page_latest'; + } + + return $this->loadRevisionFromConds( + $db, + [ + "rev_id=$matchId", + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey() + ], + 0, + $title + ); + } + + /** + * Load the revision for the given title with the given timestamp. + * WARNING: Timestamps may in some circumstances not be unique, + * so this isn't the best key to use. + * + * MCR migration note: this replaces Revision::loadFromTimestamp + * + * @note direct use is deprecated! Use getRevisionFromTimestamp instead! + * @todo remove when unused! + * + * @param IDatabase $db + * @param Title $title + * @param string $timestamp + * @return RevisionRecord|null + */ + public function loadRevisionFromTimestamp( IDatabase $db, $title, $timestamp ) { + return $this->loadRevisionFromConds( $db, + [ + 'rev_timestamp' => $db->timestamp( $timestamp ), + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey() + ], + 0, + $title + ); + } + + /** + * Given a set of conditions, fetch a revision + * + * This method should be used if we are pretty sure the revision exists. + * Unless $flags has READ_LATEST set, this method will first try to find the revision + * on a replica before hitting the master database. + * + * MCR migration note: this corresponds to Revision::newFromConds + * + * @param array $conditions + * @param int $flags (optional) + * @param Title $title + * + * @return RevisionRecord|null + */ + private function newRevisionFromConds( $conditions, $flags = 0, Title $title = null ) { + $db = $this->getDBConnection( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); + $rev = $this->loadRevisionFromConds( $db, $conditions, $flags, $title ); + $this->releaseDBConnection( $db ); + + $lb = $this->getDBLoadBalancer(); + + // Make sure new pending/committed revision are visibile later on + // within web requests to certain avoid bugs like T93866 and T94407. + if ( !$rev + && !( $flags & self::READ_LATEST ) + && $lb->getServerCount() > 1 + && $lb->hasOrMadeRecentMasterChanges() + ) { + $flags = self::READ_LATEST; + $db = $this->getDBConnection( DB_MASTER ); + $rev = $this->loadRevisionFromConds( $db, $conditions, $flags, $title ); + $this->releaseDBConnection( $db ); + } + + return $rev; + } + + /** + * Given a set of conditions, fetch a revision from + * the given database connection. + * + * MCR migration note: this corresponds to Revision::loadFromConds + * + * @param IDatabase $db + * @param array $conditions + * @param int $flags (optional) + * @param Title $title + * + * @return RevisionRecord|null + */ + private function loadRevisionFromConds( + IDatabase $db, + $conditions, + $flags = 0, + Title $title = null + ) { + $row = $this->fetchRevisionRowFromConds( $db, $conditions, $flags ); + if ( $row ) { + $rev = $this->newRevisionFromRow( $row, $flags, $title ); + + return $rev; + } + + return null; + } + + /** + * Throws an exception if the given database connection does not belong to the wiki this + * RevisionStore is bound to. + * + * @param IDatabase $db + * @throws MWException + */ + private function checkDatabaseWikiId( IDatabase $db ) { + $storeWiki = $this->wikiId; + $dbWiki = $db->getDomainID(); + + if ( $dbWiki === $storeWiki ) { + return; + } + + // XXX: we really want the default database ID... + $storeWiki = $storeWiki ?: wfWikiID(); + $dbWiki = $dbWiki ?: wfWikiID(); + + if ( $dbWiki !== $storeWiki ) { + throw new MWException( "RevisionStore for $storeWiki " + . "cannot be used with a DB connection for $dbWiki" ); + } + } + + /** + * Given a set of conditions, return a row with the + * fields necessary to build RevisionRecord objects. + * + * MCR migration note: this corresponds to Revision::fetchFromConds + * + * @param IDatabase $db + * @param array $conditions + * @param int $flags (optional) + * + * @return object|false data row as a raw object + */ + private function fetchRevisionRowFromConds( IDatabase $db, $conditions, $flags = 0 ) { + $this->checkDatabaseWikiId( $db ); + + $revQuery = self::getQueryInfo( [ 'page', 'user' ] ); + $options = []; + if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) { + $options[] = 'FOR UPDATE'; + } + return $db->selectRow( + $revQuery['tables'], + $revQuery['fields'], + $conditions, + __METHOD__, + $options, + $revQuery['joins'] + ); + } + + /** + * Return the tables, fields, and join conditions to be selected to create + * a new revision object. + * + * MCR migration note: this replaces Revision::getQueryInfo + * + * @since 1.31 + * + * @param array $options Any combination of the following strings + * - 'page': Join with the page table, and select fields to identify the page + * - 'user': Join with the user table, and select the user name + * - 'text': Join with the text table, and select fields to load page text + * + * @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()` + */ + public function getQueryInfo( $options = [] ) { + $ret = [ + 'tables' => [], + 'fields' => [], + 'joins' => [], + ]; + + $ret['tables'][] = 'revision'; + $ret['fields'] = array_merge( $ret['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', + ] ); + + $commentQuery = CommentStore::newKey( 'rev_comment' )->getJoin(); + $ret['tables'] = array_merge( $ret['tables'], $commentQuery['tables'] ); + $ret['fields'] = array_merge( $ret['fields'], $commentQuery['fields'] ); + $ret['joins'] = array_merge( $ret['joins'], $commentQuery['joins'] ); + + if ( $this->contentHandlerUseDB ) { + $ret['fields'][] = 'rev_content_format'; + $ret['fields'][] = 'rev_content_model'; + } + + if ( in_array( 'page', $options, true ) ) { + $ret['tables'][] = 'page'; + $ret['fields'] = array_merge( $ret['fields'], [ + 'page_namespace', + 'page_title', + 'page_id', + 'page_latest', + 'page_is_redirect', + 'page_len', + ] ); + $ret['joins']['page'] = [ 'INNER JOIN', [ 'page_id = rev_page' ] ]; + } + + if ( in_array( 'user', $options, true ) ) { + $ret['tables'][] = 'user'; + $ret['fields'] = array_merge( $ret['fields'], [ + 'user_name', + ] ); + $ret['joins']['user'] = [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ]; + } + + if ( in_array( 'text', $options, true ) ) { + $ret['tables'][] = 'text'; + $ret['fields'] = array_merge( $ret['fields'], [ + 'old_text', + 'old_flags' + ] ); + $ret['joins']['text'] = [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ]; + } + + return $ret; + } + + /** + * Return the tables, fields, and join conditions to be selected to create + * a new archived revision object. + * + * MCR migration note: this replaces Revision::getArchiveQueryInfo + * + * @since 1.31 + * + * @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()` + */ + public function getArchiveQueryInfo() { + $commentQuery = CommentStore::newKey( 'ar_comment' )->getJoin(); + $ret = [ + 'tables' => [ 'archive' ] + $commentQuery['tables'], + 'fields' => [ + 'ar_id', + 'ar_page_id', + 'ar_namespace', + 'ar_title', + 'ar_rev_id', + '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'], + ]; + + if ( $this->contentHandlerUseDB ) { + $ret['fields'][] = 'ar_content_format'; + $ret['fields'][] = 'ar_content_model'; + } + + return $ret; + } + + /** + * Do a batched query for the sizes of a set of revisions. + * + * MCR migration note: this replaces Revision::getParentLengths + * + * @param IDatabase $db + * @param int[] $revIds + * @return int[] associative array mapping revision IDs from $revIds to the nominal size + * of the corresponding revision. + */ + public function listRevisionSizes( IDatabase $db, array $revIds ) { + $this->checkDatabaseWikiId( $db ); + + $revLens = []; + if ( !$revIds ) { + return $revLens; // empty + } + + $res = $db->select( + 'revision', + [ 'rev_id', 'rev_len' ], + [ 'rev_id' => $revIds ], + __METHOD__ + ); + + foreach ( $res as $row ) { + $revLens[$row->rev_id] = intval( $row->rev_len ); + } + + return $revLens; + } + + /** + * Get previous revision for this title + * + * MCR migration note: this replaces Revision::getPrevious + * + * @param RevisionRecord $rev + * @param Title $title if known (optional) + * + * @return RevisionRecord|null + */ + public function getPreviousRevision( RevisionRecord $rev, Title $title = null ) { + if ( $title === null ) { + $title = $this->getTitle( $rev->getPageId(), $rev->getId() ); + } + $prev = $title->getPreviousRevisionID( $rev->getId() ); + if ( $prev ) { + return $this->getRevisionByTitle( $title, $prev ); + } + return null; + } + + /** + * Get next revision for this title + * + * MCR migration note: this replaces Revision::getNext + * + * @param RevisionRecord $rev + * @param Title $title if known (optional) + * + * @return RevisionRecord|null + */ + public function getNextRevision( RevisionRecord $rev, Title $title = null ) { + if ( $title === null ) { + $title = $this->getTitle( $rev->getPageId(), $rev->getId() ); + } + $title = $this->getTitle( $rev->getPageId(), $rev->getId() ); + $next = $title->getNextRevisionID( $rev->getId() ); + if ( $next ) { + return $this->getRevisionByTitle( $title, $next ); + } + return null; + } + + /** + * Get previous revision Id for this page_id + * This is used to populate rev_parent_id on save + * + * MCR migration note: this corresponds to Revision::getPreviousRevisionId + * + * @param IDatabase $db + * @param RevisionRecord $rev + * + * @return int + */ + private function getPreviousRevisionId( IDatabase $db, RevisionRecord $rev ) { + $this->checkDatabaseWikiId( $db ); + + if ( $rev->getPageId() === null ) { + return 0; + } + # Use page_latest if ID is not given + if ( !$rev->getId() ) { + $prevId = $db->selectField( + 'page', 'page_latest', + [ 'page_id' => $rev->getPageId() ], + __METHOD__ + ); + } else { + $prevId = $db->selectField( + 'revision', 'rev_id', + [ 'rev_page' => $rev->getPageId(), 'rev_id < ' . $rev->getId() ], + __METHOD__, + [ 'ORDER BY' => 'rev_id DESC' ] + ); + } + return intval( $prevId ); + } + + /** + * Get rev_timestamp from rev_id, without loading the rest of the row + * + * MCR migration note: this replaces Revision::getTimestampFromId + * + * @param Title $title + * @param int $id + * @param int $flags + * @return string|bool False if not found + */ + public function getTimestampFromId( $title, $id, $flags = 0 ) { + $db = $this->getDBConnection( + ( $flags & IDBAccessObject::READ_LATEST ) ? DB_MASTER : DB_REPLICA + ); + + $conds = [ 'rev_id' => $id ]; + $conds['rev_page'] = $title->getArticleID(); + $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ ); + + $this->releaseDBConnection( $db ); + return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false; + } + + /** + * Get count of revisions per page...not very efficient + * + * MCR migration note: this replaces Revision::countByPageId + * + * @param IDatabase $db + * @param int $id Page id + * @return int + */ + public function countRevisionsByPageId( IDatabase $db, $id ) { + $this->checkDatabaseWikiId( $db ); + + $row = $db->selectRow( 'revision', + [ 'revCount' => 'COUNT(*)' ], + [ 'rev_page' => $id ], + __METHOD__ + ); + if ( $row ) { + return intval( $row->revCount ); + } + return 0; + } + + /** + * Get count of revisions per page...not very efficient + * + * MCR migration note: this replaces Revision::countByTitle + * + * @param IDatabase $db + * @param Title $title + * @return int + */ + public function countRevisionsByTitle( IDatabase $db, $title ) { + $id = $title->getArticleID(); + if ( $id ) { + return $this->countRevisionsByPageId( $db, $id ); + } + return 0; + } + + /** + * Check if no edits were made by other users since + * the time a user started editing the page. Limit to + * 50 revisions for the sake of performance. + * + * MCR migration note: this replaces Revision::userWasLastToEdit + * + * @deprecated since 1.31; Can possibly be removed, since the self-conflict suppression + * logic in EditPage that uses this seems conceptually dubious. Revision::userWasLastToEdit + * has been deprecated since 1.24. + * + * @param IDatabase $db The Database to perform the check on. + * @param int $pageId The ID of the page in question + * @param int $userId The ID of the user in question + * @param string $since Look at edits since this time + * + * @return bool True if the given user was the only one to edit since the given timestamp + */ + public function userWasLastToEdit( IDatabase $db, $pageId, $userId, $since ) { + $this->checkDatabaseWikiId( $db ); + + if ( !$userId ) { + return false; + } + + $res = $db->select( + 'revision', + 'rev_user', + [ + 'rev_page' => $pageId, + 'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) ) + ], + __METHOD__, + [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] + ); + foreach ( $res as $row ) { + if ( $row->rev_user != $userId ) { + return false; + } + } + return true; + } + + /** + * Load a revision based on a known page ID and current revision ID from the DB + * + * This method allows for the use of caching, though accessing anything that normally + * requires permission checks (aside from the text) will trigger a small DB lookup. + * + * MCR migration note: this replaces Revision::newKnownCurrent + * + * @param Title $title the associated page title + * @param int $revId current revision of this page. Defaults to $title->getLatestRevID(). + * + * @return RevisionRecord|bool Returns false if missing + */ + public function getKnownCurrentRevision( Title $title, $revId ) { + $db = $this->getDBConnectionRef( DB_REPLICA ); + + $pageId = $title->getArticleID(); + + if ( !$pageId ) { + return false; + } + + if ( !$revId ) { + $revId = $title->getLatestRevID(); + } + + if ( !$revId ) { + wfWarn( + 'No latest revision known for page ' . $title->getPrefixedDBkey() + . ' even though it exists with page ID ' . $pageId + ); + return false; + } + + $row = $this->cache->getWithSetCallback( + // Page/rev IDs passed in from DB to reflect history merges + $this->cache->makeGlobalKey( 'revision-row-1.29', $db->getDomainID(), $pageId, $revId ), + WANObjectCache::TTL_WEEK, + function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) { + $setOpts += Database::getCacheSetOptions( $db ); + + $conds = [ + 'rev_page' => intval( $pageId ), + 'page_id' => intval( $pageId ), + 'rev_id' => intval( $revId ), + ]; + + $row = $this->fetchRevisionRowFromConds( $db, $conds ); + return $row ?: false; // don't cache negatives + } + ); + + // Reflect revision deletion and user renames + if ( $row ) { + return $this->newRevisionFromRow( $row, 0, $title ); + } else { + return false; + } + } + + // TODO: move relevant methods from Title here, e.g. getFirstRevision, isBigDeletion, etc. + +} diff --git a/includes/Storage/RevisionStoreRecord.php b/includes/Storage/RevisionStoreRecord.php new file mode 100644 index 0000000000..341855dbe3 --- /dev/null +++ b/includes/Storage/RevisionStoreRecord.php @@ -0,0 +1,208 @@ +mId = intval( $row->rev_id ); + $this->mPageId = intval( $row->rev_page ); + $this->mComment = $comment; + + $timestamp = wfTimestamp( TS_MW, $row->rev_timestamp ); + Assert::parameter( is_string( $timestamp ), '$row->rev_timestamp', 'must be a valid timestamp' ); + + $this->mUser = $user; + $this->mMinorEdit = boolval( $row->rev_minor_edit ); + $this->mTimestamp = $timestamp; + $this->mDeleted = intval( $row->rev_deleted ); + + // NOTE: rev_parent_id = 0 indicates that there is no parent revision, while null + // indicates that the parent revision is unknown. As per MW 1.31, the database schema + // allows rev_parent_id to be NULL. + $this->mParentId = isset( $row->rev_parent_id ) ? intval( $row->rev_parent_id ) : null; + $this->mSize = isset( $row->rev_len ) ? intval( $row->rev_len ) : null; + $this->mSha1 = isset( $row->rev_sha1 ) ? $row->rev_sha1 : null; + + // NOTE: we must not call $this->mTitle->getLatestRevID() here, since the state of + // page_latest may be in limbo during revision creation. In that case, calling + // $this->mTitle->getLatestRevID() would cause a bad value to be cached in the Title + // object. During page creation, that bad value would be 0. + if ( isset( $row->page_latest ) ) { + $this->mCurrent = ( $row->rev_id == $row->page_latest ); + } + + // sanity check + if ( + $this->mPageId && $this->mTitle->exists() + && $this->mPageId !== $this->mTitle->getArticleID() + ) { + throw new InvalidArgumentException( + 'The given Title does not belong to page ID ' . $this->mPageId . + ' but actually belongs to ' . $this->mTitle->getArticleID() + ); + } + } + + /** + * MCR migration note: this replaces Revision::isCurrent + * + * @return bool + */ + public function isCurrent() { + return $this->mCurrent; + } + + /** + * MCR migration note: this replaces Revision::isDeleted + * + * @param int $field One of DELETED_* bitfield constants + * + * @return bool + */ + public function isDeleted( $field ) { + if ( $this->isCurrent() && $field === self::DELETED_TEXT ) { + // Current revisions of pages cannot have the content hidden. Skipping this + // check is very useful for Parser as it fetches templates using newKnownCurrent(). + // Calling getVisibility() in that case triggers a verification database query. + return false; // no need to check + } + + return parent::isDeleted( $field ); + } + + protected function userCan( $field, User $user ) { + if ( $this->isCurrent() && $field === self::DELETED_TEXT ) { + // Current revisions of pages cannot have the content hidden. Skipping this + // check is very useful for Parser as it fetches templates using newKnownCurrent(). + // Calling getVisibility() in that case triggers a verification database query. + return true; // no need to check + } + + return parent::userCan( $field, $user ); + } + + /** + * @return int The revision id, never null. + */ + public function getId() { + // overwritten just to add a guarantee to the contract + return parent::getId(); + } + + /** + * @return string The nominal revision size, never null. May be computed on the fly. + */ + public function getSize() { + // If length is null, calculate and remember it (potentially SLOW!). + // This is for compatibility with old database rows that don't have the field set. + if ( $this->mSize === null ) { + $this->mSize = $this->mSlots->computeSize(); + } + + return $this->mSize; + } + + /** + * @return string The revision hash, never null. May be computed on the fly. + */ + public function getSha1() { + // If hash is null, calculate it and remember (potentially SLOW!) + // This is for compatibility with old database rows that don't have the field set. + if ( $this->mSha1 === null ) { + $this->mSha1 = $this->mSlots->computeSha1(); + } + + return $this->mSha1; + } + + /** + * @param int $audience + * @param User|null $user + * + * @return UserIdentity The identity of the revision author, null if access is forbidden. + */ + public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) { + // overwritten just to add a guarantee to the contract + return parent::getUser( $audience, $user ); + } + + /** + * @param int $audience + * @param User|null $user + * + * @return CommentStoreComment The revision comment, null if access is forbidden. + */ + public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) { + // overwritten just to add a guarantee to the contract + return parent::getComment( $audience, $user ); + } + + /** + * @return string timestamp, never null + */ + public function getTimestamp() { + // overwritten just to add a guarantee to the contract + return parent::getTimestamp(); + } + +} diff --git a/includes/Storage/SlotRecord.php b/includes/Storage/SlotRecord.php new file mode 100644 index 0000000000..8769330d11 --- /dev/null +++ b/includes/Storage/SlotRecord.php @@ -0,0 +1,430 @@ +row; + + return new SlotRecord( $row, function () { + throw new SuppressedDataException( 'Content suppressed!' ); + } ); + } + + /** + * Constructs a new SlotRecord from an existing SlotRecord, overriding some fields. + * The slot's content cannot be overwritten. + * + * @param SlotRecord $slot + * @param array $overrides + * + * @return SlotRecord + */ + private static function newDerived( SlotRecord $slot, array $overrides = [] ) { + $row = $slot->row; + + foreach ( $overrides as $key => $value ) { + $row->$key = $value; + } + + return new SlotRecord( $row, $slot->content ); + } + + /** + * Constructs a new SlotRecord for a new revision, inheriting the content of the given SlotRecord + * of a previous revision. + * + * @param SlotRecord $slot + * + * @return SlotRecord + */ + public static function newInherited( SlotRecord $slot ) { + return self::newDerived( $slot, [ + 'slot_inherited' => true, + 'slot_revision' => null, + ] ); + } + + /** + * Constructs a new Slot from a Content object for a new revision. + * This is the preferred way to construct a slot for storing Content that + * resulted from a user edit. + * + * @param string $role + * @param Content $content + * @param bool $inherited + * + * @return SlotRecord + */ + public static function newUnsaved( $role, Content $content, $inherited = false ) { + Assert::parameterType( 'boolean', $inherited, '$inherited' ); + Assert::parameterType( 'string', $role, '$role' ); + + $row = [ + 'slot_id' => null, // not yet known + 'slot_address' => null, // not yet known. need setter? + 'slot_revision' => null, // not yet known + 'slot_inherited' => $inherited, + 'cont_size' => null, // compute later + 'cont_sha1' => null, // compute later + 'role_name' => $role, + 'model_name' => $content->getModel(), + ]; + + return new SlotRecord( (object)$row, $content ); + } + + /** + * Constructs a SlotRecord for a newly saved revision, based on the proto-slot that was + * supplied to the code that performed the save operation. This adds information that + * has only become available during saving, particularly the revision ID and blob address. + * + * @param int $revisionId + * @param string $blobAddress + * @param SlotRecord $protoSlot The proto-slot that was provided to the code that then + * + * @return SlotRecord + */ + public static function newSaved( $revisionId, $blobAddress, SlotRecord $protoSlot ) { + Assert::parameterType( 'integer', $revisionId, '$revisionId' ); + Assert::parameterType( 'string', $blobAddress, '$blobAddress' ); + + return self::newDerived( $protoSlot, [ + 'slot_revision' => $revisionId, + 'cont_address' => $blobAddress, + ] ); + } + + /** + * SlotRecord constructor. + * + * The following fields are supported by the $row parameter: + * + * $row->blob_data + * $row->blob_address + * + * @param object $row A database row composed of fields of the slot and content tables, + * as a raw object. Any field value can be a callback that produces the field value + * given this SlotRecord as a parameter. However, plain strings cannot be used as + * callbacks here, for security reasons. + * @param Content|callable $content The content object associated with the slot, or a + * callback that will return that Content object, given this SlotRecord as a parameter. + */ + public function __construct( $row, $content ) { + Assert::parameterType( 'object', $row, '$row' ); + Assert::parameterType( 'Content|callable', $content, '$content' ); + + $this->row = $row; + $this->content = $content; + } + + /** + * Implemented to defy serialization. + * + * @throws LogicException always + */ + public function __sleep() { + throw new LogicException( __CLASS__ . ' is not serializable.' ); + } + + /** + * Returns the Content of the given slot. + * + * @note This is free to load Content from whatever subsystem is necessary, + * performing potentially expensive operations and triggering I/O-related + * failure modes. + * + * @note This method does not apply audience filtering. + * + * @throws SuppressedDataException if access to the content is not allowed according + * to the audience check performed by RevisionRecord::getSlot(). + * + * @return Content The slot's content. This is a direct reference to the internal instance, + * copy before exposing to application logic! + */ + public function getContent() { + if ( $this->content instanceof Content ) { + return $this->content; + } + + $obj = call_user_func( $this->content, $this ); + + Assert::postcondition( + $obj instanceof Content, + 'Slot content callback should return a Content object' + ); + + $this->content = $obj; + + return $this->content; + } + + /** + * Returns the string value of a data field from the database row supplied to the constructor. + * If the field was set to a callback, that callback is invoked and the result returned. + * + * @param string $name + * + * @throws OutOfBoundsException + * @return mixed Returns the field's value, or null if the field is NULL in the DB row. + */ + private function getField( $name ) { + if ( !isset( $this->row->$name ) ) { + // distinguish between unknown and uninitialized fields + if ( property_exists( $this->row, $name ) ) { + throw new IncompleteRevisionException( 'Uninitialized field: ' . $name ); + } else { + throw new OutOfBoundsException( 'No such field: ' . $name ); + } + } + + $value = $this->row->$name; + + // NOTE: allow callbacks, but don't trust plain string callables from the database! + if ( !is_string( $value ) && is_callable( $value ) ) { + $value = call_user_func( $value, $this ); + $this->setField( $name, $value ); + } + + return $value; + } + + /** + * Returns the string value of a data field from the database row supplied to the constructor. + * + * @param string $name + * + * @throws OutOfBoundsException + * @throws IncompleteRevisionException + * @return string Returns the string value + */ + private function getStringField( $name ) { + return strval( $this->getField( $name ) ); + } + + /** + * Returns the int value of a data field from the database row supplied to the constructor. + * + * @param string $name + * + * @throws OutOfBoundsException + * @throws IncompleteRevisionException + * @return int Returns the int value + */ + private function getIntField( $name ) { + return intval( $this->getField( $name ) ); + } + + /** + * @param string $name + * @return bool whether this record contains the given field + */ + private function hasField( $name ) { + return isset( $this->row->$name ); + } + + /** + * Returns the ID of the revision this slot is associated with. + * + * @return int + */ + public function getRevision() { + return $this->getIntField( 'slot_revision' ); + } + + /** + * Whether this slot was inherited from an older revision. + * + * @return bool + */ + public function isInherited() { + return $this->getIntField( 'slot_inherited' ) !== 0; + } + + /** + * Whether this slot has an address. Slots will have an address if their + * content has been stored. While building a new revision, + * SlotRecords will not have an address associated. + * + * @return bool + */ + public function hasAddress() { + return $this->hasField( 'cont_address' ); + } + + /** + * Whether this slot has revision ID associated. Slots will have a revision ID associated + * only if they were loaded as part of an existing revision. While building a new revision, + * Slotrecords will not have a revision ID associated. + * + * @return bool + */ + public function hasRevision() { + return $this->hasField( 'slot_revision' ); + } + + /** + * Returns the role of the slot. + * + * @return string + */ + public function getRole() { + return $this->getStringField( 'role_name' ); + } + + /** + * Returns the address of this slot's content. + * This address can be used with BlobStore to load the Content object. + * + * @return string + */ + public function getAddress() { + return $this->getStringField( 'cont_address' ); + } + + /** + * Returns the content size + * + * @return int size of the content, in bogo-bytes, as reported by Content::getSize. + */ + public function getSize() { + try { + $size = $this->getIntField( 'cont_size' ); + } catch ( IncompleteRevisionException $ex ) { + $size = $this->getContent()->getSize(); + $this->setField( 'cont_size', $size ); + } + + return $size; + } + + /** + * Returns the content size + * + * @return string hash of the content. + */ + public function getSha1() { + try { + $sha1 = $this->getStringField( 'cont_sha1' ); + } catch ( IncompleteRevisionException $ex ) { + $format = $this->hasField( 'format_name' ) + ? $this->getStringField( 'format_name' ) + : null; + + $data = $this->getContent()->serialize( $format ); + $sha1 = self::base36Sha1( $data ); + $this->setField( 'cont_sha1', $sha1 ); + } + + return $sha1; + } + + /** + * Returns the content model. This is the model name that decides + * which ContentHandler is appropriate for interpreting the + * data of the blob referenced by the address returned by getAddress(). + * + * @return string the content model of the content + */ + public function getModel() { + try { + $model = $this->getStringField( 'model_name' ); + } catch ( IncompleteRevisionException $ex ) { + $model = $this->getContent()->getModel(); + $this->setField( 'model_name', $model ); + } + + return $model; + } + + /** + * Returns the blob serialization format as a MIME type. + * + * @note When this method returns null, the caller is expected + * to auto-detect the serialization format, or to rely on + * the default format associated with the content model. + * + * @return string|null + */ + public function getFormat() { + // XXX: we currently do not plan to store the format for each slot! + + if ( $this->hasField( 'format_name' ) ) { + return $this->getStringField( 'format_name' ); + } + + return null; + } + + /** + * @param string $name + * @param string|int|null $value + */ + private function setField( $name, $value ) { + $this->row->$name = $value; + } + + /** + * Get the base 36 SHA-1 value for a string of text + * + * MCR migration note: this replaces Revision::base36Sha1 + * + * @param string $blob + * @return string + */ + public static function base36Sha1( $blob ) { + return \Wikimedia\base_convert( sha1( $blob ), 16, 36, 31 ); + } + +} diff --git a/includes/Storage/SqlBlobStore.php b/includes/Storage/SqlBlobStore.php new file mode 100644 index 0000000000..69e1539ad1 --- /dev/null +++ b/includes/Storage/SqlBlobStore.php @@ -0,0 +1,580 @@ +dbLoadBalancer = $dbLoadBalancer; + $this->cache = $cache; + $this->wikiId = $wikiId; + } + + /** + * @return int time for which blobs can be cached, in seconds + */ + public function getCacheExpiry() { + return $this->cacheExpiry; + } + + /** + * @param int $cacheExpiry time for which blobs can be cached, in seconds + */ + public function setCacheExpiry( $cacheExpiry ) { + Assert::parameterType( 'integer', $cacheExpiry, '$cacheExpiry' ); + + $this->cacheExpiry = $cacheExpiry; + } + + /** + * @return bool whether blobs should be compressed for storage + */ + public function getCompressBlobs() { + return $this->compressBlobs; + } + + /** + * @param bool $compressBlobs whether blobs should be compressed for storage + */ + public function setCompressBlobs( $compressBlobs ) { + $this->compressBlobs = $compressBlobs; + } + + /** + * @return false|string The legacy encoding to assume for blobs that are not marked as utf8. + * False means handling of legacy encoding is disabled, and utf8 assumed. + */ + public function getLegacyEncoding() { + return $this->legacyEncoding; + } + + /** + * @return Language|null The locale to use when decoding from a legacy encoding, or null + * if handling of legacy encoding is disabled. + */ + public function getLegacyEncodingConversionLang() { + return $this->legacyEncodingConversionLang; + } + + /** + * @param string $legacyEncoding The legacy encoding to assume for blobs that are + * not marked as utf8. + * @param Language $language The locale to use when decoding from a legacy encoding. + */ + public function setLegacyEncoding( $legacyEncoding, Language $language ) { + Assert::parameterType( 'string', $legacyEncoding, '$legacyEncoding' ); + + $this->legacyEncoding = $legacyEncoding; + $this->legacyEncodingConversionLang = $language; + } + + /** + * @return bool Whether to use the ExternalStore mechanism for storing blobs. + */ + public function getUseExternalStore() { + return $this->useExternalStore; + } + + /** + * @param bool $useExternalStore Whether to use the ExternalStore mechanism for storing blobs. + */ + public function setUseExternalStore( $useExternalStore ) { + Assert::parameterType( 'boolean', $useExternalStore, '$useExternalStore' ); + + $this->useExternalStore = $useExternalStore; + } + + /** + * @return LoadBalancer + */ + private function getDBLoadBalancer() { + return $this->dbLoadBalancer; + } + + /** + * @param int $index A database index, like DB_MASTER or DB_REPLICA + * + * @return IDatabase + */ + private function getDBConnection( $index ) { + $lb = $this->getDBLoadBalancer(); + return $lb->getConnection( $index, [], $this->wikiId ); + } + + /** + * Stores an arbitrary blob of data and returns an address that can be used with + * getBlob() to retrieve the same blob of data, + * + * @param string $data + * @param array $hints An array of hints. + * + * @throws BlobAccessException + * @return string an address that can be used with getBlob() to retrieve the data. + */ + public function storeBlob( $data, $hints = [] ) { + try { + $flags = $this->compressData( $data ); + + # Write to external storage if required + if ( $this->useExternalStore ) { + // Store and get the URL + $data = ExternalStore::insertToDefault( $data ); + if ( !$data ) { + throw new BlobAccessException( "Failed to store text to external storage" ); + } + if ( $flags ) { + $flags .= ','; + } + $flags .= 'external'; + + // TODO: we could also return an address for the external store directly here. + // That would mean bypassing the text table entirely when the external store is + // used. We'll need to assess expected fallout before doing that. + } + + $dbw = $this->getDBConnection( DB_MASTER ); + + $old_id = $dbw->nextSequenceValue( 'text_old_id_seq' ); + $dbw->insert( + 'text', + [ + 'old_id' => $old_id, + 'old_text' => $data, + 'old_flags' => $flags, + ], + __METHOD__ + ); + + $textId = $dbw->insertId(); + + return 'tt:' . $textId; + } catch ( MWException $e ) { + throw new BlobAccessException( $e->getMessage(), 0, $e ); + } + } + + /** + * Retrieve a blob, given an address. + * Currently hardcoded to the 'text' table storage engine. + * + * MCR migration note: this replaces Revision::loadText + * + * @param string $blobAddress + * @param int $queryFlags + * + * @throws BlobAccessException + * @return string + */ + public function getBlob( $blobAddress, $queryFlags = 0 ) { + Assert::parameterType( 'string', $blobAddress, '$blobAddress' ); + + // No negative caching; negative hits on text rows may be due to corrupted replica DBs + $blob = $this->cache->getWithSetCallback( + // TODO: change key, since this is not necessarily revision text! + $this->cache->makeKey( 'revisiontext', 'textid', $blobAddress ), + $this->getCacheTTL(), + function () use ( $blobAddress, $queryFlags ) { + return $this->fetchBlob( $blobAddress, $queryFlags ); + }, + [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => IExpiringStore::TTL_PROC_LONG ] + ); + + if ( $blob === false ) { + throw new BlobAccessException( 'Failed to load blob from address ' . $blobAddress ); + } + + return $blob; + } + + /** + * MCR migration note: this corresponds to Revision::fetchText + * + * @param string $blobAddress + * @param int $queryFlags + * + * @throw BlobAccessException + * @return string|false + */ + private function fetchBlob( $blobAddress, $queryFlags ) { + 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 { + // XXX: change to better exceptions! That makes migration more difficult, though. + throw new BlobAccessException( "Unknown blob address schema: $schema" ); + } + + if ( !$textId || $id !== (string)$textId ) { + // XXX: change to better exceptions! That makes migration more difficult, though. + throw new BlobAccessException( "Bad blob address: $blobAddress" ); + } + + // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables + // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases. + $queryFlags |= DBAccessObjectUtils::hasFlags( $queryFlags, self::READ_LATEST ) + ? self::READ_LATEST_IMMUTABLE + : 0; + + list( $index, $options, $fallbackIndex, $fallbackOptions ) = + DBAccessObjectUtils::getDBOptions( $queryFlags ); + + // Text data is immutable; check replica DBs first. + $row = $this->getDBConnection( $index )->selectRow( + 'text', + [ 'old_text', 'old_flags' ], + [ 'old_id' => $textId ], + __METHOD__, + $options + ); + + // Fallback to DB_MASTER in some cases if the row was not found, using the appropriate + // options, such as FOR UPDATE to avoid missing rows due to REPEATABLE-READ. + if ( !$row && $fallbackIndex !== null ) { + $row = $this->getDBConnection( $fallbackIndex )->selectRow( + 'text', + [ 'old_text', 'old_flags' ], + [ 'old_id' => $textId ], + __METHOD__, + $fallbackOptions + ); + } + + if ( !$row ) { + wfWarn( __METHOD__ . ": No text row with ID $textId." ); + return false; + } + + $blob = $this->expandBlob( $row->old_text, $row->old_flags, $blobAddress ); + + if ( $blob === false ) { + wfWarn( __METHOD__ . ": Bad data in text row $textId." ); + return false; + } + + return $blob; + } + + /** + * Expand a raw data blob according to the flags given. + * + * MCR migration note: this replaces Revision::getRevisionText + * + * @note direct use is deprecated, use getBlob() or SlotRecord::getContent() instead. + * @todo make this private, there should be no need to use this method outside this class. + * + * @param string $raw The raw blob data, to be processed according to $flags. + * May be the blob itself, or the blob compressed, or just the address + * of the actual blob, depending on $flags. + * @param string|string[] $flags Blob flags, such as 'external' or 'gzip'. + * @param string|null $cacheKey May be used for caching if given + * + * @return false|string The expanded blob or false on failure + */ + public function expandBlob( $raw, $flags, $cacheKey = null ) { + if ( is_string( $flags ) ) { + $flags = explode( ',', $flags ); + } + + // Use external methods for external objects, text in table is URL-only then + if ( in_array( 'external', $flags ) ) { + $url = $raw; + $parts = explode( '://', $url, 2 ); + if ( count( $parts ) == 1 || $parts[1] == '' ) { + return false; + } + + if ( $cacheKey && $this->wikiId === false ) { + // Make use of the wiki-local revision text cache. + // The cached value should be decompressed, so handle that and return here. + // NOTE: we rely on $this->cache being the right cache for $this->wikiId! + return $this->cache->getWithSetCallback( + // TODO: change key, since this is not necessarily revision text! + $this->cache->makeKey( 'revisiontext', 'textid', $cacheKey ), + $this->getCacheTTL(), + function () use ( $url, $flags ) { + // No negative caching per BlobStore::getBlob() + $blob = ExternalStore::fetchFromURL( $url, [ 'wiki' => $this->wikiId ] ); + + return $this->decompressData( $blob, $flags ); + }, + [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => WANObjectCache::TTL_PROC_LONG ] + ); + } else { + $blob = ExternalStore::fetchFromURL( $url, [ 'wiki' => $this->wikiId ] ); + return $this->decompressData( $blob, $flags ); + } + } else { + return $this->decompressData( $raw, $flags ); + } + } + + /** + * If $wgCompressRevisions is enabled, we will compress data. + * The input string is modified in place. + * Return value is the flags field: contains 'gzip' if the + * data is compressed, and 'utf-8' if we're saving in UTF-8 + * mode. + * + * MCR migration note: this replaces Revision::compressRevisionText + * + * @note direct use is deprecated! + * @todo make this private, there should be no need to use this method outside this class. + * + * @param mixed &$blob Reference to a text + * + * @return string + */ + public function compressData( &$blob ) { + $blobFlags = []; + + // Revisions not marked as UTF-8 will have legacy decoding applied by decompressData(). + // XXX: if $this->legacyEncoding is not set, we could skip this. May be risky, though. + $blobFlags[] = 'utf-8'; + + if ( $this->compressBlobs ) { + if ( function_exists( 'gzdeflate' ) ) { + $deflated = gzdeflate( $blob ); + + if ( $deflated === false ) { + wfLogWarning( __METHOD__ . ': gzdeflate() failed' ); + } else { + $blob = $deflated; + $blobFlags[] = 'gzip'; + } + } else { + wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" ); + } + } + return implode( ',', $blobFlags ); + } + + /** + * Re-converts revision text according to its flags. + * + * MCR migration note: this replaces Revision::decompressRevisionText + * + * @note direct use is deprecated, use getBlob() or SlotRecord::getContent() instead. + * @todo make this private, there should be no need to use this method outside this class. + * + * @param mixed $blob Reference to a text + * @param array $blobFlags Compression flags + * + * @return string|bool Decompressed text, or false on failure + */ + public function decompressData( $blob, $blobFlags ) { + if ( $blob === false ) { + // Text failed to be fetched; nothing to do + return false; + } + + if ( in_array( 'gzip', $blobFlags ) ) { + # Deal with optional compression of archived pages. + # This can be done periodically via maintenance/compressOld.php, and + # as pages are saved if $wgCompressRevisions is set. + $blob = gzinflate( $blob ); + + if ( $blob === false ) { + wfLogWarning( __METHOD__ . ': gzinflate() failed' ); + return false; + } + } + + if ( in_array( 'object', $blobFlags ) ) { + # Generic compressed storage + $obj = unserialize( $blob ); + if ( !is_object( $obj ) ) { + // Invalid object + return false; + } + $blob = $obj->getText(); + } + + // Needed to support old revisions left over from from the 1.4 / 1.5 migration. + if ( $blob !== false && $this->legacyEncoding && $this->legacyEncodingConversionLang + && !in_array( 'utf-8', $blobFlags ) && !in_array( 'utf8', $blobFlags ) + ) { + # Old revisions kept around in a legacy encoding? + # Upconvert on demand. + # ("utf8" checked for compatibility with some broken + # conversion scripts 2008-12-30) + $blob = $this->legacyEncodingConversionLang->iconv( $this->legacyEncoding, 'UTF-8', $blob ); + } + + return $blob; + } + + /** + * Get the text cache TTL + * + * MCR migration note: this replaces Revision::getCacheTTL + * + * @return int + */ + private function getCacheTTL() { + if ( $this->cache->getQoS( WANObjectCache::ATTR_EMULATION ) + <= WANObjectCache::QOS_EMULATION_SQL + ) { + // Do not cache RDBMs blobs in...the RDBMs store + $ttl = WANObjectCache::TTL_UNCACHEABLE; + } else { + $ttl = $this->cacheExpiry ?: WANObjectCache::TTL_UNCACHEABLE; + } + + return $ttl; + } + + /** + * Returns an ID corresponding to the old_id field in the text table, corresponding + * to the given $address. + * + * Currently, $address must start with 'tt:' followed by a decimal integer representing + * the old_id; if $address does not start with 'tt:', null is returned. However, + * the implementation may change to insert rows into the text table on the fly. + * + * @note This method exists for use with the text table based storage schema. + * It should not be assumed that is will function with all future kinds of content addresses. + * + * @deprecated since 1.31, so not assume that all blob addresses refer to a row in the text + * table. This method should become private once the relevant refactoring in WikiPage is + * complete. + * + * @param string $address + * + * @return int|null + */ + public function getTextIdFromAddress( $address ) { + list( $schema, $id, ) = self::splitBlobAddress( $address ); + + if ( $schema !== 'tt' ) { + return null; + } + + $textId = intval( $id ); + + if ( !$textId || $id !== (string)$textId ) { + throw new InvalidArgumentException( "Malformed text_id: $id" ); + } + + return $textId; + } + + /** + * Splits a blob address into three parts: the schema, the ID, and parameters/flags. + * + * @param string $address + * + * @throws InvalidArgumentException + * @return array [ $schema, $id, $parameters ], with $parameters being an assoc array. + */ + private static function splitBlobAddress( $address ) { + if ( !preg_match( '/^(\w+):(\w+)(\?(.*))?$/', $address, $m ) ) { + throw new InvalidArgumentException( "Bad blob address: $address" ); + } + + $schema = strtolower( $m[1] ); + $id = $m[2]; + $parameters = isset( $m[4] ) ? wfCgiToArray( $m[4] ) : []; + + return [ $schema, $id, $parameters ]; + } + +} diff --git a/includes/Storage/SuppressedDataException.php b/includes/Storage/SuppressedDataException.php new file mode 100644 index 0000000000..24f16a6482 --- /dev/null +++ b/includes/Storage/SuppressedDataException.php @@ -0,0 +1,33 @@ +setTitle( $this->getTitle() ); + $rev = new Revision( $row, 0, $this->getTitle() ); + $text = FeedUtils::formatDiffRow( $this->getTitle(), $this->getTitle()->getPreviousRevisionID( $rev->getId() ), @@ -639,12 +639,10 @@ class HistoryPager extends ReverseChronologicalPager { */ function historyLine( $row, $next, $notificationtimestamp = false, $latest = false, $firstInList = false ) { - $rev = new Revision( $row ); - $rev->setTitle( $this->getTitle() ); + $rev = new Revision( $row, 0, $this->getTitle() ); if ( is_object( $next ) ) { - $prevRev = new Revision( $next ); - $prevRev->setTitle( $this->getTitle() ); + $prevRev = new Revision( $next, 0, $this->getTitle() ); } else { $prevRev = null; } diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 4d37af3162..366a6df98f 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -67,12 +67,12 @@ class ApiBlock extends ApiBase { $params['user'] = $username; } } else { - $target = User::newFromName( $params['user'] ); + list( $target, $type ) = SpecialBlock::getTargetAndType( $params['user'] ); // T40633 - if the target is a user (not an IP address), but it // doesn't exist or is unusable, error. - if ( $target instanceof User && - ( $target->isAnon() /* doesn't exist */ || !User::isUsableName( $target->getName() ) ) + if ( $type === Block::TYPE_USER && + ( $target->isAnon() /* doesn't exist */ || !User::isUsableName( $params['user'] ) ) ) { $this->dieWithError( [ 'nosuchusershort', $params['user'] ], 'nosuchuser' ); } diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php index edea2661a2..84ea2662d3 100644 --- a/includes/api/ApiEmailUser.php +++ b/includes/api/ApiEmailUser.php @@ -71,7 +71,7 @@ class ApiEmailUser extends ApiBase { } $result = array_filter( [ - 'result' => $retval->isGood() ? 'Success' : ( $retval->isOk() ? 'Warnings' : 'Failure' ), + 'result' => $retval->isGood() ? 'Success' : ( $retval->isOK() ? 'Warnings' : 'Failure' ), 'warnings' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'warning' ), 'errors' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'error' ), ] ); diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php index e3a757f79a..db150323f9 100644 --- a/includes/api/ApiFeedWatchlist.php +++ b/includes/api/ApiFeedWatchlist.php @@ -215,10 +215,7 @@ class ApiFeedWatchlist extends ApiBase { preg_match( '!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment, $matches ) ) { global $wgParser; - - $sectionTitle = $wgParser->stripSectionName( $matches[2] ); - $sectionTitle = Sanitizer::normalizeSectionNameWhitespace( $sectionTitle ); - $titleUrl .= Title::newFromText( '#' . $sectionTitle )->getFragmentForURL(); + $titleUrl .= $wgParser->guessSectionNameFromWikiText( $matches[ 2 ] ); } $timestamp = $info['timestamp']; diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index edc1a3e7e9..3bda3e8113 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -1933,7 +1933,7 @@ class ApiMain extends ApiBase { $id = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_PRIMARY ); $idFallback = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_FALLBACK ); $headline = Linker::makeHeadline( min( 6, $level ), - ' class="apihelp-header"', + ' class="apihelp-header">', $id, $header, '', @@ -1961,7 +1961,7 @@ class ApiMain extends ApiBase { $id = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_PRIMARY ); $idFallback = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_FALLBACK ); $headline = Linker::makeHeadline( min( 6, $level ), - ' class="apihelp-header"', + ' class="apihelp-header">', $id, $header, '', diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 8e9b1b4973..179e6f7b25 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -262,7 +262,7 @@ abstract class ApiQueryBase extends ApiBase { * @param string|string[] $value Value; ignored if null or empty array; */ protected function addWhereFld( $field, $value ) { - if ( $value !== null && count( $value ) ) { + if ( $value !== null && !( is_array( $value ) && !$value ) ) { $this->where[$field] = $value; } } diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php index f0c4180069..2c681f57af 100644 --- a/includes/api/ApiQuerySearch.php +++ b/includes/api/ApiQuerySearch.php @@ -272,6 +272,16 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { if ( isset( $prop['isfilematch'] ) ) { $vals['isfilematch'] = $result->isFileMatch(); } + + if ( isset( $prop['extensiondata'] ) ) { + $extra = $result->getExtensionData(); + // Add augmented data to the result. The data would be organized as a map: + // augmentorName => data + if ( $extra ) { + $vals['extensiondata'] = ApiResult::addMetadataToResultVars( $extra ); + } + } + return $vals; } @@ -372,6 +382,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { 'categorysnippet', 'score', // deprecated 'hasrelated', // deprecated + 'extensiondata', ], ApiBase::PARAM_ISMULTI => true, ApiBase::PARAM_HELP_MSG_PER_VALUE => [], diff --git a/includes/api/ApiTag.php b/includes/api/ApiTag.php index 9304c2b414..c9f6db3994 100644 --- a/includes/api/ApiTag.php +++ b/includes/api/ApiTag.php @@ -39,7 +39,7 @@ class ApiTag extends ApiBase { // Check if user can add tags if ( $params['tags'] ) { $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user ); - if ( !$ableToTag->isOk() ) { + if ( !$ableToTag->isOK() ) { $this->dieStatus( $ableToTag ); } } diff --git a/includes/api/i18n/ar.json b/includes/api/i18n/ar.json index 6d7fea2c8b..44be54691d 100644 --- a/includes/api/i18n/ar.json +++ b/includes/api/i18n/ar.json @@ -9,7 +9,8 @@ "Maroen1990", "محمد أحمد عبد الفتاح", "ديفيد", - "ASHmed" + "ASHmed", + "Yasser Yousssef" ] }, "apihelp-main-param-action": "أي فعل للعمل.", @@ -30,7 +31,9 @@ "apihelp-block-param-noemail": "منع المستخدم من إرسال البريد الإلكتروني من خلال الويكي. (يتطلب صلاحية blockemail).", "apihelp-block-param-hidename": "إخفاء اسم المستخدم من سجل المنع. (يتطلب صلاحية hideuser).", "apihelp-block-param-allowusertalk": "تسمح للمستخدم بتحرير صفحة النقاش الخاصة (يعتمد على [[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]).", + "apihelp-block-param-reblock": "إذا كان المستخدم محظوراً بالفعل، يستبدل الحظر القائم.", "apihelp-block-param-watchuser": "مشاهدة صفحة المستخدم ونقاش IP.", + "apihelp-block-param-tags": "تغيير الوسوم للتطبيق على الإدخال في سجل الحظر.", "apihelp-block-example-ip-simple": "منع عنوان IP 192.0.2.5 لمدة ثلاثة أيام بسبب >المخالفة الأولى.", "apihelp-block-example-user-complex": "منع المستخدم المخرب لأجل غير مسمى بسبب التخريب، ومنع إنشاء حساب جديد وإرسال بريد إلكتروني.", "apihelp-changeauthenticationdata-summary": "تغيير بيانات المصادقة للمستخدم الحالي.", diff --git a/includes/api/i18n/de.json b/includes/api/i18n/de.json index ba8d2f989d..85b65cf17c 100644 --- a/includes/api/i18n/de.json +++ b/includes/api/i18n/de.json @@ -838,6 +838,7 @@ "apihelp-query+search-param-prop": "Eigenschaften zur Rückgabe:", "apihelp-query+search-param-qiprofile": "Zu verwendendes anfrageunabhängiges Profil (wirkt sich auf den Ranking-Algorithmus aus).", "apihelp-query+search-paramvalue-prop-wordcount": "Ergänzt den Wortzähler der Seite.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Ergänzt zusätzliche von Erweiterungen erzeugte Daten.", "apihelp-query+search-param-limit": "Wie viele Seiten insgesamt zurückgegeben werden sollen.", "apihelp-query+search-example-simple": "Nach meaning suchen.", "apihelp-query+search-example-text": "Texte nach meaning durchsuchen.", diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json index 91c3e185b0..e1360c8ad8 100644 --- a/includes/api/i18n/en.json +++ b/includes/api/i18n/en.json @@ -1153,6 +1153,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "Adds the title of the matching section.", "apihelp-query+search-paramvalue-prop-categorysnippet": "Adds a parsed snippet of the matching category.", "apihelp-query+search-paramvalue-prop-isfilematch": "Adds a boolean indicating if the search matched file content.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Adds extra data generated by extensions.", "apihelp-query+search-paramvalue-prop-score": "Ignored.", "apihelp-query+search-paramvalue-prop-hasrelated": "Ignored.", "apihelp-query+search-param-limit": "How many total pages to return.", diff --git a/includes/api/i18n/es.json b/includes/api/i18n/es.json index b84057ea6f..ef5f50d257 100644 --- a/includes/api/i18n/es.json +++ b/includes/api/i18n/es.json @@ -1069,6 +1069,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "Añade el título de la sección correspondiente.", "apihelp-query+search-paramvalue-prop-categorysnippet": "Añade un fragmento analizado de la categoría correspondiente.", "apihelp-query+search-paramvalue-prop-isfilematch": "Añade un booleano que indica si la búsqueda corresponde al contenido del archivo.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Añade datos adicionales generados por las extensiones.", "apihelp-query+search-paramvalue-prop-score": "Ignorado.", "apihelp-query+search-paramvalue-prop-hasrelated": "Ignorado", "apihelp-query+search-param-limit": "Cuántas páginas en total se devolverán.", diff --git a/includes/api/i18n/fr.json b/includes/api/i18n/fr.json index a56b42f083..d9bf39c20f 100644 --- a/includes/api/i18n/fr.json +++ b/includes/api/i18n/fr.json @@ -28,7 +28,8 @@ "Pols12", "The RedBurn", "Umherirrender", - "Thibaut120094" + "Thibaut120094", + "KATRINE1992" ] }, "apihelp-main-extended-description": "
\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
\nÉtat : 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\nRequêtes erronées : 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\nTest : Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].", @@ -1091,6 +1092,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "Ajoute le titre de la section correspondante.", "apihelp-query+search-paramvalue-prop-categorysnippet": "Ajoute un extrait analysé de la catégorie correspondante.", "apihelp-query+search-paramvalue-prop-isfilematch": "Ajoute un booléen indiquant si la recherche correspond au contenu du fichier.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Va ajouter des données générées supplémentaires par extension.", "apihelp-query+search-paramvalue-prop-score": "Ignoré.", "apihelp-query+search-paramvalue-prop-hasrelated": "Ignoré.", "apihelp-query+search-param-limit": "Combien de pages renvoyer au total.", diff --git a/includes/api/i18n/gl.json b/includes/api/i18n/gl.json index aa21cd701d..6b01875cc2 100644 --- a/includes/api/i18n/gl.json +++ b/includes/api/i18n/gl.json @@ -12,7 +12,8 @@ "Macofe", "Hamilton Abreu", "Umherirrender", - "Fitoschido" + "Fitoschido", + "Athena in Wonderland" ] }, "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Documentación]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discusión]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anuncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e solicitudes]\n
\nEstado: Tódalas funcionalidades mostradas nesta páxina deberían estar funcionando, pero a API aínda está desenrolo, e pode ser modificada en calquera momento. Apúntese na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discusión mediawiki-api-announce] para estar informado acerca das actualizacións.\n\nSolicitudes incorrectas: Cando se envían solicitudes incorrectas á API, envíase unha cabeceira HTTP coa chave \"MediaWiki-API-Error\" e, a seguir, tanto o valor da cabeceira como o código de erro retornado serán definidos co mesmo valor. Para máis información, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Erros e avisos]].\n\nTest: Para facilitar as probas das peticións da API, consulte [[Special:ApiSandbox]].", @@ -1458,7 +1459,7 @@ "api-help-param-upload": "Debe ser enviado como un ficheiro importado usando multipart/form-data.", "api-help-param-multi-separate": "Separe os valores con | ou [[Special:ApiHelp/main#main/datatypes|outros]].", "api-help-param-multi-max": "O número máximo de valores é {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} para os bots).", - "api-help-param-multi-max-simple": "O número máximo de valores é {{PLURAL:1$|1$}}.", + "api-help-param-multi-max-simple": "O número máximo de valores é {{PLURAL:$1|$1}}.", "api-help-param-multi-all": "Para especificar tódolos valores use $1.", "api-help-param-default": "Por defecto: $1", "api-help-param-default-empty": "Por defecto: (baleiro)", @@ -1469,6 +1470,8 @@ "api-help-param-direction": "En que dirección enumerar:\n;newer:Lista os máis antigos primeiro. Nota: $1start ten que estar antes que $1end.\n;older:Lista os máis novos primeiro (por defecto). Nota: $1start ten que estar despois que $1end.", "api-help-param-continue": "Cando estean dispoñibles máis resultados, use isto para continuar.", "api-help-param-no-description": "(sen descrición)", + "api-help-param-maxbytes": "Non pode ser máis longo que $1 {{PLURAL:$1|byte|bytes}}.", + "api-help-param-maxchars": "Non pode ser máis longo que $1 {{PLURAL:$1|carácter|caracteres}}.", "api-help-examples": "{{PLURAL:$1|Exemplo|Exemplos}}:", "api-help-permissions": "{{PLURAL:$1|Permiso|Permisos}}:", "api-help-permissions-granted-to": "{{PLURAL:$1|Concedida a|Concedidas a}}: $2", diff --git a/includes/api/i18n/lt.json b/includes/api/i18n/lt.json index 2726944c91..6f4c6a32b7 100644 --- a/includes/api/i18n/lt.json +++ b/includes/api/i18n/lt.json @@ -265,6 +265,7 @@ "apihelp-query+logevents-param-prop": "Kurias savybes gauti:", "apihelp-query+logevents-paramvalue-prop-ids": "Prideda žurnalo įvykio ID.", "apihelp-query+logevents-paramvalue-prop-type": "Prideda žurnalo įvykio tipą.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Prideda papildomus duomenis, sugeneruotus plėtinių.", "apihelp-query+transcludedin-paramvalue-prop-pageid": "Kiekvieno puslapio ID.", "apihelp-query+transcludedin-paramvalue-prop-title": "Kiekvieno puslapio pavadinimas.", "apihelp-query+transcludedin-param-limit": "Kiek gražinti.", diff --git a/includes/api/i18n/mk.json b/includes/api/i18n/mk.json index 10934b453c..a0129009c6 100644 --- a/includes/api/i18n/mk.json +++ b/includes/api/i18n/mk.json @@ -26,9 +26,9 @@ "apihelp-block-param-autoblock": "Автоматски блокирај ја последно употребената IP-адреса и сите понатамошни IP-адреси од кои лицето ќе се обиде да се најави.", "apihelp-block-param-noemail": "Оневозможи му на корисникот да испаќа е-пошта преку викито. (Го бара правото code>blockemail).", "apihelp-block-param-hidename": "Скриј го корисничкото име од дневникот на блокирања. (Го бара правото hideuser)", - "apihelp-block-param-allowusertalk": "Овозможи му на корисникот да си ја уредува сопствената страница за разговор (зависи од [[mw:Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]).", + "apihelp-block-param-allowusertalk": "Овозможи му на корисникот да ја уредува неговата разговорна страница (зависи од [[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]).", "apihelp-block-param-reblock": "Ако корисникот е веќе блокиран, наметни врз постоечкиот блок.", - "apihelp-block-param-watchuser": "Набљудувај ја корисничката страница и страницата за разговор на овој корисник или IP-адреса", + "apihelp-block-param-watchuser": "Набљудувај ја корисничката страница и разговорна страница на овој корисник или IP-адреса", "apihelp-block-example-ip-simple": "Блокирај ја IP-адресата 192.0.2.5 три дена со причината Прва опомена.", "apihelp-block-example-user-complex": "Блокирај го корисникот Vandal (Вандал) бесконечно со причината Vandal (Вандализам) и оневозможи создавање на нови сметки и праќање е-пошта.", "apihelp-checktoken-summary": "Проверка на полноважноста на шифрата од [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].", @@ -205,7 +205,7 @@ "apihelp-move-param-fromid": "Назнака на страницата што треба да се премести. Не може да се користи заедно со $1from.", "apihelp-move-param-to": "Како да гласи новата страница.", "apihelp-move-param-reason": "Причина за преименувањето.", - "apihelp-move-param-movetalk": "Преименувај ја и страницата за разговор, ако ја има.", + "apihelp-move-param-movetalk": "Преименувај ја и разговорната страница, ако ја има.", "apihelp-move-param-movesubpages": "Преименувај потстраници, ако има.", "apihelp-move-param-noredirect": "Не прави пренасочување.", "apihelp-move-param-watch": "Додај ги страницата и пренасочувањето во набљудуваните на тековниот корисник.", diff --git a/includes/api/i18n/nb.json b/includes/api/i18n/nb.json index 2e89ea397e..dc9fb1169c 100644 --- a/includes/api/i18n/nb.json +++ b/includes/api/i18n/nb.json @@ -66,6 +66,7 @@ "apihelp-compare-param-totitle": "Andre tittel å sammenligne.", "apihelp-compare-param-toid": "Andre side-ID å sammenligne.", "apihelp-compare-param-torev": "Andre revisjon å sammenligne.", + "apihelp-compare-param-torelative": "Bruk en revisjon som er relativ til revisjonen som hentes fra fromtitle, fromid eller fromrev. Alle de andre «to»-alternativene vil ignoreres.", "apihelp-compare-param-totext": "Bruk denne teksten i stedet for innholdet i revisjonen spesifisert av totitle, toid eller torev.", "apihelp-compare-param-topst": "Gjør en transformering av totext før lagring.", "apihelp-compare-param-tocontentmodel": "Innholdsmodellen til totext. Om denne ikke angis vil den bli gjettet ut fra andre parametere.", @@ -78,6 +79,7 @@ "apihelp-compare-paramvalue-prop-title": "Sidetitlene for «from»- og «to»-revisjonene.", "apihelp-compare-paramvalue-prop-user": "Brukernavnet og ID-en til «from»- og «to»-revisjonene.", "apihelp-compare-paramvalue-prop-comment": "Kommentaren til «from»- og «to»-revisjonene.", + "apihelp-compare-paramvalue-prop-parsedcomment": "Den parsede kommentaren til «from»- og «to»-revisjonene.", "apihelp-compare-paramvalue-prop-size": "Størrelsen til «from»- og «to»-revisjonene.", "apihelp-compare-example-1": "Lag en diff mellom revisjon 1 og 2.", "apihelp-createaccount-summary": "Opprett en ny brukerkonto.", @@ -126,20 +128,39 @@ "apihelp-edit-param-watch": "Legg til siden til aktuell brukers overvåkningsliste.", "apihelp-edit-param-unwatch": "Fjern siden fra aktuell brukers overvåkningsliste.", "apihelp-edit-param-prependtext": "Legg til denne teksten til starten av siden. Overstyrer $1text.", + "apihelp-edit-param-undo": "Fjern (gjør om) denne revisjonen. Overstyrer $1text, $1prependtext og $1appendtext.", + "apihelp-edit-param-undoafter": "Fjern alle revisjoner fra $1undo til denne. Om den ikke er satt, fjern kun én revisjon.", "apihelp-edit-param-redirect": "Bestem omdirigeringer automatisk.", "apihelp-edit-param-contentformat": "Innholdsserialiseringsformat brukt for inndatateksten.", "apihelp-edit-param-contentmodel": "Det nye innholdets innholdsmodell.", + "apihelp-edit-param-token": "Nøkkelen bør alltid sendes som siste parameter, eller i det minste etter parameteren $1text.", "apihelp-edit-example-edit": "Rediger en side.", + "apihelp-edit-example-prepend": "Legg til __NOTOC__ i begynnelsen av en side.", + "apihelp-edit-example-undo": "Fjerner revisjon 13579–13585 med automatisk redigeringsforklaring.", "apihelp-emailuser-summary": "Send e-post til en bruker.", "apihelp-emailuser-param-target": "Bruker som det skal sendes e-post til.", "apihelp-emailuser-param-subject": "Emne.", "apihelp-emailuser-param-text": "E-post innhold.", "apihelp-emailuser-param-ccme": "Send en kopi av denne e-posten til meg.", + "apihelp-emailuser-example-email": "Send en epost til brukeren WikiSysop med teksten Content.", "apihelp-expandtemplates-summary": "Ekspanderer alle maler i wikitekst.", "apihelp-expandtemplates-param-title": "Sidetittel.", "apihelp-expandtemplates-param-text": "Wikitekst som skal konverteres.", + "apihelp-expandtemplates-param-revid": "Revisjons-ID, for {{REVISIONID}} og lignende variabler.", + "apihelp-expandtemplates-param-prop": "Hvilken informasjon som skal hentes.\n\nMerk at om ingen verdier velges vil resultatet inneholde wikiteksten, men utdataene vil komme i et utdatert format.", "apihelp-expandtemplates-paramvalue-prop-wikitext": "Den utvidede wikiteksten.", "apihelp-expandtemplates-paramvalue-prop-categories": "Kategorier som er tilstede i innputt som ikke representeres i utputt.", + "apihelp-expandtemplates-paramvalue-prop-properties": "Sideegenskaper definert av utvidede magiske ord i wikiteksten.", + "apihelp-expandtemplates-paramvalue-prop-volatile": "Hvorvidt utdataene er ustabile og ikke burde gjenbrukes andre steder på siden.", + "apihelp-expandtemplates-paramvalue-prop-ttl": "Maksimal tid som skal ha gått før mellomlagrede resultater skal ugyldiggjøres.", + "apihelp-expandtemplates-paramvalue-prop-jsconfigvars": "Gir JavaScript-konfigurasjonsvariabler som er spesifikke for siden.", + "apihelp-expandtemplates-paramvalue-prop-encodedjsconfigvars": "Gir JavaScript-konfigurasjonsvariabler som er spesifikke for siden som en JSON-streng.", + "apihelp-expandtemplates-param-includecomments": "Hvorvidt HTML-kommentarer skal inkluderes i utdataene.", + "apihelp-expandtemplates-example-simple": "Utvid wikiteksten {{Project:Sandbox}}.", + "apihelp-feedcontributions-summary": "Returnerer en mating med brukerbidrag.", + "apihelp-feedcontributions-param-feedformat": "Matingens format.", + "apihelp-feedcontributions-param-user": "Hvilke brukere det skal hentes bidrag av.", + "apihelp-feedcontributions-param-namespace": "Hvilke navnerom bidragene skal filtreres med.", "apihelp-feedcontributions-param-year": "Fra år (og tidligere).", "apihelp-feedcontributions-param-month": "Fra måned (og tidligere).", "apihelp-feedcontributions-param-tagfilter": "Filtrer bidrag som har disse merkene.", @@ -149,6 +170,7 @@ "apihelp-feedcontributions-param-hideminor": "Skjul mindre endringer.", "apihelp-feedcontributions-param-showsizediff": "Vis størrelsesforskjellen mellom revisjoner.", "apihelp-feedcontributions-example-simple": "Returner bidrag for brukeren Example.", + "apihelp-feedrecentchanges-summary": "Returnerer en mating med siste endringer.", "apihelp-feedrecentchanges-param-feedformat": "Matingens format.", "apihelp-feedrecentchanges-param-namespace": "Navnerom resultater skal begrenses til.", "apihelp-feedrecentchanges-param-invert": "Alle navnerom utenom det valgte.", @@ -172,6 +194,9 @@ "apihelp-feedrecentchanges-example-30days": "Vis siste endringer for 30 døgn.", "apihelp-feedwatchlist-summary": "Returnerer en overvåkningslistemating.", "apihelp-feedwatchlist-param-feedformat": "Matingens format.", + "apihelp-feedwatchlist-param-linktosections": "Lenk direkte til endrede seksjoner om mulig.", + "apihelp-feedwatchlist-example-default": "Vis matingen til overvåkningslisten.", + "apihelp-feedwatchlist-example-all6hrs": "Vis alle endringer på overvåkede sider de siste 6 timene.", "apihelp-filerevert-summary": "Tilbakestill en fil til en gammel versjon.", "apihelp-filerevert-param-filename": "Målfilnavn, uten prefikset File:.", "apihelp-filerevert-param-comment": "Opplastingskommentar.", @@ -197,6 +222,7 @@ "apihelp-import-extended-description": "Merk at HTTP POST må gjøres som filopplasting (altså med bruk av multipart/form-data) når man sender en fil for parameteren xml.", "apihelp-import-param-summary": "Sammendrag for importering av loggelement.", "apihelp-import-param-xml": "Opplastet XML-fil.", + "apihelp-import-param-assignknownusers": "Tildel redigeringer til lokale brukere der den navngitte brukeren finnes lokalt.", "apihelp-import-param-interwikisource": "For interwikiimport: wiki det skal importeres fra.", "apihelp-import-param-interwikipage": "For interwikiimport: side som skal importeres.", "apihelp-import-param-fullhistory": "For interwikiimport: importer hele historikken, ikke bare den nåværende versjonen.", @@ -205,6 +231,9 @@ "apihelp-import-param-rootpage": "Importer som underside av denne siden. Kan ikke brukes sammen med $1namespace.", "apihelp-import-param-tags": "Endringstagger som skal klistres på oppføringen i importloggen og nullrevisjonen til de importerte sidene.", "apihelp-import-example-import": "Importer [[meta:Help:ParserFunctions]] til navnerom 100 med full historikk.", + "apihelp-linkaccount-summary": "Lenk en konto fra en tredjepartsleverandør til den gjeldende brukeren.", + "apihelp-linkaccount-example-link": "Start prosessen med å lenke til en konto fra Example.", + "apihelp-login-summary": "Logg inn og få autentiseringsinformasjonskapsler.", "apihelp-login-param-name": "Brukernavn.", "apihelp-login-param-password": "Passord.", "apihelp-login-param-domain": "Domene (valgfritt).", @@ -249,22 +278,438 @@ "apihelp-parse-paramvalue-prop-categorieshtml": "Gir HTML-versjonen av kategoriene.", "apihelp-parse-paramvalue-prop-headitems": "Gir elementer som skal puttes i <head>-taggen til siden.", "apihelp-patrol-summary": "Patruljer en side eller revisjon.", + "apihelp-query+allfileusages-param-prop": "Hvilken informasjon som skal inkluderes:", "apihelp-query+allfileusages-paramvalue-prop-title": "Legger til filens tittel.", "apihelp-query+allfileusages-param-limit": "Hvor mange elementer som skal returneres totalt.", "apihelp-query+allfileusages-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+allfileusages-example-unique": "List opp unike filtitler.", + "apihelp-query+allfileusages-example-unique-generator": "Hent alle filtitler og marker de som mangler.", + "apihelp-query+allfileusages-example-generator": "Hent sider som inneholder filene.", + "apihelp-query+allimages-param-sort": "Egenskap det skal sorteres etter.", + "apihelp-query+allimages-param-dir": "Retningen det skal sorteres i.", + "apihelp-query+allimages-param-minsize": "Begrens til bilder med minst dette antallet byte.", + "apihelp-query+allimages-param-maxsize": "Begrens til bilder med minst dette antallet byte.", + "apihelp-query+allimages-param-sha1": "SHA1-hash for bildet. Setter til side $1sha1base36.", + "apihelp-query+allimages-param-sha1base36": "SHA1-hash av bildet i base 36 (brukes i MediaWiki).", + "apihelp-query+allimages-param-user": "Returner bare filer lastet opp av denne brukeren. Kan kun brukes med $1sort=timestamp. Kan ikke brukes med $1filterbots.", + "apihelp-query+allimages-param-filterbots": "Hvordan opplastinger av roboter skal filtreres. Kan kun brukes med $1sort=timestamp. Kan ikke brukes sammen med $1user.", + "apihelp-query+allimages-param-mime": "Hvilke MIME-typer det skal søkes etter, f.eks. image/jpeg.", + "apihelp-query+allimages-param-limit": "Hvor mange bilder som skal returneres totalt.", + "apihelp-query+allimages-example-B": "Viser en liste over filer som begynner med bokstaven B.", + "apihelp-query+allimages-example-recent": "Viser en liste over nylig opplastede filer på samme måte som [[Special:NewFiles]].", + "apihelp-query+allimages-example-mimetypes": "Viser en liste over filer med MIME-typen image/png eller image/gif", + "apihelp-query+allimages-example-generator": "Viser informasjon om 4 filer fra og med bokstaven T.", + "apihelp-query+alllinks-param-prefix": "Søk etter alle lenkede titler som begynner med denne verdien.", + "apihelp-query+alllinks-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+alllinks-paramvalue-prop-title": "Legger til tittelen til lenka.", + "apihelp-query+alllinks-param-limit": "Hvor mange elementer som skal returneres totalt.", + "apihelp-query+alllinks-param-dir": "Retningen det skal listes i.", + "apihelp-query+alllinks-example-unique": "List opp unike lenkede titler.", + "apihelp-query+alllinks-example-unique-generator": "Henter alle lenkede titler og markerer de som mangler.", + "apihelp-query+alllinks-example-generator": "Hent sider som inneholder lenkene.", + "apihelp-query+allmessages-summary": "Returnerer meldinger fra denne siden.", + "apihelp-query+allmessages-param-prop": "Hvilke egenskaper som skal hentes.", + "apihelp-query+allmessages-param-filter": "Returner kun meldinger med navn som inneholder denne strengen.", + "apihelp-query+allmessages-param-lang": "Returner meldinger på dette språket.", + "apihelp-query+allmessages-param-title": "Sidenavn å bruke som kontekst når meldingen tolkes (for alternativet $1enableparser).", + "apihelp-query+allmessages-param-prefix": "Returner meldinger med dette prefikset.", + "apihelp-query+allmessages-example-ipb": "Vis meldinger som starter med ipb-.", + "apihelp-query+allmessages-example-de": "Vis meldingene august og mainpage på tysk.", + "apihelp-query+allpages-param-filterredir": "Hvilke sider som skal listes opp.", + "apihelp-query+allpages-param-minsize": "Begrens til sider med dette antallet byte.", + "apihelp-query+allpages-param-maxsize": "Begrens til sider med maksimalt dette antallet byte.", + "apihelp-query+allpages-param-prtype": "Begrens til beskyttede sider.", + "apihelp-query+allpages-param-limit": "Hvor mange sider som skal returneres totalt.", + "apihelp-query+allpages-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+allpages-example-generator-revisions": "Vis innholdet til de to første ikke-omdirigeringssidene som begynner med Re.", + "apihelp-query+allredirects-summary": "Lister opp alle omdirigeringer til et navnerom.", + "apihelp-query+allredirects-param-prefix": "Søk etter alle målsider som begynner med denne verdien.", + "apihelp-query+allredirects-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+allredirects-paramvalue-prop-ids": "Legger til side-ID-en til den omdirigerende siden (kan ikke brukes med $1unique).", + "apihelp-query+allredirects-paramvalue-prop-title": "Legger til tittelen til omdirigeringen.", + "apihelp-query+allredirects-paramvalue-prop-fragment": "Legger til fragmentet fra omdirigeringen, om det er et (kan ikke brukes med $1unique).", + "apihelp-query+allredirects-paramvalue-prop-interwiki": "Legger til interwikiprefikset fra omdirigeringen, om det er et (kan ikke brukes med $1unique).", + "apihelp-query+allredirects-param-limit": "Hvor mange elementer som skal returneres totalt.", + "apihelp-query+allredirects-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+allredirects-example-B": "List opp målsider, inkludert de som mangler, med side-ID-ene de kommer fra, begynner på B.", + "apihelp-query+allredirects-example-unique": "List opp unike målsider.", + "apihelp-query+allredirects-example-unique-generator": "Henter alle målsider, markerer de som mangler.", + "apihelp-query+allredirects-example-generator": "Henter sider som inneholder omdirigeringene.", + "apihelp-query+allrevisions-summary": "List opp alle revisjoner.", + "apihelp-query+allrevisions-param-user": "List bare opp revisjoner av denne brukeren.", + "apihelp-query+allrevisions-param-excludeuser": "Ikke list opp revisjoner av denne brukeren.", + "apihelp-query+allrevisions-param-namespace": "List kun opp sider i dette navnerommet.", + "apihelp-query+allrevisions-param-generatetitles": "Når denne brukes som generator, generer titler i stedet for revisjons-ID-er.", + "apihelp-query+allrevisions-example-user": "List opp de siste 50 bidragene fra brukeren Example.", + "apihelp-query+allrevisions-example-ns-main": "List opp de første 50 revisjonene i hovednavnerommet.", + "apihelp-query+mystashedfiles-param-prop": "Hvilke egenskaper som skal hentes for filene.", + "apihelp-query+mystashedfiles-paramvalue-prop-size": "Hent filstørrelse og bildedimensjoner.", + "apihelp-query+mystashedfiles-paramvalue-prop-type": "Hent filas MIME-type og medietype.", + "apihelp-query+mystashedfiles-param-limit": "Hvor mange filer som skal hentes.", + "apihelp-query+alltransclusions-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+alltransclusions-paramvalue-prop-title": "Legger til tittelen på transklusjonen.", + "apihelp-query+allusers-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+allusers-paramvalue-prop-blockinfo": "Legger til informasjon om en gjeldende blokkering av brukeren.", + "apihelp-query+allusers-paramvalue-prop-groups": "Lister opp grupper brukeren er i. Dette bruker flere tjenerressurser og kan returnere færre resultater enn grensa.", + "apihelp-query+allusers-paramvalue-prop-implicitgroups": "Lister opp alle grupper brukeren automatisk er med i.", + "apihelp-query+allusers-paramvalue-prop-rights": "Lister opp rettigheter brukeren har.", + "apihelp-query+allusers-paramvalue-prop-editcount": "Legger til redigeringstelleren til brukeren.", + "apihelp-query+allusers-paramvalue-prop-registration": "Legger til tidsstempelet for når brukeren ble registrert, om tilgjengelig (kan være blank).", + "apihelp-query+allusers-paramvalue-prop-centralids": "Legger til sentrale ID-er og tilkoblingsstatus for brukeren.", + "apihelp-query+allusers-param-limit": "Hvor mange brukernavn som skal returneres.", + "apihelp-query+allusers-param-witheditsonly": "List bare opp brukere som har gjort redigeringer.", + "apihelp-query+allusers-param-activeusers": "List bare opp brukere som har vært aktiv {{PLURAL:$1|den siste dagene|de siste $1 dagene}}.", + "apihelp-query+allusers-param-attachedwiki": "Med $1prop, indiker også hvorvidt brukeren er tilkoblet med wikien som identifiseres av denne ID-en.", + "apihelp-query+allusers-example-Y": "List opp brukere fra og med Y.", + "apihelp-query+authmanagerinfo-summary": "Hent informasjon om den gjeldende autentiseringsstatusen.", + "apihelp-query+backlinks-summary": "Finn alle sider som lenker til den gitte siden.", + "apihelp-query+backlinks-param-title": "Tittel det skal søkes etter. Kan ikke brukes sammen med $1pageid.", + "apihelp-query+backlinks-param-pageid": "Side-ID det skal søkes etter. Kan ikke brukes sammen med $1title.", + "apihelp-query+backlinks-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+categorymembers-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Modus|Moduser}}: $2", + "apihelp-query+exturlusage-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+iwbacklinks-param-limit": "Hvor mange sider som skal returneres totalt.", + "apihelp-query+iwbacklinks-param-prop": "Hvilke egenskaper som skal hentes:", + "apihelp-query+iwbacklinks-paramvalue-prop-iwprefix": "Legger til prefikset til interwikien.", + "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Legger til tittelen til interwikien.", + "apihelp-query+iwbacklinks-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+iwbacklinks-example-simple": "Hent sider som lenker til [[wikibooks:Test]].", + "apihelp-query+iwbacklinks-example-generator": "Hent informasjon om sider som lenker til [[wikibooks:Test]].", + "apihelp-query+iwlinks-summary": "Returnerer alle interwikilenker fra de gitte sidene.", + "apihelp-query+iwlinks-param-url": "Hvorvidt den fulle URL-en skal hentes (kan ikke brukes med $1prop).", + "apihelp-query+iwlinks-param-prop": "Hvilke ekstra egenskaper som skal hentes for hver språklenke:", + "apihelp-query+iwlinks-paramvalue-prop-url": "Legger til den fulle URL-en.", + "apihelp-query+iwlinks-param-limit": "Hvor mange interikilenker som skal returneres.", + "apihelp-query+iwlinks-param-prefix": "Returner bare interwikilenker med dette prefikset.", + "apihelp-query+iwlinks-param-title": "Interwikilenker det skal søkes etter. Må brukes med $1prefix.", + "apihelp-query+iwlinks-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+iwlinks-example-simple": "Hent interwikilenker fra sida Main Page.", + "apihelp-query+langbacklinks-summary": "Finn alle sider som lenker til den gitte språklenka.", + "apihelp-query+langbacklinks-param-lang": "Språket for språklenka.", + "apihelp-query+langbacklinks-param-title": "Språklenke det skal søkes etter. Må brukes med $1lang.", + "apihelp-query+langbacklinks-param-limit": "Hvor mange sider som skal returneres totalt.", + "apihelp-query+langbacklinks-param-prop": "Hvilke egenskaper som skal hentes:", + "apihelp-query+langbacklinks-paramvalue-prop-lllang": "Legger til språkkoden til språklenka.", + "apihelp-query+langbacklinks-paramvalue-prop-lltitle": "Legger til tittelen til språklenka.", + "apihelp-query+langbacklinks-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+langbacklinks-example-simple": "Hent sider som lenker til [[:fr:Test]].", + "apihelp-query+langbacklinks-example-generator": "Hent informasjon om sider som lenker til [[:fr:Test]].", + "apihelp-query+langlinks-summary": "Returnerer alle språklenker fra de gitte sidene.", + "apihelp-query+langlinks-param-limit": "Hvor mange språklenker som skal returneres.", + "apihelp-query+langlinks-param-url": "Hvorvidt den fulle URL-en skal hentes (kan ikke brukes med $1prop).", + "apihelp-query+langlinks-param-prop": "Hvilke ekstra egenskaper som skal hentes for hver språklenke:", + "apihelp-query+langlinks-paramvalue-prop-url": "Legger til den fulle URL-en.", + "apihelp-query+langlinks-param-lang": "Returner bare språklenker med denne språkkoden.", + "apihelp-query+langlinks-param-title": "Lenke det skal søkes etter. Må brukes med $1lang.", + "apihelp-query+langlinks-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+langlinks-param-inlanguagecode": "Språkkode for oversatte språknavn.", + "apihelp-query+langlinks-example-simple": "Hent språklenker fra siden Main Page.", + "apihelp-query+links-summary": "Returnerer alle lenker fra de gitte sidene.", + "apihelp-query+links-param-namespace": "Viser lenker kun i disse navnerommene.", + "apihelp-query+links-param-limit": "Hvor mange lenker som skal returneres.", + "apihelp-query+links-param-titles": "List bare opp lenker til disse titlene. Nyttig for å sjekke om en viss side lenker til en annen viss side.", + "apihelp-query+links-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+links-example-simple": "Hent lenker fra sida Main Page", + "apihelp-query+links-example-generator": "Hent informasjon om lenkesidene på sida Main Page.", + "apihelp-query+links-example-namespaces": "Hent lenker fra sida Main Page i navnerommene {{ns:user}} og {{ns:template}}.", + "apihelp-query+linkshere-summary": "Finn alle sider som lenker til de gitte sidene.", + "apihelp-query+linkshere-param-prop": "Hvilke egenskaper som skal hentes:", + "apihelp-query+linkshere-paramvalue-prop-pageid": "Side-ID for hver side.", + "apihelp-query+linkshere-paramvalue-prop-title": "Tittelen til hver side.", + "apihelp-query+linkshere-paramvalue-prop-redirect": "Marker om siden er omdirigering.", + "apihelp-query+linkshere-param-namespace": "Inkluder bare sider i disse navnerommene.", + "apihelp-query+linkshere-param-limit": "Hvor mange som skal returneres.", + "apihelp-query+linkshere-param-show": "Vis bare elementer som møter disse kriteriene:\n;redirect:Vis bare omdirigeringer.\n;!redirect:Vis bare ikke-omdirigeringer.", + "apihelp-query+linkshere-example-simple": "Hent ei liste over sider som lenker til [[Main Page]].", + "apihelp-query+linkshere-example-generator": "Hent informasjon om sider som lenker til [[Main Page]].", + "apihelp-query+logevents-summary": "Hent oppføringer fra logger.", + "apihelp-query+logevents-param-prop": "Hvilke egenskaper som skal hentes:", + "apihelp-query+logevents-paramvalue-prop-ids": "Legger til ID-en til loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-title": "Legger til tittelen på siden i loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-type": "Legger til typen loggoppføring.", + "apihelp-query+logevents-paramvalue-prop-user": "Legger til brukeren som er ansvarlig for loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-userid": "Legger til bruker-ID-en som var ansvarlig for loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-timestamp": "Legger til tidsstempelet for loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-comment": "Legger til kommentaren til loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-parsedcomment": "Legger til den parsede kommentaren til loggoppføringen.", + "apihelp-query+logevents-paramvalue-prop-details": "Lister opp ekstra detaljer om loggoppføringen.", + "apihelp-query+logevents-param-type": "Filtrer loggoppføringer til kun denne typen.", + "apihelp-query+logevents-param-action": "Filtrer logghandlinger til kun denne handlingen. Overstyrer $1type. I listen over mulige verdier kan verdier med jokertegnet stjerne, som action/* ha forskjellige strenger etter skråstreken.", + "apihelp-query+logevents-param-user": "Filtrer oppføringer til de som er gjort av den gitte brukeren.", + "apihelp-query+logevents-param-title": "Filtrer oppføringer til de som er relatert til ei side.", + "apihelp-query+logevents-param-namespace": "Filtrer oppføringer til de i det gitte navnerommet.", + "apihelp-query+logevents-param-prefix": "Filtrer oppføringer som starter med dette prefikset.", + "apihelp-query+logevents-param-tag": "List bare opp oppføringer som er tagget med denne taggen.", + "apihelp-query+logevents-param-limit": "Hvor mange oppføringer som skal returneres.", + "apihelp-query+logevents-example-simple": "List opp nylige loggoppføringer.", + "apihelp-query+pagepropnames-summary": "List opp alle sideegenskapsnavn i bruk på wikien.", + "apihelp-query+pagepropnames-param-limit": "Maksimalt antall navn som skal returneres.", + "apihelp-query+pagepropnames-example-simple": "Hent de 10 første egenskapsnavnene.", + "apihelp-query+pageprops-summary": "Hent diverse sideegenskaper definert i sideinnholdet.", + "apihelp-query+pageprops-param-prop": "List kun opp disse sideegenskapene ([[Special:ApiHelp/query+pagepropnames|action=query&list=pagepropnames]] returnerer sideegenskapsnavn i bruk). Nyttig for å sjekke om sider bruker en viss sideegenskap.", + "apihelp-query+pageprops-example-simple": "Hent egenskaper for sidene Main Page og MediaWiki.", + "apihelp-query+pageswithprop-summary": "Lister opp alle sider med en gitt sideegenskap.", + "apihelp-query+pageswithprop-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+pageswithprop-paramvalue-prop-ids": "Legger til side-ID-en.", + "apihelp-query+pageswithprop-paramvalue-prop-title": "Legger til tittel- og navneroms-ID-en til sida.", + "apihelp-query+pageswithprop-paramvalue-prop-value": "Legger til verdien til sideegenskapen.", + "apihelp-query+pageswithprop-param-limit": "Maksimalt antall sider som skal returneres.", + "apihelp-query+pageswithprop-param-dir": "Hvilken retning det skal sorteres i.", + "apihelp-query+pageswithprop-example-simple": "List opp de første 10 sidene som bruker {{DISPLAYTITLE:}}.", + "apihelp-query+pageswithprop-example-generator": "Hent ekstra informasjon om de 10 første sidene som bruker __NOTOC__.", + "apihelp-query+prefixsearch-summary": "Utfør et prefikssøk for sidetitler.", + "apihelp-query+prefixsearch-param-search": "Søkestreng.", + "apihelp-query+prefixsearch-param-namespace": "Navnerom det skal søkes i.", + "apihelp-query+prefixsearch-param-limit": "Maksimalt antall resultater som skal returneres.", + "apihelp-query+prefixsearch-param-offset": "Antall resultater som skal hoppes over.", + "apihelp-query+prefixsearch-example-simple": "Søk etter sidetitler som begynner med meaning.", + "apihelp-query+prefixsearch-param-profile": "Søkeprofil som skal brukes.", + "apihelp-query+protectedtitles-summary": "List opp alle titler som er beskyttet fra opprettelse.", + "apihelp-query+protectedtitles-param-namespace": "List kun opp titler i disse navnerommene.", + "apihelp-query+protectedtitles-param-level": "List kun opp titler med disse beskyttelsesnivåene.", + "apihelp-query+protectedtitles-param-limit": "Hvor mange sider som skal returneres totalt.", + "apihelp-query+protectedtitles-param-prop": "Hvilke egenskaper som skal hentes:", + "apihelp-query+querypage-param-limit": "Antall resultater som skal returneres.", + "apihelp-query+querypage-example-ancientpages": "Returner resultater fra [[Special:Ancientpages]].", + "apihelp-query+random-summary": "Hent et sett av tilfeldige sider.", + "apihelp-query+userinfo-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+users-param-prop": "Hvilken informasjon som skal inkluderes:", + "apihelp-query+watchlist-param-type": "Hvilke typer endringer som skal vises:", + "apihelp-query+watchlist-paramvalue-type-edit": "Vanlige sideredigeringer.", + "apihelp-query+watchlist-paramvalue-type-external": "Eksterne endringer.", + "apihelp-query+watchlist-paramvalue-type-new": "Sideopprettelser", + "apihelp-query+watchlist-paramvalue-type-log": "Loggoppføringer.", + "apihelp-query+watchlist-paramvalue-type-categorize": "Endringer i kategorimedlemskap.", + "apihelp-query+watchlist-param-owner": "Brukes sammen med $1token for å få tilgang til en annen brukers overvåkningsliste.", + "apihelp-query+watchlist-param-token": "En sikkerhetsnøkkel (tilgjengelig i brukerens [[Special:Preferences#mw-prefsection-watchlist|innstillinger]]) for å tillate tilgang til en annen brukers overvåkningsliste.", + "apihelp-query+watchlistraw-param-namespace": "List kun opp sider i de gitte navnerommene.", + "apihelp-query+watchlistraw-param-limit": "Hvor mange resultater som skal returneres totalt per forespørsel.", + "apihelp-query+watchlistraw-param-prop": "Hvilke ekstra egenskaper som skal hentes:", + "apihelp-query+watchlistraw-paramvalue-prop-changed": "Legger til tidsstempel for når brukeren sist ble varslet om redigeringen.", + "apihelp-query+watchlistraw-param-show": "List kun opp elementer som tilfredsstiller disse kriteriene.", + "apihelp-query+watchlistraw-param-owner": "Brukes sammen med $1token for å få tilgang til en annen brukers overvåkningsliste.", + "apihelp-query+watchlistraw-param-token": "En sikkerhetsnøkkel (tilgjengelig i brukerens [[Special:Preferences#mw-prefsection-watchlist|innstillinger]]) for å tillate tilgang til en annen brukers overvåkningsliste.", + "apihelp-query+watchlistraw-param-dir": "Retningen det skal listes opp i.", + "apihelp-query+watchlistraw-example-simple": "List opp sider på den gjeldende brukerens overvåkningsliste.", + "apihelp-query+watchlistraw-example-generator": "Hent sideinfo for sider på den gjeldende brukerens overvåkningsliste.", + "apihelp-removeauthenticationdata-summary": "Fjern autentiseringsdata for den gjeldende brukeren.", + "apihelp-removeauthenticationdata-example-simple": "Forsøk å fjerne den gjeldende brukerens data for FooAuthenticationRequest.", + "apihelp-resetpassword-summary": "Send en epost for nullstilling av passord til en bruker.", + "apihelp-revisiondelete-summary": "Slett og gjenopprett revisjoner.", + "apihelp-revisiondelete-param-type": "Type revisjonssletting som utføres.", + "apihelp-revisiondelete-param-target": "Sidetittelen for revisjonssletting, om det kreves for typen.", + "apihelp-revisiondelete-param-ids": "Identifikatorer for revisjonene som skal slettes.", + "apihelp-revisiondelete-param-hide": "Hva som skal skjules for hver revisjon.", + "apihelp-revisiondelete-param-show": "Hva som skal vises for hver revisjon.", + "apihelp-revisiondelete-param-suppress": "Hvorvidt data skal skjules for administratorer i tillegg til andre.", + "apihelp-revisiondelete-param-reason": "Årsak for slettingen eller gjenopprettingen.", + "apihelp-revisiondelete-param-tags": "Tagger som skal brukes på oppføringen i sletteloggen.", + "apihelp-revisiondelete-example-revision": "Skjul innhold for revisjon 12345 på siden Main Page.", + "apihelp-revisiondelete-example-log": "Skjul alle data om loggoppføringen 67890 med årsak BLP violation.", + "apihelp-rollback-summary": "Omgjør den siste redigeringen på siden.", + "apihelp-rollback-extended-description": "Om den siste brukeren som redigerte siden gjorde flere redigeringer på rad, vil alle disse redigeringene fjernes.", + "apihelp-rollback-param-title": "Tittelen på siden som skal tilbakestilles. Kan ikke brukes sammen med $1pageid.", + "apihelp-rollback-param-pageid": "Side-ID for siden som skal tilbakestilles. Kan ikke brukes sammen med $1title.", + "apihelp-rollback-param-tags": "Tagger som skal påføres tilbakestillingen.", + "apihelp-rollback-param-user": "Navnet til brukeren hvis redigeringer skal tilbakestilles.", + "apihelp-rollback-param-summary": "Egendefinert redigeringssammendrag. Om denne er tom vil standardsammendraget brukes.", + "apihelp-rollback-param-markbot": "Merk de tilbakestilte redigeringene og tilbakestillingen som botredigeringer.", + "apihelp-rollback-example-simple": "Tilbakestill de siste redigeringene på siden Main Page av brukeren Example.", + "apihelp-rollback-example-summary": "Tilbakestill de siste redigeringene på siden Main Page av IP-adressen 192.0.2.5 med sammendraget Reverting vandalism, og merk disse redigeringene samt tilbakestillingen som botredigeringer.", + "apihelp-setnotificationtimestamp-summary": "Oppdater varselstidsstempelet for overvåkede sider.", + "apihelp-setpagelanguage-summary": "Endre språket til en side.", + "apihelp-setpagelanguage-extended-description-disabled": "Endring av språket til en side tillates ikke på denne wikien.\n\nSlå på [[mw:Special:MyLanguage/Manual:$wgPageLanguageUseDB|$wgPageLanguageUseDB]] for å bruke denne handlingen.", + "apihelp-setpagelanguage-param-title": "Tittelen på siden som skal endre språk. Kan ikke brukes sammen med $1pageid.", + "apihelp-setpagelanguage-param-pageid": "Side-ID til siden du ønsker å endre språk på. Kan ikke brukes sammen med $1title.", + "apihelp-setpagelanguage-param-lang": "Språkkoden for språket du ønsker å endre siden til. Bruk default for å tilbakestille siden til wikiens standardspråk.", + "apihelp-setpagelanguage-param-reason": "Årsak for endringen.", + "apihelp-setpagelanguage-param-tags": "Endringstagger som skal påføres loggoppføringen som oppstår på grunn av denne handlingen.", + "apihelp-setpagelanguage-example-language": "Endre språket til Main Page til baskisk.", + "apihelp-setpagelanguage-example-default": "Endre språket til siden med ID 123 til wikiens standardspråk.", + "apihelp-stashedit-param-title": "Tittelen på siden som redigeres.", + "apihelp-stashedit-param-section": "Seksjonsnummer. 0 for toppseksjonen, new for en ny seksjon.", + "apihelp-stashedit-param-sectiontitle": "Tittelen til en ny seksjon.", + "apihelp-stashedit-param-text": "Sideinnhold.", + "apihelp-stashedit-param-contentmodel": "Innholdsmodellen til det nye innholdet.", + "apihelp-stashedit-param-baserevid": "Revisjons-ID-en til grunnrevisjonen.", + "apihelp-stashedit-param-summary": "Endringssammendrag.", + "apihelp-tag-summary": "Legg til eller fjern endringstagger fra individuelle revisjoner eller loggoppføringer.", + "apihelp-tag-param-revid": "Én eller flere revisjons-ID-er taggen skal legges til på eller fjernes fra.", + "apihelp-tag-param-logid": "Én eller flere loggoppførings-ID-er taggen skal legges til på eller fjernes fra.", + "apihelp-tag-param-add": "Tagger som skal legges til. Kun manuelt definerte tagger kan legges til.", + "apihelp-tag-param-remove": "Tagger som skal fjernes. Kun tagger som er enten manuelt definert eller helt udefinerte kan fjernes.", + "apihelp-tag-param-reason": "Årsak for endringen.", + "apihelp-tag-param-tags": "Tagger som skal påføres loggoppføringen som vil oppstå på grunn av denne handlingen.", + "apihelp-tag-example-rev": "Legg til taggen vandalism til revisjons-ID-en 123 uten å oppgi årsak", + "apihelp-tag-example-log": "Fjern taggen spam fra loggoppførings-ID-en 123 med årsaken Wrongly applied", + "apihelp-tokens-extended-description": "Denne modulen er foreldet til fordel for [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].", + "apihelp-tokens-example-edit": "Hent en redigeringsnøkkel (standard).", + "apihelp-tokens-example-emailmove": "Hent en epostnøkkel og en flyttingsnøkkel.", + "apihelp-unblock-summary": "Avblokker en bruker.", + "apihelp-userrights-param-user": "Brukernavn.", + "apihelp-userrights-param-userid": "Bruker-ID.", + "apihelp-userrights-param-remove": "Fjern brukeren fra disse gruppene.", + "apihelp-userrights-param-reason": "Årsak for endringen.", + "apihelp-userrights-param-tags": "Endringstagger som skal påføres oppføringen i brukerettighetsloggen.", + "apihelp-userrights-example-user": "Legg til brukeren FooBot i gruppa bot, og fjern den fra gruppene sysop og bureaucrat.", + "apihelp-userrights-example-userid": "Legg til brukeren med ID 123 til gruppa bot, og fjern den fra gruppene sysop og bureaucrat.", + "apihelp-userrights-example-expiry": "Legg til brukeren SometimeSysop til gruppa sysop midlertidig i én måned.", + "apihelp-validatepassword-summary": "Valider et passord mot wikiens passordkrav.", + "apihelp-validatepassword-param-password": "Passord som skal valideres.", + "apihelp-watch-example-watch": "Overvåk siden Main Page.", + "apihelp-watch-example-unwatch": "Avslutt overvåking av siden Main Page.", + "apihelp-format-example-generic": "Returner spørringsresultatet i formatet $1.", "apihelp-json-summary": "Resultatdata i JSON-format.", "apihelp-none-summary": "Ingen resultat.", + "api-help-main-header": "Hovedmodul", + "api-help-undocumented-module": "Ingen dokumentasjon for modulen $1.", + "api-help-flag-deprecated": "Modulen er foreldet.", + "api-help-flag-internal": "Denne modulen er intern eller ustabel. Hvordan den fungerer kan forandre seg uten forvarsel.", "api-help-flag-readrights": "Denne modulen krever lesetilgang.", "api-help-flag-writerights": "Denne modulen krever skrivetilgang.", "api-help-flag-mustbeposted": "Denne modulen aksepterer bare POST forespørsler.", "api-help-flag-generator": "Denne modulen kan brukes som en generator.", + "api-help-source": "Kilde: $1", + "api-help-source-unknown": "Kilde: ukjent", + "api-help-license": "Lisens: [[$1|$2]]", + "api-help-license-noname": "Lisens: [[$1|Se lenke]]", + "api-help-license-unknown": "Lisens: ukjent", "api-help-parameters": "{{PLURAL:$1|Parameter|Parametre}}:", "api-help-param-deprecated": "Utgått.", "api-help-param-required": "Denne parameteren er påkrevd.", + "api-help-datatypes-header": "Datatyper", + "api-help-param-type-limit": "Type: heltall eller max", + "api-help-param-type-integer": "Type: {{PLURAL:$1|1=heltall|2=liste over heltall}}", + "api-help-param-type-boolean": "Type: boolsk verdi ([[Special:ApiHelp/main#main/datatypes|detaljer]])", + "api-help-param-type-timestamp": "Type: {{PLURAL:$1|1=tidsstempel|2=liste over tidsstempler}} ([[Special:ApiHelp/main#main/datatypes|tillatte formater]])", + "api-help-param-type-user": "Type: {{PLURAL:$1|1=brukernavn|2=liste over brukernavn}}", + "api-help-param-list": "{{PLURAL:$1|1=Én av følgende verdier|2=Verdier (separer med {{!}} eller [[Special:ApiHelp/main#main/datatypes|alternativ]])}}: $2", + "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Må være tom|Kan være tom, eller $2}}", + "api-help-param-limit": "Ikke mer enn $1 er tillatt.", + "api-help-param-limit2": "Ikke mer enn $1 ($2 for botter) er tillatt.", + "api-help-param-integer-min": "{{PLURAL:$1|Verdien|Verdiene}} må ikke være mindre enn $2.", + "api-help-param-integer-max": "{{PLURAL:$1|Verdien|Verdiene}} må ikke være større enn $3.", + "api-help-param-integer-minmax": "{{PLURAL:$1|Verdien|Verdiene}} må være mellom $2 og $3.", + "api-help-param-multi-separate": "Separer verdier med | eller [[Special:ApiHelp/main#main/datatypes|alternativ]].", + "api-help-param-multi-max": "Maksimalt antall verdier er {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} for botter)", + "api-help-param-multi-max-simple": "Maksimalt antall verdier er {{PLURAL:$1|$1}}.", + "api-help-param-multi-all": "For å angi alle verdier, bruk $1.", + "api-help-param-default": "Standard: $1", + "api-help-param-default-empty": "Standard: (tom)", + "api-help-param-continue": "Når flere resultater er tilgjengelige, bruk denne for å fortsette.", + "api-help-param-no-description": "(ingen beskrivelse)", + "api-help-param-maxbytes": "Kan ikke være lengre enn $1 {{PLURAL:$1|byte}}.", + "api-help-param-maxchars": "Kan ikke være lengre enn $1 {{PLURAL:$1|tegn}}.", + "api-help-examples": "{{PLURAL:$1|Eksempel|Eksempler}}:", + "api-help-permissions": "{{PLURAL:$1|Tillatelse|Tillatelser}}:", + "api-help-permissions-granted-to": "{{PLURAL:$1|Gitt til}}: $2", + "api-help-open-in-apisandbox": "[åpne i sandkasse]", + "apierror-changeauth-norequest": "Kunne ikke opprette endringsforespørsel.", + "apierror-contentserializationexception": "Innholdsserialisering feliet: $1", + "apierror-contenttoobig": "Innholdet du oppga overskrider artikkelstørrelsesgrensen på $1 {{PLURAL:$1|kilobyte}}.", + "apierror-copyuploadbaddomain": "Opplasting via URL tillates ikke fra dette domenet.", + "apierror-copyuploadbadurl": "Opplasting tillates ikke fra denne URL-en.", + "apierror-create-titleexists": "Eksisterende titler kan ikke beskyttes med create.", + "apierror-csp-report": "Feil under prosessering av CSP-rapport: $1.", + "apierror-databaseerror": "[$1] Databasespørringsfeil.", + "apierror-deletedrevs-param-not-1-2": "Parameteren $1 kan ikke brukes i modus 1 eller 2.", + "apierror-deletedrevs-param-not-3": "Parameteren $1 kan ikke brukes i modus 3.", + "apierror-emptynewsection": "Oppretting av tomme nye seksjoner er ikke mulig.", + "apierror-emptypage": "Oppretting av nye, tomme sider tillates ikke.", + "apierror-exceptioncaught": "[$1] Unntak fanget: $2", + "apierror-filedoesnotexist": "Fila fins ikke.", + "apierror-fileexists-sharedrepo-perm": "Målfila fins på et delt fillager. Bruk parameteren ignorewarnings for å overstyre den.", + "apierror-filenopath": "Kan ikke hente lokal filsti.", + "apierror-filetypecannotberotated": "Filtypen kan ikke roteres.", + "apierror-formatphp": "Denne responsen kan ikke representeres med format=php. Se https://phabricator.wikimedia.org/T68776.", + "apierror-imageusage-badtitle": "Tittelen for $1 må være ei fil.", + "apierror-import-unknownerror": "Ukjent feil under importering: $1.", + "apierror-integeroutofrange-abovebotmax": "$1 kan ikke være over $2 (satt til $3) for botter eller administratorer.", + "apierror-integeroutofrange-abovemax": "$1 kan ikke være over $2 (satt til $3) for brukere.", + "apierror-integeroutofrange-belowminimum": "$1 kan ikke være mindre enn $2 (satt til $3).", + "apierror-invalidcategory": "Kategorinavnet du skrev inn er ikke gyldig.", + "apierror-invalidexpiry": "Ugyldig utløpstid «$1».", + "apierror-invalid-file-key": "Ikke en gyldig filnøkkel.", + "apierror-invalidlang": "Ugyldig språkkode for parameteren $1.", + "apierror-invalidoldimage": "Parameteren oldimage har et ugyldig format.", + "apierror-invalidparammix-cannotusewith": "Parameteren $1 kan ikke brukes med $2.", + "apierror-invalidparammix-mustusewith": "Parameteren $1 kan ikke brukes med $2.", + "apierror-invalidparammix-parse-new-section": "section=new kan ikke kombineres med parameterne oldid, pageid eller page. Bruk title og text.", + "apierror-invalidparammix": "{{PLURAL:$2|Parameterne}} $1 kan ikke brukes sammen.", + "apierror-invalidsection": "Parameteren section må være en gyldig seksjons-ID eller new.", + "apierror-invalidtitle": "Ugyldig tittel «$1».", + "apierror-invalidurlparam": "Ugyldig verdi for $1urlparam ($2=$3).", + "apierror-invaliduser": "Ugyldig brukernavn «$1».", + "apierror-invaliduserid": "Bruker-ID-en $1 er ikke gyldig.", + "apierror-maxbytes": "Parameteren $1 kan ikke være lengre enn $2 {{PLURAL:$2|byte}}", + "apierror-maxchars": "Parameteren $1 kan ikke være lengre enn $2 {{PLURAL:$2|tegn}}", + "apierror-maxlag-generic": "Venter på en databasetjener: Henger etter med {{PLURAL:$1|ett sekund|$1 sekunder}}.", + "apierror-maxlag": "Venter på $2: Henger etter med {{PLURAL:$1|ett sekund|$1 sekunder}}.", + "apierror-mimesearchdisabled": "MIME-søk er slått av i Miser-modus.", + "apierror-missingcontent-pageid": "Manglende innhold for side-ID $1.", + "apierror-missingcontent-revid": "Manglende innhold for revisjons-ID $1.", + "apierror-missingparam-at-least-one-of": "{{PLURAL:$1|Parameteren|Minst én av parameterne}} $1 er påkrevd.", + "apierror-missingparam-one-of": "{{PLURAL:$2|Parameteren|Én av parameterne}} $1 er påkrevd.", + "apierror-missingparam": "Parameteren $1 må være satt.", + "apierror-missingrev-pageid": "Ingen gjeldende revisjon av side-ID $1.", + "apierror-missingrev-title": "Ingen gjeldende revisjon av tittelen $1.", + "apierror-missingtitle-createonly": "Manglende titler kan bare beskyttes med create.", + "apierror-missingtitle": "Siden du oppga fins ikke.", + "apierror-missingtitle-byname": "Siden $1 fins ikke.", + "apierror-moduledisabled": "Modulen $1 har blitt slått av.", + "apierror-multival-only-one-of": "{{PLURAL:$3|Kun|Kun én av} $2 tillates for parameteren $3.", "apierror-multival-only-one": "Bare én verdi er tillatt for parameteret $1.", + "apierror-multpages": "$1 kan kun brukes med én enkel side.", + "apierror-mustbeloggedin-changeauth": "Du må være logget inn for å endre autentiseringsdata.", + "apierror-mustbeloggedin-generic": "Du må være logget inn.", + "apierror-mustbeloggedin-linkaccounts": "Du må være logget inn for å lenke kontoer.", + "apierror-mustbeloggedin-removeauth": "Du må være logget inn for å fjerne autentiseringsdata.", "apierror-mustbeloggedin": "Du må være logget inn for å $1.", + "apierror-mustbeposted": "Modulen $1 krever en POST-forespørsel.", + "apierror-mustpostparams": "Følgende {{PLURAL:$2|parameter|parametre}} ble funnet i spørringsstrengen, men må være i POST-innholdet: $1.", + "apierror-noapiwrite": "Redigering av denne wikien via API er slått av. Sjekk at utsagnet $wgEnableWriteAPI=true; inkluderes i wikiens LocalSettings.php-fil.", + "apierror-nochanges": "Ingen endringer ble forespurt.", + "apierror-no-direct-editing": "Direkte redigering via API-et støttes ikke for innholdsmodellen $1 som brukes av $2.", + "apierror-noedit-anon": "Anonyme brukere kan ikke redigere sider.", + "apierror-noedit": "Du har ikke tillatelse til å redigere sider.", + "apierror-noimageredirect-anon": "Anonyme brukere kan ikke opprette bildeomdirigeringer.", + "apierror-noimageredirect": "Du har ikke tillatelse til å opprette bildeomdirigeringer.", + "apierror-nosuchlogid": "Det er ingen loggoppføring med ID $1.", + "apierror-nosuchpageid": "Det er ingen side med ID $1.", + "apierror-nosuchrcid": "Det er ingen nylig endring med ID $1.", + "apierror-nosuchrevid": "Det er ingen revisjon med ID $1.", + "apierror-nosuchsection": "Det er ingen seksjon $1.", + "apierror-nosuchsection-what": "Det er ingen seksjon $1 i $2.", + "apierror-nosuchuserid": "Det er ingen bruker med ID $1.", + "apierror-notarget": "Du har ikke angitt et gyldig mål for denne handlingen.", + "apierror-notpatrollable": "Revisjonen r$1 kan ikke patruljeres fordi den er for gammel.", + "apierror-nouploadmodule": "Ingen opplastingsmodul satt.", "apierror-offline": "Kunne ikke fortsette på grunn av tilkoblingsproblemer. Sjekk at internettforbindelsen din virker og prøv igjen.", + "apierror-opensearch-json-warnings": "Advarsel kan ikke representeres OpenSearch JSON-format.", + "apierror-pagecannotexist": "Navnerommet tillater ikke faktiske sider.", + "apierror-pagedeleted": "Siden har blitt slettet siden du hentet tidsstempelet dens.", + "apierror-pagelang-disabled": "Endring av sidespråk tillates ikke på denne wikien.", + "apierror-paramempty": "Parameteren $1 kan ikke være tom.", + "apierror-parsetree-notwikitext": "prop=parsetree støttes kut for wikitekstinnhold.", + "apierror-parsetree-notwikitext-title": "prop=parsetree støttes kun for wikitekstinnhold. $1 bruker innholdsmodellen $2.", + "apierror-pastexpiry": "Utløpstiden «$1» er i fortiden.", + "apierror-permissiondenied": "Du har ikke tillatelse til å $1.", "apierror-permissiondenied-generic": "Tilgang nektet.", + "apierror-permissiondenied-patrolflag": "Du trenger rettigheten patrol eller patrolmarks for å be om patruljert-flagget.", + "apierror-permissiondenied-unblock": "Du har ikke tillatelse til å avblokkere brukere.", + "apierror-prefixsearchdisabled": "Prefikssøk er slått av i Miser-modus.", + "apierror-protect-invalidaction": "Ugyldig beskyttelsestype «$1».", + "apierror-protect-invalidlevel": "Ugyldig beskyttelsesnivå «$1».", + "apierror-readapidenied": "Du må ha lesetilgang for å bruke denne modulen.", + "apierror-readonly": "Wikien er for tiden skrivebeskyttet.", + "apierror-revwrongpage": "r$1 er ikke en revisjon av $2.", + "apierror-searchdisabled": "$1-søk er slått av.", + "apierror-sectionreplacefailed": "Kunne ikke flette oppdatert seksjon.", + "apierror-sectionsnotsupported": "Seksjoner støttes ikke for innholdsmodellen $1.", + "apierror-sectionsnotsupported-what": "Seksjoner støttes ikke av $1.", + "apierror-siteinfo-includealldenied": "Kan ikke vise alle tjenernes info med mindre $wgShowHostNames er sann.", + "apierror-sizediffdisabled": "Størrelsesforskjell er slått av i Miser-modus.", "apierror-timeout": "Tjeneren svarte ikke innenfor forventet tid.", "apiwarn-validationfailed": "Bekreftelsesfeil $1: $2" } diff --git a/includes/api/i18n/nl.json b/includes/api/i18n/nl.json index 2faf2a8278..b6ac668f77 100644 --- a/includes/api/i18n/nl.json +++ b/includes/api/i18n/nl.json @@ -18,125 +18,125 @@ "Mainframe98" ] }, - "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Documentatie]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-maillijst]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-aankondigingen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & verzoeken]\n
\nStatus: Alle functies die op deze pagina worden weergegeven horen te werken. Aan de API wordt actief gewerkt, en deze kan gewijzigd worden. Abonneer u op de [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-maillijst mediawiki-api-announce] voor meldingen over aanpassingen.\n\nFoutieve verzoeken: als de API foutieve verzoeken ontvangt, wordt er geantwoord met een HTTP-header met de sleutel \"MediaWiki-API-Error\" en daarna worden de waarde van de header en de foutcode op dezelfde waarde ingesteld. Zie [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Foutmeldingen en waarschuwingen]] voor meer informatie.\n\nTesten: u kunt [[Special:ApiSandbox|eenvoudig API-verzoeken testen]].", + "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Documentatie]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-maillijst]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-aankondigingen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & verzoeken]\n
\nStatus: Alle op deze pagina weergegeven functies horen te werken, maar aan de API wordt actief gewerkt en deze kan dus op elk moment wijzigen. Abonneer u op de [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-maillijst mediawiki-api-announce] voor meldingen over aanpassingen.\n\nFoutieve verzoeken: als de API foutieve verzoeken ontvangt, wordt er geantwoord met een HTTP-header met de sleutel \"MediaWiki-API-Error\" en daarna worden de waarde van de header en de foutcode op dezelfde waarde ingesteld. Zie [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Foutmeldingen en waarschuwingen]] voor meer informatie.\n\nTesten: u kunt [[Special:ApiSandbox|eenvoudig API-verzoeken testen]].", "apihelp-main-param-action": "Welke handeling uit te voeren.", "apihelp-main-param-format": "De opmaak van de uitvoer.", - "apihelp-main-param-maxlag": "De maximale vertraging kan gebruikt worden als MediaWiki is geïnstalleerd op een databasecluster die gebruik maakt van replicatie. Om te voorkomen dat handelingen nog meer databasereplicatievertraging veroorzaken, kan deze parameter er voor zorgen dat de client wacht totdat de replicatievertraging lager is dan de aangegeven waarde. In het geval van buitensporige vertraging, wordt de foutcode maxlag teruggegeven met een bericht als Waiting for $host: $lag seconds lagged.
Zie [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Handleiding:Maxlag parameter]] voor meer informatie.", + "apihelp-main-param-maxlag": "De maximale vertraging kan gebruikt worden als MediaWiki is geïnstalleerd op een databasecluster die gebruik maakt van replicatie. Om te voorkomen dat handelingen nog meer databasereplicatievertraging veroorzaken, kan deze parameter ervoor zorgen dat de client wacht totdat de replicatievertraging lager is dan de aangegeven waarde. In geval van buitensporige vertraging wordt de foutcode maxlag teruggegeven met een bericht als Waiting for $host: $lag seconds lagged.
Zie [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Handleiding:Maxlag parameter]] voor meer informatie.", "apihelp-main-param-smaxage": "Stelt de s-maxage HTTP cache controle header in op het aangegeven aantal seconden. Foutmeldingen komen nooit in de cache.", "apihelp-main-param-maxage": "Stelt de max-age HTTP cache controle header in op het aangegeven aantal seconden. Foutmeldingen komen nooit in de cache.", - "apihelp-main-param-assert": "Controleer of de gebruiker is aangemeld als user is meegegeven, en of de gebruiker het robotgebruikersrecht heeft als bot is meegegeven.", - "apihelp-main-param-assertuser": "Bevestig dat de huidige gebruiker de genoemde gebruiker is.", + "apihelp-main-param-assert": "Controleer of de gebruiker is aangemeld indien assert=user, of het botgebruikersrecht heeft indien assert=bot.", + "apihelp-main-param-assertuser": "Controleer of de huidige gebruiker de genoemde gebruiker is.", "apihelp-main-param-requestid": "Elke waarde die hier gegeven wordt, wordt aan het antwoord toegevoegd. Dit kan gebruikt worden om verzoeken te onderscheiden.", - "apihelp-main-param-servedby": "Voeg de hostnaam van de server die de aanvraag heeft afgehandeld toe aan het antwoord.", - "apihelp-main-param-curtimestamp": "Huidige tijd aan het antwoord toevoegen.", - "apihelp-main-param-responselanginfo": "Toon de talen gebruikt voor uselang en errorlang in het resultaat.", - "apihelp-main-param-errorlang": "De taal om te gebruiken voor waarschuwingen en fouten. [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]] met siprop=languages toont een lijst van taalcodes, of stel inhoud in om gebruik te maken van de inhoudstaal van deze wiki, of stel uselang in om gebruik te maken van dezelfde waarde als de uselang parameter.", - "apihelp-main-param-errorsuselocal": "Indien ingesteld maken foutmeldingen gebruik van lokaal-aangepaste berichten in de {{ns:MediaWiki}} naamruimte.", + "apihelp-main-param-servedby": "De hostnaam van de server die de aanvraag heeft afgehandeld aan de resultaten toevoegen.", + "apihelp-main-param-curtimestamp": "Huidige tijd aan de resultaten toevoegen.", + "apihelp-main-param-responselanginfo": "De voor uselang en errorlang gebruikte talen aan de resultaten toevoegen.", + "apihelp-main-param-errorlang": "De voor waarschuwingen en fouten te gebruiken taal. [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]] met siprop=languages geeft een lijst van taalcodes, of stel content in om de taal van de inhoud van deze wiki te gebruiken, of stel uselang in om dezelfde waarde als de parameter uselang te gebruiken.", + "apihelp-main-param-errorsuselocal": "Indien ingesteld maken foutmeldingen gebruik van lokaal-aangepaste berichten in de {{ns:MediaWiki}}-naamruimte.", "apihelp-block-summary": "Gebruiker blokkeren.", - "apihelp-block-param-user": "Gebruikersnaam, IP-adres of IP-range om te blokkeren. Kan niet samen worden gebruikt me $1userid", - "apihelp-block-param-userid": "Gebruikers-ID om te blokkeren. Kan niet worden gebruikt in combinatie met $1user.", - "apihelp-block-param-expiry": "Vervaldatum. Kan relatief zijn (bijv. 5 months of 2 weeks) of absoluut (2014-09-18T12:34:56Z). Indien ingesteld op infinite, indefinite, of never verloopt de blokkade nooit.", + "apihelp-block-param-user": "Te blokkeren gebruikersnaam, IP-adres of IP-range. Kan niet in combinatie met $1userid gebruikt worden.", + "apihelp-block-param-userid": "Te blokkeren gebruikers-ID. Kan niet in combinatie met $1user gebruikt worden.", + "apihelp-block-param-expiry": "Vervaldatum. Kan relatief zijn (bijv. 5 months of 2 weeks) of absoluut (bijv. 2014-09-18T12:34:56Z). Indien ingesteld op infinite, indefinite of never zal de blokkade nooit verlopen.", "apihelp-block-param-reason": "Reden voor blokkade.", - "apihelp-block-param-anononly": "Alleen anonieme gebruikers blokkeren (uitschakelen van anonieme bewerkingen via dit IP-adres)", - "apihelp-block-param-nocreate": "Voorkom registeren van accounts.", - "apihelp-block-param-autoblock": "Blokkeer automatisch het laatst gebruikte IP-adres en ieder volgend IP-adres van waaruit ze proberen aan te melden.", - "apihelp-block-param-noemail": "Gebruiker weerhouden van het sturen van e-mail. (Vereist het blockemail recht).", - "apihelp-block-param-hidename": "Verberg de gebruikersnaam uit het blokkeerlogboek. (Vereist het hideuser recht).", - "apihelp-block-param-allowusertalk": "De gebruiker toestaan om hun eigen overlegpagina te bewerken (afhankelijk van [[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]).", - "apihelp-block-param-reblock": "De huidige blokkade aanpassen als de gebruiker al geblokkeerd is.", - "apihelp-block-param-watchuser": "De gebruikerspagina en overlegpagina van de gebruiker of het IP-adres volgen.", - "apihelp-block-param-tags": "Wijzigingslabels om toe te passen op de regel in het blokkeerlogboek.", - "apihelp-block-example-ip-simple": "Het IP-adres 192.0.2.5 voor drie dagen blokkeren met First strike als opgegeven reden.", - "apihelp-block-example-user-complex": "Blokkeer gebruikerVandal voor altijd met reden Vandalism en voorkom het aanmaken van nieuwe accounts en het versturen van email", - "apihelp-changeauthenticationdata-example-password": "Poging tot het wachtwoord van de huidige gebruiker te veranderen naar ExamplePassword.", - "apihelp-checktoken-summary": "Controleer de geldigheid van een token van [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].", - "apihelp-checktoken-param-type": "Tokentype wordt getest.", - "apihelp-checktoken-param-token": "Token om te controleren.", - "apihelp-checktoken-param-maxtokenage": "Maximum levensduur van de token, in seconden.", - "apihelp-checktoken-example-simple": "Test de geldigheid van een csrf token.", - "apihelp-clearhasmsg-summary": "Wist de hasmsg vlag voor de huidige gebruiker.", - "apihelp-clearhasmsg-example-1": "Wis de hasmsg vlag voor de huidige gebruiker.", - "apihelp-clientlogin-summary": "Log in op de wiki met behulp van de interactieve flow.", - "apihelp-clientlogin-example-login": "Start het inlogproces op de wiki als gebruiker Example met wachtwoord ExamplePassword.", - "apihelp-compare-summary": "Toon het verschil tussen 2 pagina's.", - "apihelp-compare-extended-description": "Een versienummer, een paginatitel of een pagina-ID is vereist voor zowel de \"from\" en \"to\" parameter.", - "apihelp-compare-param-fromtitle": "Eerste paginanaam om te vergelijken.", - "apihelp-compare-param-fromid": "Eerste pagina-ID om te vergelijken.", - "apihelp-compare-param-fromrev": "Eerste versie om te vergelijken.", - "apihelp-compare-param-totitle": "Tweede paginanaam om te vergelijken.", - "apihelp-compare-param-toid": "Tweede pagina-ID om te vergelijken.", - "apihelp-compare-param-torev": "Tweede versie om te vergelijken.", - "apihelp-createaccount-summary": "Nieuw gebruikersaccount aanmaken.", - "apihelp-createaccount-example-create": "Start het proces voor het aanmaken van de gebruiker Example met het wachtwoord ExamplePassword.", + "apihelp-block-param-anononly": "Alleen anonieme gebruikers blokkeren (d.w.z. anonieme bewerkingen via dit IP-adres uitschakelen).", + "apihelp-block-param-nocreate": "Het aanmaken van accounts verhinderen.", + "apihelp-block-param-autoblock": "Automatisch het laatst gebruikte IP-adres blokkeren, en tevens ieder volgend IP-adres waarvanaf de gebruiker probeert aan te melden.", + "apihelp-block-param-noemail": "Het sturen van e-mail via wiki verhinderen. (Vereist het blockemail-recht.)", + "apihelp-block-param-hidename": "De gebruikersnaam in het blokkeerlogboek verbergen. (Vereist het hideuser-recht.)", + "apihelp-block-param-allowusertalk": "De gebruiker toestaan om de eigen overlegpagina te bewerken (afhankelijk van [[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]).", + "apihelp-block-param-reblock": "De huidige blokkade overschrijven indien de gebruiker al geblokkeerd is.", + "apihelp-block-param-watchuser": "De gebruikers- en overlegpagina's van de gebruiker of het IP-adres volgen.", + "apihelp-block-param-tags": "De labels voor de regel in het blokkeerlogboek wijzigen.", + "apihelp-block-example-ip-simple": "IP-adres 192.0.2.5 voor drie dagen blokkeren met als reden First strike.", + "apihelp-block-example-user-complex": "Gebruiker Vandal voor onbepaalde tijd blokkeren met als reden Vandalism, en verhinder het aanmaken van nieuwe accounts en het versturen van e-mail.", + "apihelp-changeauthenticationdata-example-password": "Poging om het wachtwoord van de huidige gebruiker te veranderen in ExamplePassword.", + "apihelp-checktoken-summary": "De geldigheid van een token van [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] controleren.", + "apihelp-checktoken-param-type": "Het type token dat getest wordt.", + "apihelp-checktoken-param-token": "Te testen token.", + "apihelp-checktoken-param-maxtokenage": "Maximale levensduur van de token, in seconden.", + "apihelp-checktoken-example-simple": "Test de geldigheid van een csrf-token.", + "apihelp-clearhasmsg-summary": "Wist de hasmsg-vlag voor de huidige gebruiker.", + "apihelp-clearhasmsg-example-1": "De hasmsg-vlag voor de huidige gebruiker wissen.", + "apihelp-clientlogin-summary": "Bij de wiki aanmelden met behulp van de interactieve flow.", + "apihelp-clientlogin-example-login": "Start het aanmeldproces als gebruiker Example met wachtwoord ExamplePassword.", + "apihelp-compare-summary": "Het verschil tussen twee pagina's ophalen.", + "apihelp-compare-extended-description": "Voor zowel \"from\" als \"to\" moet een revisienummer, paginatitel, pagina-ID, tekst of relatieve referentie ingegeven worden.", + "apihelp-compare-param-fromtitle": "Eerste te vergelijken paginanaam.", + "apihelp-compare-param-fromid": "Eerste te vergelijken pagina-ID.", + "apihelp-compare-param-fromrev": "Eerste te vergelijken revisie.", + "apihelp-compare-param-totitle": "Tweede te vergelijken paginanaam.", + "apihelp-compare-param-toid": "Tweede te vergelijken pagina-ID.", + "apihelp-compare-param-torev": "Tweede te vergelijken revisie.", + "apihelp-createaccount-summary": "Een nieuw gebruikersaccount aanmaken.", + "apihelp-createaccount-example-create": "Start het proces voor het aanmaken van gebruiker Example met wachtwoord ExamplePassword.", "apihelp-createaccount-param-name": "Gebruikersnaam.", "apihelp-createaccount-param-password": "Wachtwoord (genegeerd als $1mailpassword is ingesteld).", "apihelp-createaccount-param-domain": "Domein voor externe authentificatie (optioneel).", "apihelp-createaccount-param-email": "E-mailadres van de gebruiker (optioneel).", "apihelp-createaccount-param-realname": "Echte naam van de gebruiker (optioneel).", - "apihelp-createaccount-param-reason": "Optionele reden voor het aanmaken van het account voor in het logboek.", - "apihelp-createaccount-param-language": "Taalcode om als standaard in te stellen voor de gebruiker (optioneel, standaard de inhoudstaal).", - "apihelp-createaccount-example-pass": "Maak gebruiker testuser aan met wachtwoord test123.", - "apihelp-createaccount-example-mail": "Maak gebruiker testmailuser aan en e-mail een willekeurig gegenereerd wachtwoord.", + "apihelp-createaccount-param-reason": "Optionele reden voor het aanmaken van het account, om in de logboeken te zetten.", + "apihelp-createaccount-param-language": "Taalcode om als standaard voor de gebruiker in te stellen (optioneel, standaard ingesteld op de inhoudstaal).", + "apihelp-createaccount-example-pass": "Gebruiker testuser met wachtwoord test123 aanmaken.", + "apihelp-createaccount-example-mail": "Gebruiker testmailuser aanmaken en een willekeurig gegenereerd wachtwoord e-mailen.", "apihelp-delete-summary": "Een pagina verwijderen.", - "apihelp-delete-param-title": "Titel van de pagina om te verwijderen. Kan niet samen worden gebruikt met $1pageid.", - "apihelp-delete-param-pageid": "ID van de pagina om te verwijderen. Kan niet samen worden gebruikt met $1title.", - "apihelp-delete-param-reason": "Reden voor verwijdering. Wanneer dit niet is opgegeven wordt een automatisch gegenereerde reden gebruikt.", - "apihelp-delete-param-tags": "Wijzigingslabels om toe te passen op de regel in het verwijderlogboek.", + "apihelp-delete-param-title": "Titel van de te verwijderen pagina. Kan niet in combinatie met $1pageid gebruikt worden.", + "apihelp-delete-param-pageid": "Pagina-ID van de te verwijderen pagina. Kan niet in combinatie met $1title gebruikt worden.", + "apihelp-delete-param-reason": "Reden voor de verwijdering. Indien niet opgegeven, zal er een automatisch gegenereerde reden gebruikt worden.", + "apihelp-delete-param-tags": "De labels voor de regel in het verwijderlogboek wijzigen.", "apihelp-delete-param-watch": "De pagina aan de volglijst van de huidige gebruiker toevoegen.", "apihelp-delete-param-unwatch": "De pagina van de volglijst van de huidige gebruiker verwijderen.", "apihelp-delete-example-simple": "Verwijder Main Page.", "apihelp-delete-example-reason": "Verwijder Main Page met als reden Preparing for move.", "apihelp-disabled-summary": "Deze module is uitgeschakeld.", - "apihelp-edit-summary": "Aanmaken en bewerken van pagina's.", - "apihelp-edit-param-title": "Naam van de pagina om te bewerken. Kan niet gebruikt worden samen met $1pageid.", - "apihelp-edit-param-pageid": "ID van de pagina om te bewerken. Kan niet samen worden gebruikt met $1title.", - "apihelp-edit-param-sectiontitle": "De naam van de nieuwe sectie.", + "apihelp-edit-summary": "Pagina's aanmaken en bewerken.", + "apihelp-edit-param-title": "Naam van de te bewerken pagina. Kan niet in combinatie met $1pageid gebruikt worden.", + "apihelp-edit-param-pageid": "Pagina-ID van de te bewerken pagina. Kan niet in combinatie met $1title gebruikt worden.", + "apihelp-edit-param-sectiontitle": "De naam van een nieuwe sectie.", "apihelp-edit-param-text": "Pagina-inhoud.", - "apihelp-edit-param-tags": "Wijzigingslabels om aan de versie toe te voegen.", + "apihelp-edit-param-tags": "De labels voor de revisie wijzigen.", "apihelp-edit-param-minor": "Kleine bewerking.", - "apihelp-edit-param-notminor": "Geen kleine bewerking.", - "apihelp-edit-param-bot": "Deze bewerking markeren als gedaan door een robot.", + "apihelp-edit-param-notminor": "Niet-kleine bewerking.", + "apihelp-edit-param-bot": "Deze bewerking markeren als een botbewerking.", "apihelp-edit-param-createonly": "De pagina niet bewerken als die al bestaat.", "apihelp-edit-param-nocreate": "Een foutmelding geven als de pagina niet bestaat.", "apihelp-edit-param-watch": "Voeg de pagina toe aan de volglijst van de huidige gebruiker.", "apihelp-edit-param-unwatch": "Verwijder de pagina van de volglijst van de huidige gebruiker.", - "apihelp-edit-param-md5": "De MD5-hash van de $1text parameter, of de $1prependtext en $1appendtext parameters samengevoegd. Indien ingesteld, wordt de bewerking niet gemaakt, tenzij de hash juist is.", + "apihelp-edit-param-md5": "De MD5-hash van de $1text parameter, of de $1prependtext en $1appendtext parameters samengevoegd. Indien ingesteld, wordt de bewerking niet gemaakt tenzij de hash juist is.", "apihelp-edit-param-prependtext": "Voeg deze tekst toe aan het begin van de pagina. Overschrijft $1text.", - "apihelp-edit-param-appendtext": "Voeg deze tekst toe aan het begin van de pagina. Overschrijft $1text.\n\nGebruik $1section=new in plaats van deze parameter om een nieuw kopje toe te voegen.", - "apihelp-edit-param-undo": "Maak deze versie ongedaan. Overschrijft $1text, $1prependtext en $1appendtext.", - "apihelp-edit-param-undoafter": "Maak alle versies vanaf $1undo to deze ongedaan maken. Indien niet ingesteld wordt slechts één versie ongedaan gemaakt.", + "apihelp-edit-param-appendtext": "Voeg deze tekst toe aan het eind van de pagina. Overschrijft $1text.\n\nGebruik $1section=new om een nieuwe sectie toe te voegen, in plaats van deze parameter.", + "apihelp-edit-param-undo": "Maak deze revisie ongedaan. Overschrijft $1text, $1prependtext en $1appendtext.", + "apihelp-edit-param-undoafter": "Maak alle revisies vanaf $1undo tot deze ongedaan. Indien niet ingesteld wordt slechts één revisie ongedaan gemaakt.", "apihelp-edit-param-redirect": "Doorverwijzingen automatisch oplossen.", "apihelp-edit-param-contentmodel": "Inhoudsmodel van de nieuwe inhoud.", - "apihelp-edit-param-token": "Het token moet altijd worden verzonden als de laatste parameter, of tenminste na de $1text parameter.", + "apihelp-edit-param-token": "De token moet altijd als de laatste parameter worden verzonden, of in ieder geval na de $1text parameter.", "apihelp-edit-example-edit": "Een pagina bewerken.", - "apihelp-edit-example-prepend": "Voeg __NOTOC__ toe aan het begin van een pagina.", - "apihelp-edit-example-undo": "Versies 13579 tot 13585 ongedaan maken met automatische beschrijving.", + "apihelp-edit-example-prepend": "Voeg __NOTOC__ toe aan het begin van een pagina.", + "apihelp-edit-example-undo": "Revisies 13579 tot 13585 ongedaan maken met automatische beschrijving.", "apihelp-emailuser-summary": "Gebruiker e-mailen.", "apihelp-emailuser-param-target": "Gebruiker naar wie de e-mail moet worden gestuurd.", "apihelp-emailuser-param-subject": "Onderwerpkoptekst.", "apihelp-emailuser-param-text": "E-mailtekst.", - "apihelp-emailuser-param-ccme": "Mij een kopie sturen van deze e-mail.", - "apihelp-emailuser-example-email": "Stuur een e-mail naar de gebruiker WikiSysop met de tekst Inhoud.", + "apihelp-emailuser-param-ccme": "Mij een kopie van deze e-mail sturen.", + "apihelp-emailuser-example-email": "Stuur een e-mail naar gebruiker WikiSysop met de tekst Content.", "apihelp-expandtemplates-param-title": "Paginanaam.", - "apihelp-expandtemplates-param-text": "Wikitekst om om te zetten.", - "apihelp-expandtemplates-paramvalue-prop-wikitext": "De uitgevulde wikitekst.", + "apihelp-expandtemplates-param-text": "Om te zetten wikitekst.", + "apihelp-expandtemplates-paramvalue-prop-wikitext": "De uitgewerkte wikitekst.", "apihelp-expandtemplates-paramvalue-prop-ttl": "De maximale tijdsduur waarna de cache van het resultaat moet worden weggegooid.", - "apihelp-feedcontributions-summary": "Haalt de feed van de gebruikersbijdragen op.", + "apihelp-feedcontributions-summary": "Retourneert een feed van gebruikersbijdragen.", "apihelp-feedcontributions-param-feedformat": "De indeling van de feed.", - "apihelp-feedcontributions-param-user": "De gebruiker om de bijdragen voor te verkrijgen.", + "apihelp-feedcontributions-param-user": "De gebruikers voor wie de bijdragen verkregen moeten worden.", "apihelp-feedcontributions-param-year": "Van jaar (en eerder).", "apihelp-feedcontributions-param-month": "Van maand (en eerder).", "apihelp-feedcontributions-param-deletedonly": "Alleen verwijderde bijdragen weergeven.", - "apihelp-feedcontributions-param-toponly": "Alleen bewerkingen die de nieuwste versies zijn weergeven.", - "apihelp-feedcontributions-param-newonly": "Alleen bewerkingen die nieuwe pagina's aanmaken weergeven.", + "apihelp-feedcontributions-param-toponly": "Alleen bewerkingen weergeven die de nieuwste revisies zijn.", + "apihelp-feedcontributions-param-newonly": "Alleen bewerkingen weergeven die nieuwe pagina's hebben gestart.", "apihelp-feedcontributions-param-hideminor": "Verberg kleine bewerkingen.", - "apihelp-feedcontributions-param-showsizediff": "Toon het verschil in grootte tussen versies.", + "apihelp-feedcontributions-param-showsizediff": "Toon het verschil in grootte tussen revisies.", "apihelp-feedcontributions-example-simple": "Toon bijdragen voor gebruiker Example.", "apihelp-feedrecentchanges-param-feedformat": "De indeling van de feed.", "apihelp-feedrecentchanges-param-namespace": "Naamruimte om de resultaten tot te beperken.", "apihelp-feedrecentchanges-param-invert": "Alle naamruimten behalve de geselecteerde.", "apihelp-feedrecentchanges-param-days": "Aantal dagen om de resultaten tot te beperken.", - "apihelp-feedrecentchanges-param-limit": "Het maximaal aantal weer te geven resultaten.", + "apihelp-feedrecentchanges-param-limit": "Het maximale aantal weer te geven resultaten.", "apihelp-feedrecentchanges-param-hideminor": "Kleine wijzigingen verbergen.", "apihelp-feedrecentchanges-param-hidebots": "Wijzigingen gedaan door bots verbergen.", "apihelp-feedrecentchanges-param-hideanons": "Wijzigingen gedaan door anonieme gebruikers verbergen.", @@ -146,199 +146,200 @@ "apihelp-feedrecentchanges-param-hidecategorization": "Wijzigingen in categorielidmaatschap verbergen.", "apihelp-feedrecentchanges-param-tagfilter": "Filteren op label.", "apihelp-feedrecentchanges-example-simple": "Recente wijzigingen weergeven.", - "apihelp-feedrecentchanges-example-30days": "Recente wijzigingen van de afgelopen 30 dagen weergeven.", + "apihelp-feedrecentchanges-example-30days": "Wijzigingen van de afgelopen 30 dagen weergeven.", "apihelp-feedwatchlist-param-feedformat": "De indeling van de feed.", "apihelp-filerevert-summary": "Een oude versie van een bestand terugplaatsen.", - "apihelp-filerevert-param-filename": "Doel bestandsnaam, zonder het Bestand: voorvoegsel.", + "apihelp-filerevert-param-filename": "Bestandsnaam, zonder het Bestand: voorvoegsel.", "apihelp-filerevert-param-comment": "Opmerking voor het uploaden.", "apihelp-filerevert-example-revert": "Zet Wiki.png terug naar de versie van 2011-03-05T15:27:40Z.", - "apihelp-help-summary": "Toon help voor de opgegeven modules.", - "apihelp-help-param-helpformat": "Indeling van de help uitvoer.", + "apihelp-help-summary": "Toon hulp voor de opgegeven modules.", + "apihelp-help-param-helpformat": "Indeling van de hulpuitvoer.", "apihelp-help-example-main": "Hulp voor de hoofdmodule.", "apihelp-help-example-submodules": "Hulp voor action=query en alle submodules.", - "apihelp-help-example-recursive": "Alle hulp op een pagina.", - "apihelp-help-example-help": "Help voor de help-module zelf.", + "apihelp-help-example-recursive": "Alle hulp op één pagina.", + "apihelp-help-example-help": "Hulp voor de hulpmodule zelf.", "apihelp-imagerotate-summary": "Een of meerdere afbeeldingen draaien.", "apihelp-imagerotate-param-rotation": "Aantal graden om de afbeelding met de klok mee te draaien.", - "apihelp-imagerotate-param-tags": "Labels om toe te voegen aan de regel in het uploadlogboek.", + "apihelp-imagerotate-param-tags": "Labels voor de regel in het uploadlogboek.", "apihelp-imagerotate-example-simple": "Roteer File:Example.png met 90 graden.", "apihelp-imagerotate-example-generator": "Roteer alle afbeeldingen in Category:Flip met 180 graden.", - "apihelp-import-summary": "Importeer een pagina van een andere wiki, of van een XML bestand.", - "apihelp-import-extended-description": "Merk op dat de HTTP POST moet worden uitgevoerd als bestandsupload (bijv. door middel van multipart/form-data) wanneer een bestand wordt verstuurd voor de xml parameter.", - "apihelp-import-param-summary": "Importsamenvatting voor het logboek.", + "apihelp-import-summary": "Importeer een pagina van een andere wiki, of van een XML-bestand.", + "apihelp-import-extended-description": "Merk op dat de HTTP POST moet worden uitgevoerd als bestandsupload (d.w.z. door middel van multipart/form-data) wanneer een bestand wordt verstuurd voor de xml parameter.", + "apihelp-import-param-summary": "Importeersamenvatting voor het logboek.", "apihelp-import-param-xml": "Geüpload XML-bestand.", - "apihelp-import-param-interwikisource": "Voor interwiki imports: wiki om van te importeren.", - "apihelp-import-param-namespace": "Importeren in deze naamruimte. Can niet samen gebruikt worden met $1rootpage.", - "apihelp-import-param-rootpage": "Importeren als subpagina van deze pagina. Kan niet samen met $1namespace gebruikt worden.", + "apihelp-import-param-interwikisource": "Voor interwiki-imports: wiki om van te importeren.", + "apihelp-import-param-namespace": "Naar deze naamruimte importeren. Kan niet in combinatie met $1rootpage gebruikt worden.", + "apihelp-import-param-rootpage": "Als subpagina van deze pagina importeren. Kan niet in combinatie met $1namespace gebruikt worden.", "apihelp-import-example-import": "Importeer [[meta:Help:ParserFunctions]] in naamruimte 100 met de volledige geschiedenis.", "apihelp-login-param-name": "Gebruikersnaam.", "apihelp-login-param-password": "Wachtwoord.", "apihelp-login-param-domain": "Domein (optioneel).", - "apihelp-login-example-login": "Aanmelden", + "apihelp-login-example-login": "Aanmelden.", "apihelp-logout-summary": "Afmelden en sessiegegevens wissen.", - "apihelp-logout-example-logout": "Meldt de huidige gebruiker af.", - "apihelp-managetags-param-tag": "Label om aan te maken, te activeren of te deactiveren. Voor het aanmaken van een label, mag het niet bestaan. Voor het verwijderen van een label, moet het bestaan. Voor het activeren van een label, moet het bestaan en mag het niet gebruikt worden door een uitbreiding. Voor het deactiveren van een label, moet het gebruikt worden en handmatig gedefinieerd zijn.", + "apihelp-logout-example-logout": "Meld de huidige gebruiker af.", + "apihelp-managetags-param-tag": "Aan te maken, te verwijderen, te activeren of te deactiveren label. Voor het aanmaken mag het label nog niet bestaan. Voor het verwijderen moet het label bestaan. Voor het activeren moet het label bestaan en mag het niet door een uitbreiding gebruikt worden. Voor het deactiveren moet het label gebruikt worden en handmatig gedefinieerd zijn.", "apihelp-managetags-example-create": "Maak een label met de naam spam aan met als reden For use in edit patrolling", - "apihelp-managetags-example-delete": "Verwijder het vandlaism label met de reden Misspelt", - "apihelp-mergehistory-summary": "Geschiedenis van pagina's samenvoegen.", - "apihelp-mergehistory-param-reason": "Reden voor samenvoegen van de geschiedenis.", - "apihelp-mergehistory-example-merge": "Voeg de hele geschiedenis van Oldpage samen met Newpage.", + "apihelp-managetags-example-delete": "Verwijder het label vandlaism met als reden Misspelt", + "apihelp-mergehistory-summary": "Geschiedenissen van pagina's samenvoegen.", + "apihelp-mergehistory-param-reason": "Reden voor de samenvoeging van de geschiedenissen.", + "apihelp-mergehistory-example-merge": "Voeg de hele geschiedenis van Oldpage samen in Newpage.", "apihelp-move-summary": "Pagina hernoemen.", "apihelp-move-param-to": "Nieuwe paginanaam.", "apihelp-move-param-reason": "Reden voor de naamswijziging.", "apihelp-move-param-movetalk": "Hernoem de overlegpagina, indien deze bestaat.", "apihelp-move-param-noredirect": "Geen doorverwijzing achterlaten.", - "apihelp-move-param-watch": "Pagina en de omleiding toevoegen aan de volglijst van de huidige gebruiker.", + "apihelp-move-param-watch": "Voeg de pagina en de doorverwijzing toe aan de volglijst van de huidige gebruiker.", "apihelp-move-param-unwatch": "Verwijder de pagina en de doorverwijzing van de volglijst van de huidige gebruiker.", "apihelp-move-param-watchlist": "De pagina onvoorwaardelijk toevoegen aan of verwijderen van de volglijst van de huidige gebruiker, gebruik voorkeuren of wijzig het volgen niet.", "apihelp-move-param-ignorewarnings": "Eventuele waarschuwingen negeren.", - "apihelp-move-example-move": "Hernoem Badtitle naar Goodtitle zonder een doorverwijzing te laten staan.", - "apihelp-opensearch-summary": "Zoeken in de wiki met het OpenSearchprotocol.", + "apihelp-move-example-move": "Hernoem Badtitle naar Goodtitle zonder een doorverwijzing achter te laten.", + "apihelp-opensearch-summary": "In de wiki zoeken met behulp het OpenSearchprotocol.", "apihelp-opensearch-param-search": "Zoektekst.", - "apihelp-opensearch-param-limit": "Het maximaal aantal weer te geven resultaten.", + "apihelp-opensearch-param-limit": "Het maximale aantal weer te geven resultaten.", "apihelp-opensearch-param-namespace": "Te doorzoeken naamruimten.", "apihelp-opensearch-param-suggest": "Niets doen als [[mw:Special:MyLanguage/Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]] onwaar is.", - "apihelp-opensearch-param-redirects": "Hoe om te gaan met doorverwijzingen:\n;return:Geef de doorverwijzing terug.\n;resolve:Geef de doelpagina terug. Kan minder dan de limiet $1 resultaten teruggeven.\nOm historische redenen is de standaardinstelling \"return\" voor $1format=json en \"resolve\" voor andere formaten.", + "apihelp-opensearch-param-redirects": "Hoe om te gaan met doorverwijzingen:\n;return:Retourneer de doorverwijzing.\n;resolve:Retourneer de doelpagina. Retourneert mogelijk minder dan $1limit resultaten.\nOm historische redenen is \"return\" de standaardinstelling voor $1format=json, en \"resolve\" voor andere formaten.", "apihelp-opensearch-param-format": "Het uitvoerformaat.", "apihelp-opensearch-param-warningsaserror": "Als er waarschuwingen zijn met format=json, geef dan een API-fout terug in plaats van deze te negeren.", - "apihelp-opensearch-example-te": "Pagina's vinden die beginnen met Te.", + "apihelp-opensearch-example-te": "Vind pagina's die beginnen met Te.", "apihelp-options-summary": "Voorkeuren van de huidige gebruiker wijzigen.", - "apihelp-options-extended-description": "Alleen opties die zijn geregistreerd in core of in een van de geïnstalleerde uitbreidingen, of opties met de toetsen aangeduid met userjs- (bedoeld om te worden gebruikt door gebruikersscripts), kunnen worden ingesteld.", - "apihelp-options-param-reset": "Zet de voorkeuren terug naar de standaard van de website.", - "apihelp-options-param-resetkinds": "Lijst van de optiestypes die opnieuw ingesteld worden wanneer de optie $1reset is ingesteld.", - "apihelp-options-param-change": "Lijst van wijzigingen, opgemaakt als naam=waarde (bijvoorbeeld skin=vector). Als er geen waarde wordt opgegeven (zelfs niet een is-gelijk teken), bijvoorbeeld optienaam|andereoptie|..., dan wordt de optie ingesteld op de standaardwaarde. Als een opgegeven waarde een sluisteken bevat (|), gebruik dan het [[Special:ApiHelp/main#main/datatypes|alternatieve scheidingsteken tussen meerdere waardes]] voor een juiste werking.", - "apihelp-options-param-optionname": "De naam van de optie die moet worden ingesteld op de waarde gegeven door $1optiewaarde.", + "apihelp-options-extended-description": "Alleen opties die zijn geregistreerd in core of in een van de geïnstalleerde uitbreidingen, of opties met sleutels die beginnen met userjs- (bedoeld om door gebruikersscripts gebruikt te worden), kunnen worden ingesteld.", + "apihelp-options-param-reset": "Reset voorkeuren naar de standaard van de website.", + "apihelp-options-param-resetkinds": "Lijst van types van te resetten opties wanneer de optie $1reset is ingesteld.", + "apihelp-options-param-change": "Lijst van wijzigingen, opgemaakt als naam=waarde (bijv. skin=vector). Als er geen waarde wordt opgegeven (zelfs niet een isgelijkteken), bijvoorbeeld optienaam|andereoptie|..., dan wordt de optie ingesteld op de standaardwaarde. Als een opgegeven waarde een sluisteken bevat (|), gebruik dan het [[Special:ApiHelp/main#main/datatypes|alternatieve scheidingsteken tussen meerdere waardes]] voor een juiste werking.", + "apihelp-options-param-optionname": "De naam van de optie die moet worden ingesteld op de waarde gegeven door $1optionvalue.", "apihelp-options-param-optionvalue": "De waarde voor de optie opgegeven door $1optionname.", - "apihelp-options-example-reset": "Alle voorkeuren opnieuw instellen.", - "apihelp-options-example-change": "Voorkeuren wijzigen voor skin en hideminor.", + "apihelp-options-example-reset": "Reset alle voorkeuren.", + "apihelp-options-example-change": "Wijzig skin en hideminor voorkeuren.", "apihelp-paraminfo-summary": "Verkrijg informatie over API-modules.", - "apihelp-parse-paramvalue-prop-categorieshtml": "Vraagt een HTML-versie van de categorieën op.", + "apihelp-parse-paramvalue-prop-categorieshtml": "Geeft de HTML-versie van de categorieën.", "apihelp-parse-example-page": "Een pagina verwerken.", - "apihelp-parse-example-text": "Wikitext verwerken.", + "apihelp-parse-example-text": "Wikitekst verwerken.", + "apihelp-parse-example-texttitle": "Wikitekst verwerken, waarbij de paginatitel opgeven is.", "apihelp-parse-example-summary": "Een samenvatting verwerken.", - "apihelp-patrol-summary": "Een pagina of versie markeren als gecontroleerd.", - "apihelp-patrol-example-rcid": "Een recente wijziging markeren als gecontroleerd.", - "apihelp-patrol-example-revid": "Een versie markeren als gecontroleerd.", - "apihelp-protect-param-reason": "Reden voor opheffen van de beveiliging.", - "apihelp-protect-example-protect": "Een pagina beveiligen", + "apihelp-patrol-summary": "Een pagina of revisie als gecontroleerd markeren.", + "apihelp-patrol-example-rcid": "Een recente wijziging als gecontroleerd markeren.", + "apihelp-patrol-example-revid": "Een revisie als gecontroleerd markeren.", + "apihelp-protect-param-reason": "Reden voor instellen of opheffen van de beveiliging.", + "apihelp-protect-example-protect": "Een pagina beveiligen.", "apihelp-purge-param-forcelinkupdate": "Werk de koppelingstabellen bij.", - "apihelp-purge-param-forcerecursivelinkupdate": "Werk de koppelingentabel bij, en werk de koppelingstabellen bij voor alle pagina's die gebruik maken van deze pagina als sjabloon.", + "apihelp-purge-param-forcerecursivelinkupdate": "Werk de koppelingentabel bij, en werk de koppelingstabellen bij voor alle pagina's die deze pagina als sjabloon gebruiken.", "apihelp-query+allcategories-param-dir": "Richting om in te sorteren.", "apihelp-query+allcategories-param-limit": "Hoeveel categorieën te tonen.", "apihelp-query+allcategories-paramvalue-prop-size": "Voegt het aantal pagina's in de categorie toe.", - "apihelp-query+allcategories-paramvalue-prop-hidden": "Markeert categorieën die verborgen zijn met __HIDDENCAT__", - "apihelp-query+alldeletedrevisions-param-tag": "Alleen versies weergeven met dit label.", - "apihelp-query+alldeletedrevisions-param-excludeuser": "Toon geen versies door deze gebruiker.", - "apihelp-query+alldeletedrevisions-param-namespace": "Toon alleen pagina's in deze naamruimte.", + "apihelp-query+allcategories-paramvalue-prop-hidden": "Markeert categorieën die verborgen zijn met __HIDDENCAT__.", + "apihelp-query+alldeletedrevisions-param-tag": "Alleen versies met dit label weergeven.", + "apihelp-query+alldeletedrevisions-param-excludeuser": "Geen revisies door deze gebruiker weergeven.", + "apihelp-query+alldeletedrevisions-param-namespace": "Alleen pagina's in deze naamruimte weergeven.", "apihelp-query+allfileusages-paramvalue-prop-title": "Voegt de titel van het bestand toe.", "apihelp-query+allfileusages-param-limit": "Hoeveel items er in totaal moeten worden getoond.", - "apihelp-query+allimages-example-recent": "Toon een lijst van recentlijk geüploade bestanden, vergelijkbaar met [[Special:NewFiles]].", - "apihelp-query+alllinks-param-namespace": "De naamruimte om door te lopen.", + "apihelp-query+allimages-example-recent": "Toon een lijst van recentelijk geüploade bestanden, vergelijkbaar met [[Special:NewFiles]].", + "apihelp-query+alllinks-param-namespace": "De door te lopen naamruimte.", "apihelp-query+alllinks-param-limit": "Hoeveel items er in totaal moeten worden getoond.", - "apihelp-query+allmessages-param-enableparser": "Stel in om de parser in te schakelen, zorgt voor het voorverwerken van de wikitekst van een bericht (vervangen van magische woorden, de afhandeling van sjablonen, enzovoort).", + "apihelp-query+allmessages-param-enableparser": "Stel in om de parser in te schakelen, zorgt voor het voorverwerken van de wikitekst van een bericht (vervangen van magische woorden, afhandelen van sjablonen, enz.).", "apihelp-query+allmessages-param-lang": "Toon berichten in deze taal.", "apihelp-query+allmessages-param-from": "Toon berichten vanaf dit bericht.", "apihelp-query+allmessages-param-to": "Toon berichten tot aan dit bericht.", "apihelp-query+allredirects-summary": "Toon alle doorverwijzingen naar een naamruimte.", - "apihelp-query+allrevisions-example-user": "Toon de laatste 50 bijdragen van de gebruiker Example.", + "apihelp-query+allrevisions-example-user": "Toon de laatste 50 bijdragen van gebruiker Example.", "apihelp-query+mystashedfiles-paramvalue-prop-type": "Vraag het MIME- en mediatype van het bestand op.", "apihelp-query+mystashedfiles-param-limit": "Hoeveel bestanden te tonen.", "apihelp-query+allusers-param-excludegroup": "Sluit gebruikers in de gegeven groepen uit.", - "apihelp-query+allusers-paramvalue-prop-blockinfo": "Voegt informatie over een actuale blokkade van de gebruiker toe.", - "apihelp-query+allusers-paramvalue-prop-groups": "Toont de groepen waar de gebruiker in zit. Dit gebruikt meer serverbronnen en kan minder resultaten teruggeven dat de opgegeven limiet.", - "apihelp-query+allusers-paramvalue-prop-implicitgroups": "Toon alle groepen de gebruiker automatisch in zit.", - "apihelp-query+allusers-paramvalue-prop-rights": "Toon de rechten die de gebruiker heeft.", + "apihelp-query+allusers-paramvalue-prop-blockinfo": "Voegt informatie over een actuele blokkade van de gebruiker toe.", + "apihelp-query+allusers-paramvalue-prop-groups": "Toont de groepen waar de gebruiker in zit. Dit gebruikt meer serverbronnen en retourneert mogelijk minder resultaten dan de opgegeven limiet.", + "apihelp-query+allusers-paramvalue-prop-implicitgroups": "Toont alle groepen waar de gebruiker automatisch in zit.", + "apihelp-query+allusers-paramvalue-prop-rights": "Toont de rechten die de gebruiker heeft.", "apihelp-query+allusers-paramvalue-prop-editcount": "Voegt het aantal bewerkingen van de gebruiker toe.", "apihelp-query+allusers-paramvalue-prop-registration": "Voegt de registratiedatum van de gebruiker toe, indien beschikbaar (kan leeg zijn).", "apihelp-query+allusers-param-witheditsonly": "Toon alleen gebruikers die bewerkingen hebben gemaakt.", - "apihelp-query+allusers-param-activeusers": "Toon alleen gebruikers die actief zijn geweest in de laatste $1 {{PLURAL:$1|dag|dagen}}.", + "apihelp-query+allusers-param-activeusers": "Toon alleen gebruikers die actief zijn geweest in de laatste {{PLURAL:$1|dag|$1 dagen}}.", "apihelp-query+allusers-example-Y": "Toon gebruikers vanaf Y.", - "apihelp-query+authmanagerinfo-summary": "Haal informatie op over de huidige authentificatie status.", + "apihelp-query+authmanagerinfo-summary": "Haal informatie op over de huidige authentificatiestatus.", "apihelp-query+backlinks-summary": "Vind alle pagina's die verwijzen naar de gegeven pagina.", - "apihelp-query+backlinks-param-title": "Titel om op te zoeken. Kan niet worden gebruikt in combinatie met$1pageid.", - "apihelp-query+backlinks-param-pageid": "Pagina ID om op te zoeken. Kan niet worden gebruikt in combinatie met $1title.", - "apihelp-query+backlinks-param-namespace": "De naamruimte om door te lopen.", - "apihelp-query+backlinks-example-simple": "Toon verwijzingen naar de Hoofdpagina.", + "apihelp-query+backlinks-param-title": "Te onderzoeken titel. Kan niet in combinatie met $1pageid gebruikt worden.", + "apihelp-query+backlinks-param-pageid": "Te onderzoeken pagina-ID. Kan niet in combinatie met $1title gebruikt worden.", + "apihelp-query+backlinks-param-namespace": "De door te lopen naamruimte.", + "apihelp-query+backlinks-example-simple": "Toon verwijzingen naar Main page.", "apihelp-query+blocks-summary": "Toon alle geblokkeerde gebruikers en IP-adressen.", - "apihelp-query+blocks-param-limit": "Het maximum aantal blokkades te tonen.", - "apihelp-query+blocks-paramvalue-prop-id": "Voegt de blokkade ID toe.", - "apihelp-query+blocks-paramvalue-prop-user": "Voegt de gebruikernaam van de geblokeerde gebruiker toe.", - "apihelp-query+blocks-paramvalue-prop-userid": "Voegt de gebruiker-ID van de geblokkeerde gebruiker toe.", + "apihelp-query+blocks-param-limit": "Het maximale aantal te tonen blokkades.", + "apihelp-query+blocks-paramvalue-prop-id": "Voegt de blokkade-ID toe.", + "apihelp-query+blocks-paramvalue-prop-user": "Voegt de gebruikersnaam van de geblokkeerde gebruiker toe.", + "apihelp-query+blocks-paramvalue-prop-userid": "Voegt de gebruikers-ID van de geblokkeerde gebruiker toe.", "apihelp-query+blocks-paramvalue-prop-flags": "Labelt de blokkade met (automatische blokkade, alleen anoniem, enzovoort).", "apihelp-query+blocks-example-simple": "Toon blokkades.", "apihelp-query+blocks-example-users": "Toon blokkades van gebruikers Alice en Bob.", - "apihelp-query+categories-summary": "Toon alle categorieën waar de pagina in zit.", + "apihelp-query+categories-summary": "Toon alle categorieën waar de pagina's in zitten.", "apihelp-query+categories-paramvalue-prop-hidden": "Markeert categorieën die verborgen zijn met __HIDDENCAT__", "apihelp-query+categories-param-show": "Welke soort categorieën te tonen.", "apihelp-query+categories-param-limit": "Hoeveel categorieën te tonen.", "apihelp-query+categorymembers-paramvalue-prop-ids": "Voegt de pagina-ID toe.", "apihelp-query+categorymembers-paramvalue-prop-title": "Voegt de titel en de naamruimte-ID van de pagina toe.", "apihelp-query+categorymembers-param-dir": "Richting om in te sorteren.", - "apihelp-query+deletedrevisions-param-tag": "Alleen versies weergeven met dit label.", - "apihelp-query+deletedrevs-param-tag": "Alleen versies weergeven met dit label.", - "apihelp-query+embeddedin-param-namespace": "De naamruimte om door te lopen.", - "apihelp-query+fileusage-paramvalue-prop-pageid": "Pagina ID van elke pagina.", + "apihelp-query+deletedrevisions-param-tag": "Alleen revisies met dit label weergeven.", + "apihelp-query+deletedrevs-param-tag": "Alleen revisies met dit label weergeven.", + "apihelp-query+embeddedin-param-namespace": "De door te lopen naamruimte.", + "apihelp-query+fileusage-paramvalue-prop-pageid": "Pagina-ID van elke pagina.", "apihelp-query+fileusage-paramvalue-prop-title": "Titel van elke pagina.", - "apihelp-query+imageusage-param-namespace": "De naamruimte om door te lopen.", - "apihelp-query+imageusage-example-simple": "Toon pagina's die gebruik maken van [[:File:Albert Einstein Head.jpg]].", - "apihelp-query+imageusage-example-generator": "Toon informatie over pagina's die gebruik maken van [[:File:Albert Einstein Head.jpg]].", + "apihelp-query+imageusage-param-namespace": "De door te lopen naamruimte.", + "apihelp-query+imageusage-example-simple": "Toon pagina's die [[:File:Albert Einstein Head.jpg]] gebruiken.", + "apihelp-query+imageusage-example-generator": "Toon informatie over pagina's die [[:File:Albert Einstein Head.jpg]] gebruiken.", "apihelp-query+iwbacklinks-param-prefix": "Voorvoegsel voor de interwiki.", "apihelp-query+logevents-param-type": "Logboekregels alleen voor dit type filteren.", - "apihelp-query+logevents-param-tag": "Alleen logboekregels weergeven met dit label.", + "apihelp-query+logevents-param-tag": "Alleen logboekregels met dit label weergeven.", "apihelp-query+logevents-example-simple": "Recente logboekregels weergeven.", "apihelp-query+protectedtitles-paramvalue-prop-level": "Voegt het beveiligingsniveau toe.", "apihelp-query+protectedtitles-example-simple": "Toon beveiligde titels.", - "apihelp-query+querypage-param-limit": "Aantal resultaten om te tonen.", + "apihelp-query+querypage-param-limit": "Aantal te tonen resultaten.", "apihelp-query+querypage-example-ancientpages": "Toon resultaten van [[Special:Ancientpages]].", "apihelp-query+random-param-namespace": "Toon alleen pagina's in deze naamruimten.", - "apihelp-query+random-param-limit": "Beperk het aantal aan willekeurige pagina's dat wordt getoond.", + "apihelp-query+random-param-limit": "Beperk hoeveel willekeurige pagina's worden getoond.", "apihelp-query+random-example-simple": "Toon twee willekeurige pagina's uit de hoofdnaamruimte.", - "apihelp-query+random-example-generator": "Toon pagina informatie over twee willekeurige pagina's uit de hoofdnaamruimte.", + "apihelp-query+random-example-generator": "Toon pagina-informatie over twee willekeurige pagina's uit de hoofdnaamruimte.", "apihelp-query+recentchanges-param-user": "Toon alleen wijzigingen door deze gebruiker.", - "apihelp-query+recentchanges-param-excludeuser": "Toon geen wijzigingen door deze gebruiker", - "apihelp-query+recentchanges-param-tag": "Alleen versies weergeven met dit label.", + "apihelp-query+recentchanges-param-excludeuser": "Toon geen wijzigingen door deze gebruiker.", + "apihelp-query+recentchanges-param-tag": "Toon alleen wijzigingen met dit label.", "apihelp-query+recentchanges-paramvalue-prop-comment": "Voegt de bewerkingssamenvatting voor de bewerking toe.", - "apihelp-query+recentchanges-paramvalue-prop-loginfo": "Voegt logboekgegevens toe aan logboekregels (logboek-ID, logboektype, enzovoort).", + "apihelp-query+recentchanges-paramvalue-prop-loginfo": "Voegt logboekgegevens (logboek-ID, logboektype, enz.) aan logboekregels toe.", "apihelp-query+recentchanges-example-simple": "Toon recente wijzigingen.", - "apihelp-query+redirects-paramvalue-prop-pageid": "Pagina ID van elke doorverwijzing.", + "apihelp-query+redirects-paramvalue-prop-pageid": "Pagina-ID van elke doorverwijzing.", "apihelp-query+redirects-paramvalue-prop-title": "Titel van elke doorverwijzing.", "apihelp-query+redirects-param-namespace": "Toon alleen pagina's in deze naamruimten.", "apihelp-query+redirects-param-limit": "Hoeveel doorverwijzingen te tonen.", - "apihelp-query+redirects-example-simple": "Toon een lijst van doorverwijzingen naar [[Main Page]].", - "apihelp-query+redirects-example-generator": "Toon informatie over alle doorverwijzingen naar [[Main Page]].", - "apihelp-query+revisions-param-tag": "Alleen versies weergeven met dit label.", - "apihelp-query+revisions+base-paramvalue-prop-content": "Versietekst.", + "apihelp-query+redirects-example-simple": "Een lijst van doorverwijzingen naar [[Main Page]] ophalen.", + "apihelp-query+redirects-example-generator": "Informatie over alle doorverwijzingen naar [[Main Page]] ophalen.", + "apihelp-query+revisions-param-tag": "Alleen revisies met dit label weergeven.", + "apihelp-query+revisions+base-paramvalue-prop-content": "Tekst van de revisie.", "apihelp-query+revisions+base-paramvalue-prop-tags": "Labels voor de versie.", - "apihelp-query+revisions+base-param-difftotextpst": "Gebruik in plaats hiervan [[Special:ApiHelp/compare|action=compare]]. \"pre-save\"-transformatie uitvoeren op de tekst alvorens de verschillen te bepalen. Alleen geldig als dit wordt gebruikt met $1difftotext.", - "apihelp-query+search-summary": "Voer een volledige tekst zoekopdracht uit.", + "apihelp-query+revisions+base-param-difftotextpst": "Gebruik in plaats hiervan [[Special:ApiHelp/compare|action=compare]]. Een \"pre-save\"-transformatie uitvoeren op de tekst alvorens de verschillen te bepalen. Alleen geldig indien gebruikt met $1difftotext.", + "apihelp-query+search-summary": "Voer een zoekopdracht in de volledige tekst uit.", "apihelp-query+search-param-limit": "Hoeveel pagina's te tonen.", - "apihelp-query+search-example-simple": "Zoeken naar betekenis.", - "apihelp-query+siteinfo-paramvalue-prop-namespacealiases": "Toon geregistreerde naamruimte aliassen.", - "apihelp-query+siteinfo-paramvalue-prop-specialpagealiases": "Toon speciale pagina aliassen.", - "apihelp-query+siteinfo-paramvalue-prop-magicwords": "Toon magische woorden en hun aliassen.", - "apihelp-query+siteinfo-paramvalue-prop-statistics": "Toon site statistieken.", - "apihelp-query+siteinfo-paramvalue-prop-libraries": "Toont bibliotheken die op de wiki zijn geïnstalleerd.", - "apihelp-query+siteinfo-paramvalue-prop-extensions": "Toont uitbreidingen die op de wiki zijn geïnstalleerd.", - "apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Geeft een lijst met bestandsextensies (bestandstypen) die geüpload mogen worden.", - "apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Toont wiki rechten (licentie) informatie als deze beschikbaar is.", + "apihelp-query+search-example-simple": "Zoeken naar meaning.", + "apihelp-query+siteinfo-paramvalue-prop-namespacealiases": "Lijst van geregistreerde naamruimte-aliassen.", + "apihelp-query+siteinfo-paramvalue-prop-specialpagealiases": "Lijst van aliassen voor speciale pagina's.", + "apihelp-query+siteinfo-paramvalue-prop-magicwords": "Lijst van magische woorden en hun aliassen.", + "apihelp-query+siteinfo-paramvalue-prop-statistics": "Site-statistieken.", + "apihelp-query+siteinfo-paramvalue-prop-libraries": "Bibliotheken die op de wiki zijn geïnstalleerd.", + "apihelp-query+siteinfo-paramvalue-prop-extensions": "Uitbreidingen die op de wiki zijn geïnstalleerd.", + "apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Lijst van bestandsextensies (bestandstypen) die geüpload mogen worden.", + "apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Informatie over wikirechten (licentie-informatie) indien beschikbaar.", "apihelp-query+tags-summary": "Wijzigingslabels weergeven.", "apihelp-query+tags-paramvalue-prop-name": "Voegt de naam van het label toe.", - "apihelp-query+tags-paramvalue-prop-displayname": "Voegt het systeembericht toe voor het label.", - "apihelp-query+tags-paramvalue-prop-description": "Voegt beschrijving van het label toe.", + "apihelp-query+tags-paramvalue-prop-displayname": "Voegt het systeembericht voor het label toe.", + "apihelp-query+tags-paramvalue-prop-description": "Voegt de beschrijving van het label toe.", "apihelp-query+tags-paramvalue-prop-defined": "Geeft aan of het label is gedefinieerd.", "apihelp-query+tags-paramvalue-prop-active": "Of het label nog steeds wordt toegepast.", "apihelp-query+tags-example-simple": "Toon beschikbare labels.", - "apihelp-query+templates-summary": "Toon alle pagina's ingesloten op de gegeven pagina's.", - "apihelp-query+templates-param-limit": "Het aantal sjablonen om te tonen.", - "apihelp-query+transcludedin-paramvalue-prop-pageid": "Pagina ID van elke pagina.", + "apihelp-query+templates-summary": "Retourneert alle pagina's die ingesloten zijn op de gegeven pagina's.", + "apihelp-query+templates-param-limit": "Hoeveel sjablonen te retourneren.", + "apihelp-query+transcludedin-paramvalue-prop-pageid": "Pagina-ID van elke pagina.", "apihelp-query+transcludedin-paramvalue-prop-title": "Titel van elke pagina.", - "apihelp-query+usercontribs-summary": "Toon alle bewerkingen door een gebruiker.", - "apihelp-query+usercontribs-param-limit": "Het maximum aantal bewerkingen om te tonen.", - "apihelp-query+usercontribs-param-namespace": "Toon alleen bijdragen in deze naamruimten.", - "apihelp-query+usercontribs-param-tag": "Alleen versies weergeven met dit label.", - "apihelp-query+usercontribs-example-ipprefix": "Toon bijdragen van alle IP-adressen met het voorvoegsel 192.0.2..", - "apihelp-query+userinfo-summary": "Toon informatie over de huidige gebruiker.", - "apihelp-query+userinfo-paramvalue-prop-realname": "Toon de gebruikers echte naam.", + "apihelp-query+usercontribs-summary": "Alle bewerkingen door een gebruiker opvragen.", + "apihelp-query+usercontribs-param-limit": "Het maximale aantal te tonen bijdragen.", + "apihelp-query+usercontribs-param-namespace": "Alleen bijdragen in deze naamruimten weergeven.", + "apihelp-query+usercontribs-param-tag": "Alleen revisies met dit label weergeven.", + "apihelp-query+usercontribs-example-ipprefix": "Bijdragen van alle IP-adressen met het voorvoegsel 192.0.2. weergeven.", + "apihelp-query+userinfo-summary": "Informatie over de huidige gebruiker opvragen.", + "apihelp-query+userinfo-paramvalue-prop-realname": "Voegt de echte naam van de gebruiker toe.", "apihelp-query+watchlist-paramvalue-prop-loginfo": "Voegt logboekgegevens toe waar van toepassing.", "apihelp-query+watchlist-param-type": "Welke typen wijzigingen weer te geven:", "apihelp-query+watchlist-paramvalue-type-edit": "Gewone paginabewerkingen.", @@ -347,11 +348,11 @@ "apihelp-query+watchlist-paramvalue-type-log": "Logboekregels.", "apihelp-query+watchlist-paramvalue-type-categorize": "Wijzigingen in categorielidmaatschap.", "apihelp-stashedit-param-text": "Pagina-inhoud.", - "apihelp-unblock-param-user": "Gebruikersnaam, IP-adres of IP-range om te deblokkeren. Kan niet samen worden gebruikt met $1id of $1userid.", - "apihelp-unblock-param-userid": "Gebruikers-ID om te deblokkeren. Kan niet worden gebruikt in combinatie met $1id of $1user.", - "apihelp-json-param-formatversion": "Uitvoeropmaak:\n;1:Achterwaarts compatibele opmaak (XML-stijl booleans, *-sleutels voor contentnodes, enzovoort).\n;2:Experimentele moderne opmaak. Details kunnen wijzigen!\n;latest:Gebruik de meest recente opmaak (op het moment 2), kan zonder waarschuwing wijzigen.", - "apihelp-php-param-formatversion": "Uitvoeropmaak:\n;1:Achterwaarts compatibele opmaak (XML-stijl booleans, *-sleutels voor contentnodes, enzovoort).\n;2:Experimentele moderne opmaak. Details kunnen wijzigen!\n;latest:Gebruik de meest recente opmaak (op het moment 2), kan zonder waarschuwing wijzigen.", - "apihelp-rawfm-summary": "Uitvoergegevens, inclusief debugelementen, opgemaakt in JSON (nette opmaak in HTML).", + "apihelp-unblock-param-user": "Te deblokkeren gebruikersnaam, IP-adres of IP-range. Kan niet in combinatie met $1id of $1userid gebruikt worden.", + "apihelp-unblock-param-userid": "Te deblokkeren gebruikers-ID. Kan niet in combinatie met $1id of $1user gebruikt worden.", + "apihelp-json-param-formatversion": "Uitvoeropmaak:\n;1:Achterwaarts-compatibele opmaak (booleans in XML-stijl, *-sleutels voor contentnodes, enz.).\n;2:Experimentele moderne opmaak. Details kunnen wijzigen!\n;latest:Gebruik de meest recente opmaak (op het moment 2), kan zonder waarschuwing wijzigen.", + "apihelp-php-param-formatversion": "Uitvoeropmaak:\n;1:Achterwaarts-compatibele opmaak (booleans in XML-stijl, *-sleutels voor contentnodes, enz.).\n;2:Experimentele moderne opmaak. Details kunnen wijzigen!\n;latest:Gebruik de meest recente opmaak (op het moment 2), kan zonder waarschuwing wijzigen.", + "apihelp-rawfm-summary": "Gegevens, inclusief debugelementen, in JSON-formaat (nette opmaak in HTML) uitvoeren.", "api-help-flag-readrights": "Voor deze module zijn leesrechten nodig.", "api-help-flag-writerights": "Voor deze module zijn schrijfrechten nodig.", "api-help-parameters": "{{PLURAL:$1|Parameter|Parameters}}:", @@ -359,15 +360,15 @@ "api-help-datatypes-header": "Gegevenstypen", "api-help-param-default": "Standaard: $1", "api-help-examples": "{{PLURAL:$1|Voorbeeld|Voorbeelden}}:", - "apierror-autoblocked": "Uw IP-adres is automatisch geblokeerd, omdat het gebruikt is door een geblokkeerde gebruiker.", + "apierror-autoblocked": "Uw IP-adres is automatisch geblokkeerd, omdat het gebruikt werd door een geblokkeerde gebruiker.", "apierror-badmodule-nosubmodules": "De module $1 heeft geen submodules.", - "apierror-blockedfrommail": "U bent geblokkeerd en kunt geen emails verzenden.", - "apierror-blocked": "U bent geblokkeerd en kunt niet bewerken.", + "apierror-blockedfrommail": "Het versturen van e-mail is voor u geblokkeerd.", + "apierror-blocked": "Het bewerken is voor u geblokkeerd.", "apierror-filedoesnotexist": "Bestand bestaat niet.", "apierror-integeroutofrange-belowminimum": "$1 mag niet minder zijn dan $2 (ingesteld op $3).", "apierror-invalidcategory": "De opgegeven categorienaam is niet geldig.", "apierror-invaliduser": "Ongeldige gebruikersnaam \"$1\".", - "apierror-maxlag-generic": "Wachten op een database server: $1 {{PLURAL:$1|seconde|seconden}} vertraging.", + "apierror-maxlag-generic": "Wachten op een databaseserver: $1 {{PLURAL:$1|seconde|seconden}} vertraging.", "apierror-maxlag": "Wachten op $2: $1 {{PLURAL:$1|seconde|seconden}} vertraging.", "apierror-missingtitle": "De opgegeven pagina bestaat niet.", "apierror-missingtitle-byname": "De pagina $1 bestaat niet.", @@ -386,7 +387,7 @@ "apiwarn-notfile": "\"$1\" is geen bestand.", "apiwarn-validationfailed-badpref": "geen geldige voorkeur.", "api-feed-error-title": "Fout ($1)", - "api-usage-docref": "Zie $1 voor API gebruik.", + "api-usage-docref": "Zie $1 voor API-gebruik.", "api-credits-header": "Vermeldingen", - "api-credits": "API-ontwikkelaars:\n* Roan Kattouw (hoofdontwikkelaar september 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (oorspronkelijke ontwikkelaar, hoofdontwikkelaar september 2006 – september 2007)\n* Brad Jorsch (hoofdontwikkelaar 2013 – heden)\n\nStuur uw opmerkingen, suggesties en vragen naar mediawiki-api@lists.wikimedia.org\nof maak een melding aan op https://phabricator.wikimedia.org/." + "api-credits": "API-ontwikkelaars:\n* Yuri Astrakhan (oorspronkelijke ontwikkelaar, hoofdontwikkelaar september 2006 – september 2007)\n* Roan Kattouw (hoofdontwikkelaar september 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Brad Jorsch (hoofdontwikkelaar 2013–heden)\n\nStuur uw opmerkingen, suggesties en vragen naar mediawiki-api@lists.wikimedia.org\nof maak een bugrapport aan op https://phabricator.wikimedia.org/." } diff --git a/includes/api/i18n/pt-br.json b/includes/api/i18n/pt-br.json index db03a3af9b..5c8a83c168 100644 --- a/includes/api/i18n/pt-br.json +++ b/includes/api/i18n/pt-br.json @@ -13,7 +13,8 @@ "Eduardo Addad de Oliveira", "Warley Felipe C.", "TheEduGobi", - "Felipe L. Ewald" + "Felipe L. Ewald", + "Hamilton Abreu" ] }, "apihelp-main-extended-description": "
\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 Bugs & solicitações]\n
\nStatus: Todos os recursos exibidos nesta página devem estar funcionando, mas a API ainda está em desenvolvimento ativo e pode mudar a qualquer momento. Inscrever-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para aviso de atualizações.\n\nRequisições incorretas: Quando requisições erradas são enviadas para a API, um cabeçalho HTTP será enviado com a chave \"MediaWiki-API-Error\" e então o valor do cabeçalho e o código de erro enviados de volta serão definidos para o mesmo valor. Para mais informações, veja [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Erros e avisos]].\n\nTestando: Para facilitar o teste das requisições da API, consulte [[Special:ApiSandbox]].", diff --git a/includes/api/i18n/pt.json b/includes/api/i18n/pt.json index b85ddc96b1..47e5cd645d 100644 --- a/includes/api/i18n/pt.json +++ b/includes/api/i18n/pt.json @@ -14,8 +14,8 @@ "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 ''site'', 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 maxlag é devolvido com uma mensagem como À espera do servidor $host: $lag segundos de atraso.
Consulte [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.", - "apihelp-main-param-smaxage": "Definir no cabeçalho HTTP s-maxage de controlo da ''cache'' este número de segundos. Os erros nunca são armazenados na ''cache''.", - "apihelp-main-param-maxage": "Definir no cabeçalho HTTP max-age de controlo da ''cache'' este número de segundos. Os erros nunca são armazenados na ''cache''.", + "apihelp-main-param-smaxage": "Definir no cabeçalho HTTP s-maxage de controlo da cache este número de segundos. Os erros nunca são armazenados na cache.", + "apihelp-main-param-maxage": "Definir no cabeçalho HTTP max-age de controlo da cache este número de segundos. Os erros nunca são armazenados na cache.", "apihelp-main-param-assert": "Se definido com o valor user, verificar que o utilizador está autenticado. Se definido com o valor bot, verificar que o utilizador tem o privilégio de conta robô.", "apihelp-main-param-assertuser": "Verificar que o utilizador atual é o utilizador nomeado.", "apihelp-main-param-requestid": "Qualquer valor fornecido aqui será incluído na resposta. Pode ser usado para distinguir pedidos.", @@ -158,7 +158,7 @@ "apihelp-expandtemplates-paramvalue-prop-categories": "Quaisquer categorias existentes na entrada que não estão representadas no texto em notação wiki de saída.", "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página, definidas por palavras mágicas expandidas, no texto em notação wiki.", "apihelp-expandtemplates-paramvalue-prop-volatile": "Indica se o resultado é volátil e não deve ser reutilizado noutra parte da página.", - "apihelp-expandtemplates-paramvalue-prop-ttl": "O período máximo a partir do qual os armazenamentos do resultado na ''cache'' devem ser invalidados.", + "apihelp-expandtemplates-paramvalue-prop-ttl": "O período máximo a partir do qual os armazenamentos do resultado na cache devem ser invalidados.", "apihelp-expandtemplates-paramvalue-prop-modules": "Quaisquer módulos ResourceLoader que as funções do analisador sintático solicitaram que fossem adicionados ao resultado de saída. Um dos valores jsconfigvars ou encodedjsconfigvars tem de ser solicitado em conjunto com o valor modules.", "apihelp-expandtemplates-paramvalue-prop-jsconfigvars": "Devolve as variáveis de configuração JavaScript específicas desta página.", "apihelp-expandtemplates-paramvalue-prop-encodedjsconfigvars": "Devolve as variáveis de configuração JavaScript específicas da página, no formato de uma ''string'' JSON.", @@ -196,7 +196,7 @@ "apihelp-feedrecentchanges-param-hidecategorization": "Ocultar mudanças de pertença a categorias.", "apihelp-feedrecentchanges-param-tagfilter": "Filtrar por etiqueta.", "apihelp-feedrecentchanges-param-target": "Mostrar apenas mudanças em páginas afluentes a esta.", - "apihelp-feedrecentchanges-param-showlinkedto": "Mostrar mudanças em páginas com ligações para a página selecionada.", + "apihelp-feedrecentchanges-param-showlinkedto": "Mostrar mudanças em páginas com hiperligações para a página selecionada.", "apihelp-feedrecentchanges-param-categories": "Mostrar apenas mudanças nas páginas que estão em todas estas categorias.", "apihelp-feedrecentchanges-param-categories_any": "Mostrar apenas mudanças nas páginas que estão em qualquer uma das categorias.", "apihelp-feedrecentchanges-example-simple": "Mostrar mudanças recentes.", @@ -329,23 +329,23 @@ "apihelp-parse-param-oldid": "Analisar o conteúdo desta revisão. Tem precedência sobre $1page e $1pageid.", "apihelp-parse-param-prop": "As informações que devem ser obtidas:", "apihelp-parse-paramvalue-prop-text": "Fornece o texto analisado, de um texto com notação wiki.", - "apihelp-parse-paramvalue-prop-langlinks": "Fornece os links interlínguas do texto analisado.", - "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias do texto analisado.", + "apihelp-parse-paramvalue-prop-langlinks": "Fornece as hiperligações interlínguas do texto wiki analisado.", + "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias do texto wiki analisado.", "apihelp-parse-paramvalue-prop-categorieshtml": "Fornece a versão HTML das categorias.", - "apihelp-parse-paramvalue-prop-links": "Fornece os links internos do texto analisado.", - "apihelp-parse-paramvalue-prop-templates": "Fornece as predefinições do texto analisado.", - "apihelp-parse-paramvalue-prop-images": "Fornece as imagens do texto analisado.", - "apihelp-parse-paramvalue-prop-externallinks": "Fornece os links externos do texto analisado.", - "apihelp-parse-paramvalue-prop-sections": "Fornece as secções do texto analisado.", + "apihelp-parse-paramvalue-prop-links": "Fornece as hiperligações internas do texto wiki analisado.", + "apihelp-parse-paramvalue-prop-templates": "Fornece as predefinições do texto wiki analisado.", + "apihelp-parse-paramvalue-prop-images": "Fornece as imagens do texto wiki analisado.", + "apihelp-parse-paramvalue-prop-externallinks": "Fornece as hiperligações externas do texto wiki analisado.", + "apihelp-parse-paramvalue-prop-sections": "Fornece as secções do texto wiki analisado.", "apihelp-parse-paramvalue-prop-revid": "Adiciona o identificador de revisão da página analisada.", - "apihelp-parse-paramvalue-prop-displaytitle": "Adiciona o título do texto analisado.", + "apihelp-parse-paramvalue-prop-displaytitle": "Adiciona o título do texto wiki analisado.", "apihelp-parse-paramvalue-prop-headitems": "Fornece os elementos a colocar no <head> da página.", "apihelp-parse-paramvalue-prop-headhtml": "Fornece o <head> analisado da página.", "apihelp-parse-paramvalue-prop-modules": "Fornece os módulos ResourceLoader usados na página. Para carregá-los, usar mw.loader.using(). Uma das variáveis jsconfigvars ou encodedjsconfigvars tem de ser pedida em conjunto com modules.", "apihelp-parse-paramvalue-prop-jsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página. Para aplicá-las, usar mw.config.set().", "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página, no formato de uma ''string'' JSON.", "apihelp-parse-paramvalue-prop-indicators": "Fornece o HTML dos indicadores de estado de página que são usados na página.", - "apihelp-parse-paramvalue-prop-iwlinks": "Fornece os links interwikis do texto analisado.", + "apihelp-parse-paramvalue-prop-iwlinks": "Fornece as hiperligações interwikis do texto wiki analisado.", "apihelp-parse-paramvalue-prop-wikitext": "Fornece o texto original com notação wiki que foi analisado.", "apihelp-parse-paramvalue-prop-properties": "Fornece várias propriedades definidas no texto analisado.", "apihelp-parse-paramvalue-prop-limitreportdata": "Fornece o relatório de limites de forma estruturada. Não fornece dados quando $1disablelimitreport está definido.", @@ -355,12 +355,12 @@ "apihelp-parse-param-wrapoutputclass": "A classe CSS a utilizar para envolver o resultado do analisador sintático.", "apihelp-parse-param-pst": "Fazer uma transformação anterior à gravação do texto de entrada, antes de analisá-lo. Só é válido quando usado com texto.", "apihelp-parse-param-onlypst": "Fazer uma transformação anterior à gravação (PST, ''pre-save transform'') do texto de entrada, mas não o analisar. Devolve o mesmo texto após aplicação da PST. Só é válido quando usado com $1text.", - "apihelp-parse-param-effectivelanglinks": "Inclui links interlínguas fornecidos por extensões (para ser usado com $1prop=langlinks).", + "apihelp-parse-param-effectivelanglinks": "Inclui hiperligações interlínguas fornecidas por extensões (para ser usado com $1prop=langlinks).", "apihelp-parse-param-section": "Analisar apenas o conteúdo desta secção.\n\nQuando tiver o valor new, analisar $1text e $1sectiontitle como se fosse adicionar uma nova secção à página.\n\nnew só é permitido quando se especifica text.", "apihelp-parse-param-sectiontitle": "O novo título da secção quando section tem o valor new.\n\nAo contrário da edição de páginas, este não toma o valor de summary se for omitido ou estiver vazio.", "apihelp-parse-param-disablelimitreport": "Omitir o relatório de limites (\"NewPP limit report\") do resultado de saída do analisador sintático.", "apihelp-parse-param-disablepp": "Em vez deste, usar $1disablelimitreport.", - "apihelp-parse-param-disableeditsection": "Omitir links para edição da secção no resultado da análise sintática.", + "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-generatexml": "Gerar a árvore de análise XML (requer o modelo de conteúdo $1; substituído por $2prop=parsetree).", "apihelp-parse-param-preview": "Executar a análise em modo de antevisão.", @@ -392,9 +392,9 @@ "apihelp-protect-example-protect": "Proteger uma página.", "apihelp-protect-example-unprotect": "Desproteger uma página definindo a restrição all (isto é, todos podem executar a operação).", "apihelp-protect-example-unprotect2": "Desproteger uma página definindo que não há restrições.", - "apihelp-purge-summary": "Limpar a ''cache'' para os títulos especificados.", - "apihelp-purge-param-forcelinkupdate": "Atualizar as tabelas de ligações.", - "apihelp-purge-param-forcerecursivelinkupdate": "Atualizar a tabela de ligações, e atualizar as tabelas de ligações de qualquer página que usa esta página como modelo.", + "apihelp-purge-summary": "Limpar a cache para os títulos especificados.", + "apihelp-purge-param-forcelinkupdate": "Atualizar as tabelas de hiperligações.", + "apihelp-purge-param-forcerecursivelinkupdate": "Atualizar a tabela de hiperligações, e atualizar as tabelas de hiperligações de qualquer página que usa esta página como modelo.", "apihelp-purge-example-simple": "Purgar as páginas Main Page e API.", "apihelp-purge-example-generator": "Purgar as primeiras 10 páginas no espaço nominal principal.", "apihelp-query-summary": "Obter dados de, e sobre, o MediaWiki.", @@ -405,7 +405,7 @@ "apihelp-query-param-indexpageids": "Incluir uma secção adicional de identificadores de página que lista todos os identificadores de página devolvidos.", "apihelp-query-param-export": "Exportar as revisões atuais de todas as páginas fornecidas ou geradas.", "apihelp-query-param-exportnowrap": "Devolver o XML de exportação sem envolvê-lo num resultado XML (o mesmo formato que [[Special:Export]]). Só pode ser usado com $1export.", - "apihelp-query-param-iwurl": "Indica se deve ser obtido o URL completo quando o título é um ''link'' interwikis.", + "apihelp-query-param-iwurl": "Indica se deve ser obtido o URL completo quando o título é uma hiperligação interwikis.", "apihelp-query-param-rawcontinue": "Devolver os dados em bruto de query-continue para continuar.", "apihelp-query-example-revisions": "Obter [[Special:ApiHelp/query+siteinfo|informação do ''site'']] e as [[Special:ApiHelp/query+revisions|revisões]] da página Main Page.", "apihelp-query-example-allpages": "Obter as revisões das páginas que começam por API/.", @@ -472,20 +472,20 @@ "apihelp-query+allimages-example-recent": "Mostrar uma lista dos ficheiros carregados recentemente, semelhante a [[Special:NewFiles]].", "apihelp-query+allimages-example-mimetypes": "Mostrar uma lista dos ficheiros com os tipos MIME image/png ou image/gif.", "apihelp-query+allimages-example-generator": "Mostrar informação sobre 4 ficheiros, começando pela letra T.", - "apihelp-query+alllinks-summary": "Enumerar todos os ''links'' que apontam para um determinado espaço nominal.", - "apihelp-query+alllinks-param-from": "O título do ''link'' a partir do qual será começada a enumeração.", - "apihelp-query+alllinks-param-to": "O título do ''link'' no qual será terminada a enumeração.", + "apihelp-query+alllinks-summary": "Enumerar todas as hiperligações que apontam para um determinado espaço nominal.", + "apihelp-query+alllinks-param-from": "O título da hiperligação a partir da qual será começada a enumeração.", + "apihelp-query+alllinks-param-to": "O título da hiperligação na qual será terminada a enumeração.", "apihelp-query+alllinks-param-prefix": "Procurar todos os títulos ligados que começam por este valor.", "apihelp-query+alllinks-param-unique": "Mostrar só títulos ligados únicos. Não pode ser usado com $1prop=ids.\nAo ser usado como gerador, produz páginas de destino em vez de páginas de origem.", "apihelp-query+alllinks-param-prop": "As informações que devem ser incluídas:", - "apihelp-query+alllinks-paramvalue-prop-ids": "Adiciona o identificador da página que contém a ligação (não pode ser usado com $1unique).", - "apihelp-query+alllinks-paramvalue-prop-title": "Adiciona o título do ''link''.", + "apihelp-query+alllinks-paramvalue-prop-ids": "Adiciona o identificador da página que contém a hiperligação (não pode ser usado com $1unique).", + "apihelp-query+alllinks-paramvalue-prop-title": "Adiciona o título da hiperligação.", "apihelp-query+alllinks-param-namespace": "O espaço nominal a ser enumerado.", "apihelp-query+alllinks-param-limit": "O número total de entradas a serem devolvidas.", "apihelp-query+alllinks-param-dir": "A direção de listagem.", "apihelp-query+alllinks-example-B": "Listar os títulos para os quais existem ligações, incluindo títulos em falta, com os identificadores das páginas que contêm as respetivas ligações, começando pela letra B.", - "apihelp-query+alllinks-example-unique": "Listar os títulos únicos para os quais existem ligações.", - "apihelp-query+alllinks-example-unique-generator": "Obtém todos os títulos para os quais existem ligações, marcando aqueles em falta.", + "apihelp-query+alllinks-example-unique": "Listar os títulos únicos para os quais existem hiperligações.", + "apihelp-query+alllinks-example-unique-generator": "Obtém todos os títulos para os quais existem hiperligações, marcando aqueles em falta.", "apihelp-query+alllinks-example-generator": "Obtém as páginas que contêm as ligações.", "apihelp-query+allmessages-summary": "Devolver as mensagens deste ''site''.", "apihelp-query+allmessages-param-messages": "Mensagens a serem produzidas no resultado. * (o valor por omissão) significa todas as mensagens.", @@ -516,7 +516,7 @@ "apihelp-query+allpages-param-prfiltercascade": "Filtrar as proteções com base na proteção em cascata (ignorado se $1prtype não estiver presente).", "apihelp-query+allpages-param-limit": "O número total de páginas a serem devolvidas.", "apihelp-query+allpages-param-dir": "A direção de listagem.", - "apihelp-query+allpages-param-filterlanglinks": "Filtrar dependo de uma página ter ''links'' interlínguas. Note que isto pode não tomar em consideração ''links'' interlínguas adicionados por extensões.", + "apihelp-query+allpages-param-filterlanglinks": "Filtrar dependo de uma página conter hiperligações interlínguas. Note que isto pode não ter em consideração hiperligações interlínguas adicionadas por extensões.", "apihelp-query+allpages-param-prexpiry": "O tipo de expiração pelo qual as páginas serão filtradas:\n;indefinite:Obter só páginas com um período de expiração indefinido.\n;definite:Obter só páginas com um período de expiração definido (específico).\n;all:Obter páginas com qualquer período de expiração.", "apihelp-query+allpages-example-B": "Mostrar uma lista de páginas, começando na letra B.", "apihelp-query+allpages-example-generator": "Mostrar informação sobre 4 páginas, começando na letra T.", @@ -595,16 +595,16 @@ "apihelp-query+authmanagerinfo-example-login": "Obter os pedidos que podem ser usados ao iniciar uma sessão.", "apihelp-query+authmanagerinfo-example-login-merged": "Obter os pedidos que podem ser usados ao iniciar uma sessão, com os campos combinados.", "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Testar se a autenticação é suficiente para a operação foo.", - "apihelp-query+backlinks-summary": "Encontrar todas as páginas que contêm ligações para a página indicada.", + "apihelp-query+backlinks-summary": "Encontrar todas as páginas que contêm hiperligações para a página indicada.", "apihelp-query+backlinks-param-title": "O título a ser procurado. Não pode ser usado em conjunto com $1pageid.", "apihelp-query+backlinks-param-pageid": "O identificador do título a ser procurado. Não pode ser usado em conjunto com $1title.", "apihelp-query+backlinks-param-namespace": "O espaço nominal a ser enumerado.", "apihelp-query+backlinks-param-dir": "A direção de listagem.", "apihelp-query+backlinks-param-filterredir": "Como filtrar os redirecionamentos. Se definido como nonredirects quando $1redirect está ativado, isto só é aplicado ao segundo nível.", "apihelp-query+backlinks-param-limit": "O número total de páginas a serem devolvidas. Se $1redirect estiver ativado, o limite aplica-se a cada nível em separado (o que significa que até 2 * $1limit resultados podem ser devolvidos).", - "apihelp-query+backlinks-param-redirect": "Se a página que contém a ligação é um redirecionamento, procurar também todas as páginas que contêm ligações para esse redirecionamento. O limite máximo é reduzido para metade.", - "apihelp-query+backlinks-example-simple": "Mostrar as ligações para Main page.", - "apihelp-query+backlinks-example-generator": "Obter informações sobre as páginas com ligações para Main page.", + "apihelp-query+backlinks-param-redirect": "Se a página que contém a hiperligação é um redirecionamento, procurar também todas as páginas que contêm hiperligações para esse redirecionamento. O limite máximo é reduzido para metade.", + "apihelp-query+backlinks-example-simple": "Mostrar as hiperligações para Main page.", + "apihelp-query+backlinks-example-generator": "Obter informações sobre as páginas com hiperligações para Main page.", "apihelp-query+blocks-summary": "Listar todos os utilizadores e endereços IP bloqueados.", "apihelp-query+blocks-param-start": "A data e hora a partir da qual será começada a enumeração.", "apihelp-query+blocks-param-end": "A data e hora na qual será terminada a enumeração.", @@ -717,21 +717,21 @@ "apihelp-query+embeddedin-example-generator": "Obter informação sobre as páginas que transcluem Template:Stub.", "apihelp-query+extlinks-summary": "Devolve todos os URL externos (que não sejam interwikis) das páginas especificadas.", "apihelp-query+extlinks-param-limit": "O número de ''links'' a serem devolvidos.", - "apihelp-query+extlinks-param-protocol": "Protocolo do URL. Se vazio e $1query está definido, o protocolo é http. Deixe isto e $1query vazios para listar todos os ''links'' externos.", + "apihelp-query+extlinks-param-protocol": "Protocolo do URL. Se vazio e $1query está definido, o protocolo é http. Deixe este parâmetro e $1query vazios para listar todas as hiperligações externas.", "apihelp-query+extlinks-param-query": "Texto de pesquisa sem protocolo. Útil para verificar se uma determinada página contém um determinado URL externo.", "apihelp-query+extlinks-param-expandurl": "Expandir os URL relativos a protocolo com o protocolo canónico.", - "apihelp-query+extlinks-example-simple": "Obter uma lista das ligações externas na Main Page.", + "apihelp-query+extlinks-example-simple": "Obter uma lista das hiperligações externas da Main Page.", "apihelp-query+exturlusage-summary": "Enumerar as páginas que contêm um determinado URL.", "apihelp-query+exturlusage-param-prop": "As informações que devem ser incluídas:", "apihelp-query+exturlusage-paramvalue-prop-ids": "Adiciona o identificador da página.", "apihelp-query+exturlusage-paramvalue-prop-title": "Adiciona o título e o identificador do espaço nominal da página.", "apihelp-query+exturlusage-paramvalue-prop-url": "Adiciona o URL usado na página.", - "apihelp-query+exturlusage-param-protocol": "Protocolo do URL. Se vazio e $1query está definido, o protocolo é http. Deixe isto e $1query vazios para listar todos os ''links'' externos.", - "apihelp-query+exturlusage-param-query": "Texto da pesquisa sem um protocolo. Ver [[Special:LinkSearch]]. Deixar vazio para listar todos os ''links'' externos.", + "apihelp-query+exturlusage-param-protocol": "Protocolo do URL. Se vazio e $1query está definido, o protocolo é http. Deixe este parâmetro e $1query vazios para listar todas as hiperligações externas.", + "apihelp-query+exturlusage-param-query": "Texto da pesquisa sem um protocolo. Ver [[Special:LinkSearch]]. Deixar vazio para listar todas as hiperligações externas.", "apihelp-query+exturlusage-param-namespace": "Os espaços nominais a serem enumerados.", "apihelp-query+exturlusage-param-limit": "O número de páginas a serem devolvidas.", "apihelp-query+exturlusage-param-expandurl": "Expandir os URL relativos a protocolo com o protocolo canónico.", - "apihelp-query+exturlusage-example-simple": "Mostrar as páginas com ligações para http://www.mediawiki.org.", + "apihelp-query+exturlusage-example-simple": "Mostrar as páginas com hiperligações para http://www.mediawiki.org.", "apihelp-query+filearchive-summary": "Enumerar todos os ficheiros eliminados sequencialmente.", "apihelp-query+filearchive-param-from": "O título da imagem a partir do qual será começada a enumeração.", "apihelp-query+filearchive-param-to": "O título da imagem no qual será terminada a enumeração.", @@ -837,57 +837,57 @@ "apihelp-query+info-example-simple": "Obter informações sobre a página Main Page.", "apihelp-query+info-example-protection": "Obter informação geral e de proteção sobre a página Main Page.", "apihelp-query+iwbacklinks-summary": "Encontrar todas as páginas que contêm ''links'' para as páginas indicadas.", - "apihelp-query+iwbacklinks-extended-description": "Pode ser usado para encontrar todos os ''links'' com um prefixo, ou todos os ''links'' para um título (com um prefixo especificado). Se nenhum parâmetro for usado, isso efetivamente significa \"todos os ''links'' interwikis\".", + "apihelp-query+iwbacklinks-extended-description": "Pode ser usado para encontrar todas as hiperligações com um prefixo, ou todas as hiperligações para um título (com um prefixo especificado). Se nenhum dos parâmetros for usado, isso efetivamente significa \"todas as hiperligações interwikis\".", "apihelp-query+iwbacklinks-param-prefix": "O prefixo interwikis.", - "apihelp-query+iwbacklinks-param-title": "O ''link'' interwikis a ser procurado. Tem de ser usado em conjunto com $1blprefix.", + "apihelp-query+iwbacklinks-param-title": "A hiperligação interwikis a ser procurada. Tem de ser usado em conjunto com $1blprefix.", "apihelp-query+iwbacklinks-param-limit": "O número total de páginas a serem devolvidas.", "apihelp-query+iwbacklinks-param-prop": "As propriedades a serem obtidas:", - "apihelp-query+iwbacklinks-paramvalue-prop-iwprefix": "Adiciona o prefixo do ''link'' interwikis.", - "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Adiciona o título do ''link'' interwikis.", + "apihelp-query+iwbacklinks-paramvalue-prop-iwprefix": "Adiciona o prefixo da hiperligação interwikis.", + "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Adiciona o título da hiperligação interwikis.", "apihelp-query+iwbacklinks-param-dir": "A direção de listagem.", - "apihelp-query+iwbacklinks-example-simple": "Obter as páginas que contêm ligações para [[wikibooks:Test]].", - "apihelp-query+iwbacklinks-example-generator": "Obter informação sobre as páginas que contêm ligações para [[wikibooks:Test]].", - "apihelp-query+iwlinks-summary": "Devolve todos os ''links'' interwikis das páginas indicadas.", + "apihelp-query+iwbacklinks-example-simple": "Obter as páginas que contêm hiperligações para [[wikibooks:Test]].", + "apihelp-query+iwbacklinks-example-generator": "Obter informação sobre as páginas que contêm hiperligações para [[wikibooks:Test]].", + "apihelp-query+iwlinks-summary": "Devolve todas as hiperligações interwikis das páginas indicadas.", "apihelp-query+iwlinks-param-url": "Indica se deve ser obtido o URL completo (não pode ser usado com $1prop).", - "apihelp-query+iwlinks-param-prop": "As propriedades adicionais que devem ser obtidas para cada ''link'' interlínguas:", + "apihelp-query+iwlinks-param-prop": "As propriedades adicionais que devem ser obtidas para cada hiperligação interlínguas:", "apihelp-query+iwlinks-paramvalue-prop-url": "Adiciona o URL completo.", - "apihelp-query+iwlinks-param-limit": "O número de ''links'' interwikis a serem devolvidos.", - "apihelp-query+iwlinks-param-prefix": "Devolver só os ''links'' interwikis com este prefixo.", - "apihelp-query+iwlinks-param-title": "Link interwikis a ser procurado. Tem de ser usado em conjunto com $1prefix.", + "apihelp-query+iwlinks-param-limit": "O número de hiperligações interwikis a serem devolvidas.", + "apihelp-query+iwlinks-param-prefix": "Devolver só as hiperligações interwikis com este prefixo.", + "apihelp-query+iwlinks-param-title": "Hiperligação interwikis a ser procurada. Tem de ser usado em conjunto com $1prefix.", "apihelp-query+iwlinks-param-dir": "A direção de listagem.", - "apihelp-query+iwlinks-example-simple": "Obter os ''links'' interwikis da página Main Page.", - "apihelp-query+langbacklinks-summary": "Encontrar todas as páginas que contêm ''links'' para o ''link'' interlínguas indicado.", - "apihelp-query+langbacklinks-extended-description": "Pode ser usado para encontrar todos os ''links'' para um determinado código de língua, ou todos os ''links'' para um determinado título (de uma língua). Se nenhum for usado, isso efetivamente significa \"todos os ''links'' interlínguas\".\n\nNote que os ''links'' interlínguas adicionados por extensões podem não ser considerados.", - "apihelp-query+langbacklinks-param-lang": "A língua do ''link'' interlínguas.", - "apihelp-query+langbacklinks-param-title": "Link interlínguas a ser procurado. Tem de ser usado com $1lang.", + "apihelp-query+iwlinks-example-simple": "Obter as hiperligações interwikis da página Main Page.", + "apihelp-query+langbacklinks-summary": "Encontrar todas as páginas que contêm hiperligações para a hiperligação interlínguas indicada.", + "apihelp-query+langbacklinks-extended-description": "Pode ser usado para encontrar todas as hiperligações para um determinado código de língua, ou todas as hiperligações para um determinado título (de uma língua). Se nenhum dos parâmetros for usado, isso efetivamente significa \"todas as hiperligações interlínguas\".\n\nNote que as hiperligações interlínguas adicionadas por extensões podem não ser consideradas.", + "apihelp-query+langbacklinks-param-lang": "A língua da hiperligação da língua.", + "apihelp-query+langbacklinks-param-title": "Hiperligação interlínguas a ser procurada. Tem de ser usado com $1lang.", "apihelp-query+langbacklinks-param-limit": "O número total de páginas a serem devolvidas.", "apihelp-query+langbacklinks-param-prop": "As propriedades a serem obtidas:", - "apihelp-query+langbacklinks-paramvalue-prop-lllang": "Adiciona o código de língua da ligação interlínguas.", - "apihelp-query+langbacklinks-paramvalue-prop-lltitle": "Adiciona o título do ''link'' interlínguas.", + "apihelp-query+langbacklinks-paramvalue-prop-lllang": "Adiciona o código de língua da hiperligação interlínguas.", + "apihelp-query+langbacklinks-paramvalue-prop-lltitle": "Adiciona o título da hiperligação interlínguas.", "apihelp-query+langbacklinks-param-dir": "A direção de listagem.", - "apihelp-query+langbacklinks-example-simple": "Obter as páginas que contêm ligações para [[:fr:Test]].", - "apihelp-query+langbacklinks-example-generator": "Obter informações sobre as páginas que contêm ligações para [[:fr:Test]].", - "apihelp-query+langlinks-summary": "Devolve todos os ''links'' interlínguas das páginas indicadas.", - "apihelp-query+langlinks-param-limit": "O número de ''links'' interlínguas a serem devolvidos.", + "apihelp-query+langbacklinks-example-simple": "Obter as páginas que contêm hiperligações para [[:fr:Test]].", + "apihelp-query+langbacklinks-example-generator": "Obter informações sobre as páginas que contêm hiperligações para [[:fr:Test]].", + "apihelp-query+langlinks-summary": "Devolve todas as hiperligações interlínguas das páginas indicadas.", + "apihelp-query+langlinks-param-limit": "O número de hiperligações interlínguas a serem devolvidas.", "apihelp-query+langlinks-param-url": "Indica se deve ser obtido o URL completo (não pode ser usado com $1prop).", - "apihelp-query+langlinks-param-prop": "As propriedades adicionais que devem ser obtidas para cada ''link'' interlínguas:", + "apihelp-query+langlinks-param-prop": "As propriedades adicionais que devem ser obtidas para cada hiperligação interlínguas:", "apihelp-query+langlinks-paramvalue-prop-url": "Adiciona o URL completo.", "apihelp-query+langlinks-paramvalue-prop-langname": "Adiciona o nome da língua localizado (melhor esforço). Usar $1inlanguagecode para controlar a língua.", "apihelp-query+langlinks-paramvalue-prop-autonym": "Adiciona o nome nativo da língua.", - "apihelp-query+langlinks-param-lang": "Devolver só os ''links'' interlínguas com este código de língua.", - "apihelp-query+langlinks-param-title": "''Link'' a ser procurado. Tem de ser usado com $1lang.", + "apihelp-query+langlinks-param-lang": "Devolver só as hiperligações interlínguas com este código de língua.", + "apihelp-query+langlinks-param-title": "A hiperligação a ser procurada. Tem de ser usado com $1lang.", "apihelp-query+langlinks-param-dir": "A direção de listagem.", "apihelp-query+langlinks-param-inlanguagecode": "O código de língua para os nomes de língua localizados.", - "apihelp-query+langlinks-example-simple": "Obter os ''links'' interlínguas da página Main Page.", - "apihelp-query+links-summary": "Devolve todos os ''links'' das páginas indicadas.", - "apihelp-query+links-param-namespace": "Mostrar apenas os ''links'' destes espaços nominais.", + "apihelp-query+langlinks-example-simple": "Obter as hiperligações interlínguas da página Main Page.", + "apihelp-query+links-summary": "Devolve todas as hiperligações das páginas indicadas.", + "apihelp-query+links-param-namespace": "Mostrar apenas as hiperligações destes espaços nominais.", "apihelp-query+links-param-limit": "O número de ''links'' a serem devolvidos.", - "apihelp-query+links-param-titles": "Listar só as ligações para estes títulos. Útil para verificar se uma determinada página contém ligações para um determinado título.", + "apihelp-query+links-param-titles": "Listar só as hiperligações para estes títulos. Útil para verificar se uma determinada página contém hiperligações para um determinado título.", "apihelp-query+links-param-dir": "A direção de listagem.", "apihelp-query+links-example-simple": "Obter os ''links'' da página Main Page.", "apihelp-query+links-example-generator": "Obter informação sobre as páginas ligadas na página Main Page.", - "apihelp-query+links-example-namespaces": "Obter os ''links'' da página Main Page nos espaços nominais {{ns:user}} e {{ns:template}}.", - "apihelp-query+linkshere-summary": "Encontrar todas as páginas que contêm ''links'' para as páginas indicadas.", + "apihelp-query+links-example-namespaces": "Obter as hiperligações da página Main Page nos espaços nominais {{ns:user}} e {{ns:template}}.", + "apihelp-query+linkshere-summary": "Encontrar todas as páginas que contêm hiperligações para as páginas indicadas.", "apihelp-query+linkshere-param-prop": "As propriedades a serem obtidas:", "apihelp-query+linkshere-paramvalue-prop-pageid": "O identificador de cada página.", "apihelp-query+linkshere-paramvalue-prop-title": "O título de cada página.", @@ -895,8 +895,8 @@ "apihelp-query+linkshere-param-namespace": "Incluir só as páginas nestes espaços nominais.", "apihelp-query+linkshere-param-limit": "O número de páginas a serem devolvidas.", "apihelp-query+linkshere-param-show": "Mostrar só as páginas que correspondem a estes critérios:\n;redirect:Mostrar só os redirecionamentos.\n;!redirect:Mostrar só os não redirecionamentos.", - "apihelp-query+linkshere-example-simple": "Obter uma lista das páginas com ligações para a página [[Main Page]].", - "apihelp-query+linkshere-example-generator": "Obter informação sobre as páginas com ligações para a página [[Main Page]].", + "apihelp-query+linkshere-example-simple": "Obter uma lista das páginas com hiperligações para a página [[Main Page]].", + "apihelp-query+linkshere-example-generator": "Obter informação sobre as páginas com hiperligações para a página [[Main Page]].", "apihelp-query+logevents-summary": "Obter eventos dos registos.", "apihelp-query+logevents-param-prop": "As propriedades a serem obtidas:", "apihelp-query+logevents-paramvalue-prop-ids": "Adiciona o identificador do evento do registo.", @@ -959,7 +959,7 @@ "apihelp-query+protectedtitles-paramvalue-prop-expiry": "Adiciona a data e hora a que a proteção será removida.", "apihelp-query+protectedtitles-paramvalue-prop-level": "Adiciona o nível de proteção.", "apihelp-query+protectedtitles-example-simple": "Lista os títulos protegidos.", - "apihelp-query+protectedtitles-example-generator": "Encontrar as ligações para os títulos protegidos que pertencem ao espaço nominal principal.", + "apihelp-query+protectedtitles-example-generator": "Encontrar as hiperligações para os títulos protegidos que pertencem ao espaço nominal principal.", "apihelp-query+querypage-summary": "Obter uma lista fornecida por uma página especial baseada em consultas (''QueryPage'').", "apihelp-query+querypage-param-page": "O nome da página especial. Note que este é sensível a maiúsculas e minúsculas.", "apihelp-query+querypage-param-limit": "O número de resultados a serem devolvidos.", @@ -1070,6 +1070,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "Adiciona o título da secção correspondente.", "apihelp-query+search-paramvalue-prop-categorysnippet": "Adiciona um fragmento de código com a categoria correspondente, após análise sintática.", "apihelp-query+search-paramvalue-prop-isfilematch": "Adiciona um valor booleano que indica se a pesquisa encontrou correspondência no conteúdo de ficheiros.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Acrescenta dados adicionais gerados por extensões.", "apihelp-query+search-paramvalue-prop-score": "Ignorado.", "apihelp-query+search-paramvalue-prop-hasrelated": "Ignorado.", "apihelp-query+search-param-limit": "O número total de páginas a serem devolvidas.", @@ -1452,7 +1453,7 @@ "api-help-source": "Fonte: $1", "api-help-source-unknown": "Fonte: desconhecida", "api-help-license": "Licença: [[$1|$2]]", - "api-help-license-noname": "Licença: [[$1|Ver ligação]]", + "api-help-license-noname": "Licença: [[$1|Ver hiperligação]]", "api-help-license-unknown": "Licença: desconhecida", "api-help-parameters": "{{PLURAL:$1|Parâmetro|Parâmetros}}:", "api-help-param-deprecated": "Obsoleto.", diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json index 47afdc12b9..1724fa905b 100644 --- a/includes/api/i18n/qqq.json +++ b/includes/api/i18n/qqq.json @@ -1077,6 +1077,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "{{doc-apihelp-paramvalue|query+search|prop|sectiontitle}}", "apihelp-query+search-paramvalue-prop-categorysnippet": "{{doc-apihelp-paramvalue|query+search|prop|categorysnippet}}", "apihelp-query+search-paramvalue-prop-isfilematch": "{{doc-apihelp-paramvalue|query+search|prop|isfilematch}}", + "apihelp-query+search-paramvalue-prop-extensiondata": "{{doc-apihelp-paramvalue|query+search|prop|extensiondata}}", "apihelp-query+search-paramvalue-prop-score": "{{doc-apihelp-paramvalue|query+search|prop|score}}\n{{Identical|Ignored}}", "apihelp-query+search-paramvalue-prop-hasrelated": "{{doc-apihelp-paramvalue|query+search|prop|hasrelated}}\n{{Identical|Ignored}}", "apihelp-query+search-param-limit": "{{doc-apihelp-param|query+search|limit}}", diff --git a/includes/api/i18n/ru.json b/includes/api/i18n/ru.json index e60ce87d9a..e825170c69 100644 --- a/includes/api/i18n/ru.json +++ b/includes/api/i18n/ru.json @@ -249,10 +249,12 @@ "apihelp-imagerotate-param-tags": "Изменить метки записи в журнале загрузок.", "apihelp-imagerotate-example-simple": "Повернуть File:Example.png на 90 градусов.", "apihelp-imagerotate-example-generator": "Повернуть все изображения в Category:Flip на 180 градусов.", - "apihelp-import-summary": "Импорт страницы из другой вики, или из XML-файла.", + "apihelp-import-summary": "Импорт страницы из другой вики или XML-файла.", "apihelp-import-extended-description": "Обратите внимание, что HTTP POST-запрос должен быть осуществлён как загрузка файла (то есть, с использованием многотомных данных) при отправки файла через параметр xml.", "apihelp-import-param-summary": "Описание записи журнала импорта.", "apihelp-import-param-xml": "Загруженный XML-файл.", + "apihelp-import-param-interwikiprefix": "Для загруженных импортов: префикс интервики для неизвестных имён участников (а также известных, если задан $1assignknownusers).", + "apihelp-import-param-assignknownusers": "Связать правки с локальными участниками, когда участники с такими именами существуют.", "apihelp-import-param-interwikisource": "Для импорта из других вики: импортируемая вики.", "apihelp-import-param-interwikipage": "Для импорта из других вики: импортируемая страница.", "apihelp-import-param-fullhistory": "Для импорта из других вики: импортировать полную историю, а не только текущую страницу.", @@ -264,7 +266,7 @@ "apihelp-linkaccount-summary": "Связать аккаунт третьей стороны с текущим участником.", "apihelp-linkaccount-example-link": "Начать связывание аккаунта с Example.", "apihelp-login-summary": "Вход и получение аутентификационных cookie.", - "apihelp-login-extended-description": "Это действие должно быть использовано только в комбинации со [[Special:BotPasswords]]; использование этого модуля для входа в основной аккаунт не поддерживается и может сбиться без предупреждения. Для безопасного входа в основной аккаунт, используйте [[Special:ApiHelp/clientlogin|action=clientlogin]].", + "apihelp-login-extended-description": "Это действие должно быть использовано только в комбинации со [[Special:BotPasswords]]; использование этого модуля для входа в основной аккаунт устарело и может сбиться без предупреждения. Для безопасного входа в основной аккаунт, используйте [[Special:ApiHelp/clientlogin|action=clientlogin]].", "apihelp-login-extended-description-nobotpasswords": "Это действие не поддерживается и может сбиться без предупреждения. Для безопасного входа, используйте [[Special:ApiHelp/clientlogin|action=clientlogin]].", "apihelp-login-param-name": "Имя участника.", "apihelp-login-param-password": "Пароль.", @@ -565,12 +567,12 @@ "apihelp-query+allrevisions-param-generatetitles": "При использовании в качестве генератора, генерирует названия страниц вместо идентификаторов версий.", "apihelp-query+allrevisions-example-user": "Перечислить последние 50 правок участника Example.", "apihelp-query+allrevisions-example-ns-main": "Перечислить первые 50 правок в основном пространстве.", - "apihelp-query+mystashedfiles-summary": "Получить список файлов в тайнике (upload stash) текущего участника.", + "apihelp-query+mystashedfiles-summary": "Получить список файлов во временном хранилище текущего участника.", "apihelp-query+mystashedfiles-param-prop": "Какие свойства файлов запрашивать.", "apihelp-query+mystashedfiles-paramvalue-prop-size": "Запросить размер и разрешение изображения.", "apihelp-query+mystashedfiles-paramvalue-prop-type": "Запросить MIME- и медиа-тип файла.", "apihelp-query+mystashedfiles-param-limit": "Сколько файлов получить.", - "apihelp-query+mystashedfiles-example-simple": "Получить ключ, размер и разрешение файлов в тайнике текущего участника.", + "apihelp-query+mystashedfiles-example-simple": "Получить ключ, размер и разрешение файлов во временном хранилище текущего участника.", "apihelp-query+alltransclusions-summary": "Перечисление всех включений (страниц, вставленных с помощью {{x}}), включая несуществующие.", "apihelp-query+alltransclusions-param-from": "Название включения, с которого начать перечисление.", "apihelp-query+alltransclusions-param-to": "Название включения, на котором закончить перечисление.", @@ -712,7 +714,7 @@ "apihelp-query+deletedrevs-param-excludeuser": "Не перечислять правки данного участника.", "apihelp-query+deletedrevs-param-namespace": "Перечислять только страницы этого пространства имён.", "apihelp-query+deletedrevs-param-limit": "Максимальное количество правок в списке.", - "apihelp-query+deletedrevs-param-prop": "Какие свойства возвращать:\n;revid: Добавляет идентификатор удалённой правки.\n;parentid: Добавляет идентификатор предыдущей версии страницы.\n;user: Добавляет ник участника, сделавшего правку.\n;userid: Добавляет идентификатор участника, сделавшего правку.\n;comment: Добавляет описание правки.\n;parsedcomment: Добавляет распарсенное описание правки.\n;minor: Отмечает, была ли правка малым.\n;len: Добавляет длину (в байтах) правки.\n;sha1: Добавляет хэш SHA-1 (base 16) правки.\n;content: Добавляет содержимое правки.\n;token: Не поддерживается. Возвращает токен редактирования.\n;tags: Теги правки.", + "apihelp-query+deletedrevs-param-prop": "Какие свойства возвращать:\n;revid: Добавляет идентификатор удалённой правки.\n;parentid: Добавляет идентификатор предыдущей версии страницы.\n;user: Добавляет ник участника, сделавшего правку.\n;userid: Добавляет идентификатор участника, сделавшего правку.\n;comment: Добавляет описание правки.\n;parsedcomment: Добавляет распарсенное описание правки.\n;minor: Отмечает, была ли правка малым.\n;len: Добавляет длину (в байтах) правки.\n;sha1: Добавляет хэш SHA-1 (base 16) правки.\n;content: Добавляет содержимое правки.\n;token: Устарело. Возвращает токен редактирования.\n;tags: Теги правки.", "apihelp-query+deletedrevs-example-mode1": "Список последних удалённых правок страниц Main Page и Talk:Main Page с содержимым (режим 1).", "apihelp-query+deletedrevs-example-mode2": "Список последних 50 удалённых правок участника Bob (режим 2).", "apihelp-query+deletedrevs-example-mode3-main": "Список последних 50 удалённых правок в основном пространстве имён (режим 3)", @@ -1060,7 +1062,7 @@ "apihelp-query+revisions+base-paramvalue-prop-parsedcomment": "Распарсенное описание правки.", "apihelp-query+revisions+base-paramvalue-prop-content": "Текст версии.", "apihelp-query+revisions+base-paramvalue-prop-tags": "Метки версии.", - "apihelp-query+revisions+base-paramvalue-prop-parsetree": "Не поддерживается. Вместо этого используйте [[Special:ApiHelp/expandtemplates|action=expandtemplates]] или [[Special:ApiHelp/parse|action=parse]]. Дерево парсинга XML содержимого версии (требуется модель содержимого $1).", + "apihelp-query+revisions+base-paramvalue-prop-parsetree": "Устарело. Вместо этого используйте [[Special:ApiHelp/expandtemplates|action=expandtemplates]] или [[Special:ApiHelp/parse|action=parse]]. Дерево парсинга XML содержимого версии (требуется модель содержимого $1).", "apihelp-query+revisions+base-param-limit": "Сколько версий вернуть.", "apihelp-query+revisions+base-param-expandtemplates": "Вместо этого используйте [[Special:ApiHelp/expandtemplates|action=expandtemplates]]. Раскрыть шаблоны в содержимом версии (требуется $1prop=content).", "apihelp-query+revisions+base-param-generatexml": "Вместо этого используйте [[Special:ApiHelp/expandtemplates|action=expandtemplates]] или [[Special:ApiHelp/parse|action=parse]]. Сгенерировать дерево парсинга XML содержимого версии (требуется $1prop=content).", @@ -1088,6 +1090,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "Добавляет заголовок найденного раздела.", "apihelp-query+search-paramvalue-prop-categorysnippet": "Добавляет распарсенный фрагмент найденной категории.", "apihelp-query+search-paramvalue-prop-isfilematch": "Добавляет логическое значение, обозначающее, удовлетворяет ли поисковому запросу содержимое файла.", + "apihelp-query+search-paramvalue-prop-extensiondata": "Добавляет дополнительные данные, сгенерированные расширениями.", "apihelp-query+search-paramvalue-prop-score": "Игнорируется.", "apihelp-query+search-paramvalue-prop-hasrelated": "Игнорируется.", "apihelp-query+search-param-limit": "Сколько страниц вернуть.", @@ -1130,10 +1133,10 @@ "apihelp-query+siteinfo-example-simple": "Запросить информацию о сайте.", "apihelp-query+siteinfo-example-interwiki": "Запросить список локальных префиксов интервик.", "apihelp-query+siteinfo-example-replag": "Проверить текущее отставание репликации.", - "apihelp-query+stashimageinfo-summary": "Возвращает информацию о файлах в тайнике (upload stash).", + "apihelp-query+stashimageinfo-summary": "Возвращает информацию о файлах во временном хранилище.", "apihelp-query+stashimageinfo-param-filekey": "Ключ, идентифицирующий предыдущую временную загрузку.", "apihelp-query+stashimageinfo-param-sessionkey": "Синоним $1filekey для обратной совместимости.", - "apihelp-query+stashimageinfo-example-simple": "Вернуть информацию о файле в тайнике.", + "apihelp-query+stashimageinfo-example-simple": "Вернуть информацию о файле во временном хранилище.", "apihelp-query+stashimageinfo-example-params": "Вернуть эскизы двух файлов в тайнике.", "apihelp-query+tags-summary": "Список меток правок.", "apihelp-query+tags-param-limit": "Максимальное количество меток в списке.", @@ -1354,7 +1357,7 @@ "apihelp-tag-example-rev": "Добавить метку vandalism к версии с идентификатором 123 без указания причины.", "apihelp-tag-example-log": "Удаление метки spam из записи журнала с идентификатором 123 с причиной Wrongly applied.", "apihelp-tokens-summary": "Получение токенов для действий, связанных с редактированием данных.", - "apihelp-tokens-extended-description": "Этот модуль не поддерживается в пользу [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].", + "apihelp-tokens-extended-description": "Этот модуль устарел в пользу [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].", "apihelp-tokens-param-type": "Типы запрашиваемых токенов.", "apihelp-tokens-example-edit": "Получить токен редактирования (по умолчанию).", "apihelp-tokens-example-emailmove": "Получить токен электронной почты и переименования.", @@ -1391,7 +1394,7 @@ "apihelp-upload-param-url": "Ссылка на запрашиваемый файл.", "apihelp-upload-param-filekey": "Ключ, идентифицирующий предыдущую временную загрузку.", "apihelp-upload-param-sessionkey": "Синоним $1filekey, обслуживаемый для обратной совместимости.", - "apihelp-upload-param-stash": "Если задано, сервер временно поместит файл в тайник вместо загрузки его в хранилище.", + "apihelp-upload-param-stash": "Если задано, сервер поместит файл во временное хранилище, не добавив в постоянное.", "apihelp-upload-param-filesize": "Полны размер файла.", "apihelp-upload-param-offset": "Смещение блока в байтах.", "apihelp-upload-param-chunk": "Содержимое кусочка.", @@ -1461,7 +1464,7 @@ "api-help-lead": "Это автоматически сгенерированная страница документации MediaWiki API.\n\nДокументация и примеры: https://www.mediawiki.org/wiki/API", "api-help-main-header": "Главный модуль", "api-help-undocumented-module": "Нет документации для модуля $1.", - "api-help-flag-deprecated": "Этот модуль не поддерживается.", + "api-help-flag-deprecated": "Этот модуль устарел.", "api-help-flag-internal": "Этот модуль внутренний или нестабильный. Его операции могут измениться без предупреждения.", "api-help-flag-readrights": "Этот модуль требует прав на чтение.", "api-help-flag-writerights": "Этот модуль требует прав на запись.", @@ -1473,7 +1476,7 @@ "api-help-license-noname": "Лицензия: [[$1|см. ссылку]]", "api-help-license-unknown": "Лицензия: unknown", "api-help-parameters": "Параметр{{PLURAL:$1||ы}}:", - "api-help-param-deprecated": "Не поддерживается.", + "api-help-param-deprecated": "Устарело.", "api-help-param-required": "Это обязательный параметр.", "api-help-datatypes-header": "Типы данных", "api-help-datatypes": "Ввод в MediaWiki должен быть NFC-нормализованным UTF-8. MediaWiki может попытаться преобразовать другой ввод, но это приведёт к провалу некоторых операций (таких, как [[Special:ApiHelp/edit|редактирование]] со сверкой MD5).\n\nНекоторые типы параметров в запросах API требуют дополнительных пояснений:\n;логический\n:Логические параметры работают как флажки (checkboxes) в HTML: если параметр задан, независимо от его значения, он воспринимается за истину. Для передачи ложного значения просто опустите параметр.\n;временные метки\n:Временные метки могут быть заданы в нескольких форматах. Рекомендуемым является дата и время ISO 8601. Всё время считается в UTC, любые включённые часовые пояса игнорируются.\n:* Дата и время ISO 8601: 2001-01-15T14:56:00Z (знаки препинания и Z необязательны)\n:* Дата и время ISO 8601 с (игнорируемой) дробной частью секунд: 2001-01-15T14:56:00.00001Z (дефисы, двоеточия и Z необязательны)\n:* Формат MediaWiki: 20010115145600\n:* Общий числовой формат: 2001-01-15 14:56:00 (необязательный часовой пояс GMT, +## или -## игнорируется)\n:* Формат EXIF: 2001:01:15 14:56:00\n:* Формат RFC 2822 (часовой пояс может быть опущен): Mon, 15 Jan 2001 14:56:00\n:* Формат RFC 850 (часовой пояс может быть опущен): Monday, 15-Jan-2001 14:56:00\n:* Формат ctime языка программирования C: Mon Jan 15 14:56:00 2001\n:* Количество секунд, прошедших с 1970-01-01T00:00:00Z, в виде челого числа с от 1 до 13 знаками (исключая 0)\n:* Строка now\n;альтернативный разделитель значений\n:Параметры, принимающие несколько значений, обычно отправляются со значениями, разделёнными с помощью символа пайпа, например, param=value1|value2 или param=value1%7Cvalue2. Если значение должно содержать символ пайпа, используйте U+001F (Unit Separator) в качестве разделителя ''и'' добавьте в начало значения U+001F, например, param=%1Fvalue1%1Fvalue2.", @@ -1503,6 +1506,8 @@ "api-help-param-direction": "В каком порядке перечислять:\n;newer: Начать с самых старых. Обратите внимание: $1start должно быть раньше $1end.\n;older: Начать с самых новых (по умолчанию). Обратите внимание: $1start должно быть позже $1end.", "api-help-param-continue": "Когда доступно больше результатов, используйте это для продолжения.", "api-help-param-no-description": "(описание отсутствует)", + "api-help-param-maxbytes": "Не может быть длиннее $1 {{PLURAL:$1|байта|байтов}}.", + "api-help-param-maxchars": "Не может быть длиннее $1 {{PLURAL:$1|символа|символов}}.", "api-help-examples": "Пример{{PLURAL:$1||ы}}:", "api-help-permissions": "{{PLURAL:$1|Разрешение|Разрешения}}:", "api-help-permissions-granted-to": "{{PLURAL:$1|Гарантируется}}: $2", @@ -1549,7 +1554,7 @@ "apierror-blockedfrommail": "Отправка электронной почты была для вас заблокирована.", "apierror-blocked": "Редактирование было для вас заблокировано.", "apierror-botsnotsupported": "Этот интерфейс не поддерживается для ботов.", - "apierror-cannot-async-upload-file": "Параметры async и file не могут применяться вместе. Если вы хотите ассинхронно обработать загруженный файл, сначала загрузите его в тайник (используя параметр stash), а затем опубликуйте этот файл ассинхронно (используя параметры filekey и async).", + "apierror-cannot-async-upload-file": "Параметры async и file не могут применяться вместе. Если вы хотите ассинхронно обработать загруженный файл, сначала загрузите его во временное хранилище (используя параметр stash), а затем опубликуйте этот файл ассинхронно (используя параметры filekey и async).", "apierror-cannotreauthenticate": "Это действие недоступно, так как ваша личность не может быть подтверждена.", "apierror-cannotviewtitle": "У вас нет прав на просмотр $1.", "apierror-cantblock-email": "У вас нет прав блокировать участникам отправку электронной почты через интерфейс вики.", @@ -1605,6 +1610,8 @@ "apierror-invalidurlparam": "Некорректное значение $1urlparam ($2=$3).", "apierror-invaliduser": "Некорректное имя участника «$1».", "apierror-invaliduserid": "Некорректный идентификатор участника $1.", + "apierror-maxbytes": "Параметр $1 не может быть длиннее $2 {{PLURAL:$2|байта|байтов}}", + "apierror-maxchars": "Параметр $1 не может быть длиннее $2 {{PLURAL:$2|символа|символов}}", "apierror-maxlag-generic": "Ожидание сервера базы данных: $1 {{PLURAL:$1|секунда|секунды|секунд}} задержки.", "apierror-maxlag": "Ожидание $2: $1 {{PLURAL:$1|секунда|секунды|секунд}} задержки.", "apierror-mimesearchdisabled": "Поиск по MIME отключен в жадном режиме.", @@ -1626,7 +1633,7 @@ "apierror-mustbeloggedin-generic": "Вы должны быть авторизованы.", "apierror-mustbeloggedin-linkaccounts": "Вы должны быть авторизованы для привязывания аккаунтов.", "apierror-mustbeloggedin-removeauth": "Вы должны быть авторизованы для удаления аутентификационных данных.", - "apierror-mustbeloggedin-uploadstash": "Тайник загрузки (upload stash) доступен только для авторизованных участников.", + "apierror-mustbeloggedin-uploadstash": "Временное хранилище доступно только для авторизованных участников.", "apierror-mustbeloggedin": "Вы должны быть авторизованы в $1.", "apierror-mustbeposted": "Модуль $1 требует запроса POST.", "apierror-mustpostparams": "{{PLURAL:$2|Следующий параметр был найден|Следующие параметры были найдены}} в строке запроса, но {{PLURAL:$2|должен|должны}} находиться в теле POST: $1.", @@ -1686,16 +1693,16 @@ "apierror-sizediffdisabled": "Подсчёт разницы размеров отключён в жадном режиме.", "apierror-spamdetected": "Ваша правка была отклонена, так как содержит спам: $1.", "apierror-specialpage-cantexecute": "У вас нет прав, чтобы просматривать результаты этой служебной страницы.", - "apierror-stashedfilenotfound": "Невозможно найти файл в тайнике: $1.", + "apierror-stashedfilenotfound": "Невозможно найти файл во временном хранилище: $1.", "apierror-stashedit-missingtext": "Не найдено содержимого тайника для данного хэша.", "apierror-stashfailed-complete": "Загрузка по кусочкам уже завершена, проверьте статус для получения подробной информации.", "apierror-stashfailed-nosession": "Не найдено сессии загрузки по кусочкам с заданным ключом.", - "apierror-stashfilestorage": "Невозможно сохранить загрузку в тайник: $1", + "apierror-stashfilestorage": "Невозможно сохранить файл во временном хранилище: $1", "apierror-stashinvalidfile": "Некорректный файл в тайнике.", "apierror-stashnosuchfilekey": "Нет такого ключа файла: $1.", "apierror-stashpathinvalid": "Ключ файла относится к некорректному формату или сам некорректен: $1.", "apierror-stashwrongowner": "Некорректный владелец: $1", - "apierror-stashzerolength": "Файл имеет нулевую длину и не может быть сохранён в тайник: $1", + "apierror-stashzerolength": "Файл имеет нулевую длину и не может быть сохранён во временное хранилище: $1", "apierror-systemblocked": "Вы были заблокированы автоматически MediaWiki.", "apierror-templateexpansion-notwikitext": "Раскрытие шаблонов разрешено только для вики-текстового содержимого. $1 использует модель содержимого $2.", "apierror-timeout": "Сервер не ответил за ожидаемое время.", @@ -1710,7 +1717,7 @@ "apierror-unsupportedrepo": "Локальное хранилище файлов не поддерживает запрос всех изображений.", "apierror-upload-filekeyneeded": "Необходимо задать filekey, если offset не ноль.", "apierror-upload-filekeynotallowed": "Невозможно обработать filekey, если offset равен 0.", - "apierror-upload-inprogress": "Процесс загрузки из тайника уже запущен.", + "apierror-upload-inprogress": "Процесс загрузки из временного хранилища уже запущен.", "apierror-upload-missingresult": "Нет результатов данных статуса.", "apierror-urlparamnormal": "Невозможно нормализовать параметры изображения для $1.", "apierror-writeapidenied": "У вас нет прав на редактирование этой вики через API.", @@ -1719,15 +1726,15 @@ "apiwarn-badutf8": "Значение, переданное $1, содержит некорректные или ненормализованные данные. Текстовые данные должны быть корректным NFC-нормализованным Юникодом без символов управления C0, кроме HT (\\t), LF (\\n) и CR (\\r).", "apiwarn-checktoken-percentencoding": "Проверьте, что символы вроде «+» в токене корректно закодированы %-последовательностями в ссылке.", "apiwarn-compare-nocontentmodel": "Модель содержимого не может быть определена, предполагается $1.", - "apiwarn-deprecation-deletedrevs": "list=deletedrevs не поддерживается. Пожалуйста, вместо него используйте prop=deletedrevisions или list=alldeletedrevisions.", + "apiwarn-deprecation-deletedrevs": "list=deletedrevs устарел. Пожалуйста, вместо него используйте prop=deletedrevisions или list=alldeletedrevisions.", "apiwarn-deprecation-expandtemplates-prop": "Поскольку никакие значения не были указаны в параметре prop, был использован наследованный формат. Этот формат является устаревшим, и в будущем параметру prop будет присвоено значение по умолчанию, что приведёт к повсеместному использованию нового формата.", "apiwarn-deprecation-httpsexpected": "Использован HTTP, где ожидался HTTPS.", - "apiwarn-deprecation-login-botpw": "Вход в основной аккаунт через action=login не поддерживается и может быть отключен без предупреждения. Для продолжения авторизации с action=login, см.\n[[Special:BotPasswords]]. Для безопасного продолжения использования входа в основной аккаунт, см. action=clientlogin.", + "apiwarn-deprecation-login-botpw": "Вход в основной аккаунт через action=login устарел и может быть отключен без предупреждения. Для продолжения авторизации с action=login, см.\n[[Special:BotPasswords]]. Для безопасного продолжения использования входа в основной аккаунт, см. action=clientlogin.", "apiwarn-deprecation-login-nobotpw": "Вход в основной аккаунт через action=login не поддерживается и может быть отключен без предупреждения. Для безопасной авторизации, см. action=clientlogin.", - "apiwarn-deprecation-login-token": "Запрос токена через action=login не поддерживается. Вместо этого, см. action=query&meta=tokens&type=login.", + "apiwarn-deprecation-login-token": "Запрос токена через action=login устарел. Используйте action=query&meta=tokens&type=login.", "apiwarn-deprecation-parameter": "Параметр $1 не поддерживается.", - "apiwarn-deprecation-parse-headitems": "prop=headitems не поддерживается с MediaWiki 1.28. Используйте prop=headhtml при создании новых HTML документов, или prop=modules|jsconfigvars при обновлении документов на стороне клиента.", - "apiwarn-deprecation-purge-get": "Использование action=purge посредством GET не поддерживается. Используйте POST.", + "apiwarn-deprecation-parse-headitems": "prop=headitems устарело с MediaWiki 1.28. Используйте prop=headhtml при создании новых HTML документов или prop=modules|jsconfigvars при обновлении документов на стороне клиента.", + "apiwarn-deprecation-purge-get": "Использование action=purge посредством GET устарело. Используйте POST.", "apiwarn-deprecation-withreplacement": "$1 не поддерживается. Пожалуйста, используйте $2.", "apiwarn-difftohidden": "Невозможно сравнить с r$1: содержимое скрыто.", "apiwarn-errorprinterfailed": "Сборщик ошибок упал. Будет совершена повторная попытка без параметров.", @@ -1748,7 +1755,7 @@ "apiwarn-tokens-origin": "Токены не могут быть получены, пока не применено правило ограничения домена.", "apiwarn-toomanyvalues": "Слишком много значений передано параметру $1. Максимальное число — $2.", "apiwarn-truncatedresult": "Результат был усечён, поскольку в противном случае он был бы больше лимита в $1 {{PLURAL:$1|байт|байта|байт}}.", - "apiwarn-unclearnowtimestamp": "Передача «$2» в качестве параметра временной метки $1 не поддерживается. Если по какой-то причине вы хотите прямо указать текущее время без вычисления его на стороне клиента, используйте now.", + "apiwarn-unclearnowtimestamp": "Передача «$2» в качестве параметра временной метки $1 устарело. Если по какой-то причине вы хотите прямо указать текущее время без вычисления его на стороне клиента, используйте now.", "apiwarn-unrecognizedvalues": "{{PLURAL:$3|Нераспознанное значение|Нераспознанные значения}} параметра $1: $2.", "apiwarn-unsupportedarray": "Параметр $1 использует неподдерживаемый синтаксис массивов PHP.", "apiwarn-urlparamwidth": "Значение ширины ($2), переданное в $1urlparam, было проигнорировано в пользу значения ($3), полученного из параметров $1urlwidth/$1urlheight.", diff --git a/includes/api/i18n/zh-hans.json b/includes/api/i18n/zh-hans.json index 2fb6178b5d..3f159165d7 100644 --- a/includes/api/i18n/zh-hans.json +++ b/includes/api/i18n/zh-hans.json @@ -1086,6 +1086,7 @@ "apihelp-query+search-paramvalue-prop-sectiontitle": "添加匹配章节的标题。", "apihelp-query+search-paramvalue-prop-categorysnippet": "添加已解析的匹配分类片段。", "apihelp-query+search-paramvalue-prop-isfilematch": "添加布尔值,表明搜索是否匹配文件内容。", + "apihelp-query+search-paramvalue-prop-extensiondata": "添加由扩展生成的额外数据。", "apihelp-query+search-paramvalue-prop-score": "已忽略。", "apihelp-query+search-paramvalue-prop-hasrelated": "已忽略。", "apihelp-query+search-param-limit": "返回的总计页面数。", diff --git a/includes/cache/MessageCache.php b/includes/cache/MessageCache.php index 768f980b26..d6e9b748ab 100644 --- a/includes/cache/MessageCache.php +++ b/includes/cache/MessageCache.php @@ -1048,8 +1048,7 @@ class MessageCache { if ( $titleObj->getLatestRevID() ) { $revision = Revision::newKnownCurrent( $dbr, - $titleObj->getArticleID(), - $titleObj->getLatestRevID() + $titleObj ); } else { $revision = false; diff --git a/includes/cache/localisation/LCStoreStaticArray.php b/includes/cache/localisation/LCStoreStaticArray.php index 1e20082f16..602c0ac463 100644 --- a/includes/cache/localisation/LCStoreStaticArray.php +++ b/includes/cache/localisation/LCStoreStaticArray.php @@ -97,17 +97,17 @@ class LCStoreStaticArray implements LCStore { $data = $encoded[1]; switch ( $type ) { - case 'v': - return $data; - case 's': - return unserialize( $data ); - case 'a': - return array_map( function ( $v ) { - return LCStoreStaticArray::decode( $v ); - }, $data ); - default: - throw new RuntimeException( - 'Unable to decode ' . var_export( $encoded, true ) ); + case 'v': + return $data; + case 's': + return unserialize( $data ); + case 'a': + return array_map( function ( $v ) { + return LCStoreStaticArray::decode( $v ); + }, $data ); + default: + throw new RuntimeException( + 'Unable to decode ' . var_export( $encoded, true ) ); } } diff --git a/includes/changes/ChangesListFilter.php b/includes/changes/ChangesListFilter.php index 2546f2ba82..1c86d44174 100644 --- a/includes/changes/ChangesListFilter.php +++ b/includes/changes/ChangesListFilter.php @@ -468,7 +468,7 @@ abstract class ChangesListFilter { * @param FormOptions $opts * @return bool */ - public function activelyInConflictWithFilter( ChangeslistFilter $filter, FormOptions $opts ) { + public function activelyInConflictWithFilter( ChangesListFilter $filter, FormOptions $opts ) { if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) { /** @var ChangesListFilter $siblingFilter */ foreach ( $this->getSiblings() as $siblingFilter ) { @@ -484,7 +484,7 @@ abstract class ChangesListFilter { return false; } - private function hasConflictWithFilter( ChangeslistFilter $filter ) { + private function hasConflictWithFilter( ChangesListFilter $filter ) { return in_array( $filter, $this->getConflictingFilters() ); } diff --git a/includes/changetags/ChangeTags.php b/includes/changetags/ChangeTags.php index b4a8ca8028..db1f599087 100644 --- a/includes/changetags/ChangeTags.php +++ b/includes/changetags/ChangeTags.php @@ -39,7 +39,8 @@ class ChangeTags { 'mw-changed-redirect-target', 'mw-blank', 'mw-replace', - 'mw-rollback' + 'mw-rollback', + 'mw-undo', ]; /** diff --git a/includes/clientpool/SquidPurgeClient.php b/includes/clientpool/SquidPurgeClient.php index f454bd4c7c..be802f9bec 100644 --- a/includes/clientpool/SquidPurgeClient.php +++ b/includes/clientpool/SquidPurgeClient.php @@ -304,40 +304,40 @@ class SquidPurgeClient { */ protected function processReadBuffer() { switch ( $this->readState ) { - case 'idle': - return 'done'; - case 'status': - case 'header': - $lines = explode( "\r\n", $this->readBuffer, 2 ); - if ( count( $lines ) < 2 ) { + case 'idle': return 'done'; - } - if ( $this->readState == 'status' ) { - $this->processStatusLine( $lines[0] ); - } else { // header - $this->processHeaderLine( $lines[0] ); - } - $this->readBuffer = $lines[1]; - return 'continue'; - case 'body': - if ( $this->bodyRemaining !== null ) { - if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) { - $this->bodyRemaining -= strlen( $this->readBuffer ); - $this->readBuffer = ''; + case 'status': + case 'header': + $lines = explode( "\r\n", $this->readBuffer, 2 ); + if ( count( $lines ) < 2 ) { return 'done'; + } + if ( $this->readState == 'status' ) { + $this->processStatusLine( $lines[0] ); + } else { // header + $this->processHeaderLine( $lines[0] ); + } + $this->readBuffer = $lines[1]; + return 'continue'; + case 'body': + if ( $this->bodyRemaining !== null ) { + if ( $this->bodyRemaining > strlen( $this->readBuffer ) ) { + $this->bodyRemaining -= strlen( $this->readBuffer ); + $this->readBuffer = ''; + return 'done'; + } else { + $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining ); + $this->bodyRemaining = 0; + $this->nextRequest(); + return 'continue'; + } } else { - $this->readBuffer = substr( $this->readBuffer, $this->bodyRemaining ); - $this->bodyRemaining = 0; - $this->nextRequest(); - return 'continue'; + // No content length, read all data to EOF + $this->readBuffer = ''; + return 'done'; } - } else { - // No content length, read all data to EOF - $this->readBuffer = ''; - return 'done'; - } - default: - throw new MWException( __METHOD__ . ': unexpected state' ); + default: + throw new MWException( __METHOD__ . ': unexpected state' ); } } diff --git a/includes/collation/Collation.php b/includes/collation/Collation.php index d009168d5e..7171a218ae 100644 --- a/includes/collation/Collation.php +++ b/includes/collation/Collation.php @@ -67,6 +67,8 @@ abstract class Collation { return new CollationFa; case 'uppercase-ba': return new BashkirUppercaseCollation; + case 'uppercase-se': + return new NorthernSamiUppercaseCollation; default: $match = []; if ( preg_match( '/^uca-([A-Za-z@=-]+)$/', $collationName, $match ) ) { diff --git a/includes/collation/NorthernSamiUppercaseCollation.php b/includes/collation/NorthernSamiUppercaseCollation.php new file mode 100644 index 0000000000..d373749e54 --- /dev/null +++ b/includes/collation/NorthernSamiUppercaseCollation.php @@ -0,0 +1,83 @@ +getSize() > 0 ) { - return 'newpage'; - } - - // New blank page - if ( $flags & EDIT_NEW && $newContent && $newContent->getSize() === 0 ) { - return 'newblank'; + if ( $flags & EDIT_NEW && $newContent ) { + if ( $newContent->getSize() === 0 ) { + // New blank page + return 'newblank'; + } else { + return 'newpage'; + } } // Removing more than 90% of the page diff --git a/includes/editpage/TextConflictHelper.php b/includes/editpage/TextConflictHelper.php index b1eaa4be9b..6e7e7ee6ee 100644 --- a/includes/editpage/TextConflictHelper.php +++ b/includes/editpage/TextConflictHelper.php @@ -140,6 +140,15 @@ class TextConflictHelper { */ public function incrementResolvedStats() { $this->stats->increment( 'edit.failures.conflict.resolved' ); + // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics + if ( + $this->title->getNamespace() >= NS_MAIN && + $this->title->getNamespace() <= NS_CATEGORY_TALK + ) { + $this->stats->increment( + 'edit.failures.conflict.resolved.byNamespaceId.' . $this->title->getNamespace() + ); + } } /** diff --git a/includes/export/ExportProgressFilter.php b/includes/export/ExportProgressFilter.php new file mode 100644 index 0000000000..9b1571f7de --- /dev/null +++ b/includes/export/ExportProgressFilter.php @@ -0,0 +1,47 @@ + + * https://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * @ingroup Dump + */ +class ExportProgressFilter extends DumpFilter { + /** + * @var BackupDumper + */ + private $progress; + + function __construct( &$sink, &$progress ) { + parent::__construct( $sink ); + $this->progress = $progress; + } + + function writeClosePage( $string ) { + parent::writeClosePage( $string ); + $this->progress->reportPage(); + } + + function writeRevision( $rev, $string ) { + parent::writeRevision( $rev, $string ); + $this->progress->revCount(); + } +} diff --git a/includes/htmlform/fields/HTMLCheckMatrix.php b/includes/htmlform/fields/HTMLCheckMatrix.php index dd4e707ee5..df44626a3d 100644 --- a/includes/htmlform/fields/HTMLCheckMatrix.php +++ b/includes/htmlform/fields/HTMLCheckMatrix.php @@ -106,6 +106,7 @@ class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable { $tooltipAttribs = [ 'class' => "mw-htmlform-tooltip $tooltipClass", 'title' => $this->mParams['tooltips'][$rowLabel], + 'aria-label' => $this->mParams['tooltips'][$rowLabel] ]; $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' ); } diff --git a/includes/htmlform/fields/HTMLUsersMultiselectField.php b/includes/htmlform/fields/HTMLUsersMultiselectField.php index f094745f5d..46cc6d3116 100644 --- a/includes/htmlform/fields/HTMLUsersMultiselectField.php +++ b/includes/htmlform/fields/HTMLUsersMultiselectField.php @@ -56,6 +56,14 @@ class HTMLUsersMultiselectField extends HTMLUserTextField { public function getInputOOUI( $value ) { $params = [ 'name' => $this->mName ]; + if ( isset( $this->mParams['id'] ) ) { + $params['id'] = $this->mParams['id']; + } + + if ( isset( $this->mParams['disabled'] ) ) { + $params['disabled'] = $this->mParams['disabled']; + } + if ( isset( $this->mParams['default'] ) ) { $params['default'] = $this->mParams['default']; } diff --git a/includes/import/WikiImporter.php b/includes/import/WikiImporter.php index 1424f33637..ed5ec1a2c2 100644 --- a/includes/import/WikiImporter.php +++ b/includes/import/WikiImporter.php @@ -125,7 +125,9 @@ class WikiImporter { if ( is_callable( $this->mNoticeCallback ) ) { call_user_func( $this->mNoticeCallback, $msg, $params ); } else { # No ImportReporter -> CLI - echo wfMessage( $msg, $params )->text() . "\n"; + // T177997: the command line importers should call setNoticeCallback() + // for their own custom callback to echo the notice + wfDebug( wfMessage( $msg, $params )->text() . "\n" ); } } @@ -543,13 +545,13 @@ class WikiImporter { $buffer = ""; while ( $this->reader->read() ) { switch ( $this->reader->nodeType ) { - case XMLReader::TEXT: - case XMLReader::CDATA: - case XMLReader::SIGNIFICANT_WHITESPACE: - $buffer .= $this->reader->value; - break; - case XMLReader::END_ELEMENT: - return $buffer; + case XMLReader::TEXT: + case XMLReader::CDATA: + case XMLReader::SIGNIFICANT_WHITESPACE: + $buffer .= $this->reader->value; + break; + case XMLReader::END_ELEMENT: + return $buffer; } } diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php index 46978e1ba1..2bb15b8ce0 100644 --- a/includes/installer/Installer.php +++ b/includes/installer/Installer.php @@ -693,10 +693,6 @@ abstract class Installer { ] ); } catch ( MediaWiki\Services\ServiceDisabledException $e ) { $html = ' ' . htmlspecialchars( $text ); - - if ( !empty( $this->debug ) ) { - $html .= ""; - } } return $html; diff --git a/includes/installer/PostgresUpdater.php b/includes/installer/PostgresUpdater.php index c38eb6aabc..393c2e1641 100644 --- a/includes/installer/PostgresUpdater.php +++ b/includes/installer/PostgresUpdater.php @@ -657,6 +657,13 @@ END; } } + protected function dropSequence( $table, $ns ) { + if ( $this->db->sequenceExists( $ns ) ) { + $this->output( "Dropping sequence $ns\n" ); + $this->db->query( "DROP SEQUENCE $ns CASCADE" ); + } + } + protected function renameSequence( $old, $new ) { if ( $this->db->sequenceExists( $new ) ) { $this->output( "...sequence $new already exists.\n" ); diff --git a/includes/installer/i18n/ca.json b/includes/installer/i18n/ca.json index c86b824447..54a7de8cb3 100644 --- a/includes/installer/i18n/ca.json +++ b/includes/installer/i18n/ca.json @@ -257,6 +257,8 @@ "config-help-tooltip": "feu clic per ampliar", "config-nofile": "No s'ha pogut trobar el fitxer «$1». S'ha suprimit?", "config-extension-link": "Sabíeu que el vostre wiki permet l'ús d'[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions extensions]?\n\nPodeu navegar les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions per categoria] o la [https://www.mediawiki.org/wiki/Extension_Matrix matriu d'extensions] per a veure'n una llista sencera.", + "config-skins-screenshots": "$1 (captures de pantalla: $2)", + "config-screenshot": "captura de pantalla", "mainpagetext": "MediaWiki s'ha instal·lat.", "mainpagedocfooter": "Consulteu la [https://meta.wikimedia.org/wiki/Help:Contents Guia d'Usuari] per a més informació sobre com utilitzar aquest programari wiki.\n\n== Primers passos ==\n\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Llista de paràmetres configurables]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ PMF del MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Llista de correu per a anuncis del MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Traducció de MediaWiki en la vostra llengua]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Aprengueu com combatre la brossa que pot atacar el vostre wiki]" } diff --git a/includes/installer/i18n/cs.json b/includes/installer/i18n/cs.json index 2ff49fcbf8..5ac35c89ce 100644 --- a/includes/installer/i18n/cs.json +++ b/includes/installer/i18n/cs.json @@ -314,6 +314,7 @@ "config-install-mainpage-failed": "Nepodařilo se vložit hlavní stranu: $1", "config-install-done": "Gratulujeme!\nNainstalovali jste MediaWiki.\n\nInstalátor vytvořil soubor LocalSettings.php.\nTen obsahuje veškerou vaši konfiguraci.\n\nBudete si ho muset stáhnout a uložit do základního adresáře vaší instalace wiki (do stejného adresáře jako soubor index.php). Stažení souboru se mělo spustit automaticky.\n\nPokud se vám stažení nenabídlo nebo jste ho zrušili, můžete ho spustit znovu kliknutím na následující odkaz:\n\n$3\n\nPoznámka: Pokud to neuděláte hned, tento vygenerovaný konfigurační soubor nebude později dostupný, pokud instalaci opustíte, aniž byste si ho stáhli.\n\nAž to dokončíte, můžete [$2 vstoupit do své wiki].", "config-install-done-path": "Gratulujeme!\nNainstalovali jste MediaWiki.\n\nInstalátor vytvořil soubor LocalSettings.php.\nTen obsahuje veškerou vaši konfiguraci.\n\nBudete si ho muset stáhnout a uložit do $4. Stažení souboru se mělo spustit automaticky.\n\nPokud se vám stažení nenabídlo nebo jste ho zrušili, můžete ho spustit znovu kliknutím na následující odkaz:\n\n$3\n\nPoznámka: Pokud to neuděláte hned, tento vygenerovaný konfigurační soubor nebude později dostupný, pokud instalaci opustíte, aniž byste si ho stáhli.\n\nAž to dokončíte, můžete [$2 vstoupit do své wiki].", + "config-install-success": "MediaWiki byla úspěšně nainstalována. Nyní můžete\nnavštívit <$1$2>, abyste si prohlédli svou novou wiki.\nPokud máte dotazy, podívejte se do našeho seznamu často kladených otázek:\n nebo použijte jedno\nz tam odkazovaných diskusních fór.", "config-download-localsettings": "Stáhnout LocalSettings.php", "config-help": "nápověda", "config-help-tooltip": "rozbalíte kliknutím", diff --git a/includes/installer/i18n/es.json b/includes/installer/i18n/es.json index d7ed72b870..62237a4026 100644 --- a/includes/installer/i18n/es.json +++ b/includes/installer/i18n/es.json @@ -267,7 +267,7 @@ "config-email-auth-help": "Si esta opción está habilitada, los usuarios tienen que confirmar su dirección de correo electrónico mediante un enlace que se les envía a ellos cuando éstos lo establecen o lo cambian.\nSolo las direcciones de correo electrónico autenticadas pueden recibir correos electrónicos de otros usuarios o correos electrónicos de notificación de cambios.\nEsta opción está '''recomendada''' para wikis públicos debido a posibles abusos de las características del correo electrónico.", "config-email-sender": "Dirección de correo electrónico de retorno:", "config-email-sender-help": "Escribe la dirección de correo electrónico que se usará como dirección de retorno en los mensajes electrónicos de salida.\nAquí llegarán los correos electrónicos que no lleguen a su destino.\nMuchos servidores de correo electrónico exigen que por lo menos la parte del nombre del dominio sea válida.", - "config-upload-settings": "Subidas de imágenes y archivos", + "config-upload-settings": "Cargas de imágenes y archivos", "config-upload-enable": "Habilitar la subida de archivos", "config-upload-help": "La subida de archivos potencialmente expone tu servidor a riesgos de seguridad.\nPara obtener más información, consulta la [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security sección de seguridad] en el manual.\n\nPara activar la subida de archivos, cambia el modo en el subdirectorio images bajo el directorio raíz de MediaWiki para que el servidor web pueda escribir en él.\nLuego, activa esta opción.", "config-upload-deleted": "Directorio para los archivos eliminados:", diff --git a/includes/installer/i18n/eu.json b/includes/installer/i18n/eu.json index 030b45ebf7..87d3be3b13 100644 --- a/includes/installer/i18n/eu.json +++ b/includes/installer/i18n/eu.json @@ -78,12 +78,15 @@ "config-using-server": "\"$1\" zerbitzari-izena erabiltzen.", "config-using-uri": "\"$1$2\" zerbitzariaren URLa erabiltzen.", "config-uploads-not-safe": "Oharra: Zure igoerak egiteko $1 direktorio lehenetsia gidoi arbitrarioen exekuzioek kaltetu dezakete.\nMediaWiki-k segurtasunerako kargatutako fitxategi guztiak egiaztatzen dituen arren, oso gomendagarria da [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security segurtasun-ahultasun hau itxi] erabiltzea gaitu aurretik.", + "config-no-cli-uploads-check": "Oharra: Zure kargatutako direktorio ($1) lehenetsia ez da hauteman ahultasunerako\nscript arbitrarioak exekutatzeko CLI instalazioan zehar.", "config-brokenlibxml": "Zure sistemak dauka PHP-ko eta libxml2-ko konbinazio akastun bat eta eragin ahal du korrupzioa datarekin MediaWikin eta beste web aplikazioetan.\nAktualizatu libxml2 2.7.3-era edo berrietara ([https://bugs.php.net/bug.php?id=45996 bug filed with PHP]).\nInstalazioa geldiarazi egin da.", + "config-suhosin-max-value-length": "Suhosin instalatuta dago eta GET parametroaren luzeera $1 byte-ra mugatzen du.\nMediaWiki-ren ResourceLoader osagaia muga honen inguruan lan egingo du, baina horrek errendimendua kaltetu egingo du.\nAhal izanez gero, suhosin.get.max_value_length 1024 edo handiagoa ezarri beharko zenuke php.ini, eta LocalSettings.php-n $wgResourceLoaderMaxQueryLength balio bera ezarri.", "config-using-32bit": "Oharra: zure sistemak 32 bit-ekin jarduten duela dirudi. Hau da [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit not advised].", "config-db-type": "Datu-base mota:", "config-db-host": "Datu-basearen zerbitzaria:", "config-db-host-help": "Zure datu-basearen zerbitzaria beste zerbitzari batean badago, sartu ostalariaren izena edo IP helbidea hemen.\n\nPartekatutako web-ostatua erabiltzen ari bazara, zure ostalaritza-hornitzaileak dokumentazio-ostalariaren izen egokia eman beharko lizuke.\n\nWindows zerbitzari batean instalatzen bazara eta MySQL erabiliz, \"localhost\" agian ez du zerbitzariaren izenerako funtzionatuko. Ez badago, saiatu \"127.0.0.1\" tokiko IP helbideetarako.\n\nPostgreSQL erabiltzen ari bazara, utzi eremu hau hutsik Unix socket bidez konektatzeko.", "config-db-host-oracle": "Datu-baseko TNS:", + "config-db-host-oracle-help": "Sartu baliozko [http://download.oracle.com/docs/cd/B28359_01/network.111/b28317/tnsnames.htm Konekzio izan lokala]; instalazio honetarako tnsnames.ora fitxategia ikusgai egon behar da.
Bezeroen 10g liburutegiak edo berriagoak erabiltzen ari bazara, [http://download.oracle.com/docs/cd/E11882_01/network.112 ere erabil dezakezu. /e10836/naming.htm Konektatzeko erraza] izendatzeko metodoa.", "config-db-wiki-settings": "Wiki hau identifikatu", "config-db-name": "Datu-base izena:", "config-db-name-help": "Aukeratu zure Wikia identifikatzen duen izena.\nEzin dira espazioak eabili.\n\nErabiltzen ari bazara web hosting partekatua, hostin-eko hornitzaileak emango dizu datu-basearen izen espezifikoa edo kontrol panel baten bitzrtez zure datu-basea sortzea utziko dizu.", @@ -106,6 +109,7 @@ "config-db-schema-help": "Patroi hau normalean egokia da. Bakarrik aldatu beharrezkoa bada.", "config-pg-test-error": "Ezin da datu-basearekin konektatu $1: $2", "config-sqlite-dir": "SQLite -eko informazioaren direktorioa:", + "config-sqlite-dir-help": "SQLite-k datu guztiak fitxategi bakarrean gordetzen ditu.\n\nHornitu duzun direktorioa web zerbitzariaren bidez idatzia izateko aukera eman beharko duu instalazioan zehar.\n\nEz da webgunearen bidez eskuragarri egon behar; horregatik zure PHP fitxategiak non dauden ez dugu erakutsi.\n\nInstalatzaileak .htaccess fitxategi bat idatziko du bertan, baina horrek huts egiten badu zure datu base gordinera norbait sar daiteke.\nErabiltzaileen datu gordinak (helbide elektronikoak, pasahitzak), ezabatutako berrikusketa eta gainontzeko datu mugatuak ere barnean hartuz.\n\nDatu-basea beste nonbait jartzearen inguruan hausnartu, adibidez, /var/lib/mediawiki/yourwiki-n.", "config-oracle-def-ts": "Taula-toki lehenetsia:", "config-oracle-temp-ts": "Aldi baterako taula:", "config-type-mysql": "MySQL (edo bateragarria)", @@ -138,6 +142,8 @@ "config-postgres-old": "PostgreSQL $1 edo berriagoa behar da. Zuk $2 badaukazu.", "config-mssql-old": "Microsoft SQL Server $1 edo berriagoa behar da. Zuk $2 badaukazu.", "config-sqlite-name-help": "Aukeratu zure wikia identifikatzen duen izen bat.\nEz erabili zuriunerik edo gidoirik.\nHau erabiliko da SQLite datuen artxiborako.", + "config-sqlite-parent-unwritable-group": "Ezin da datu-direktorioa sortu $1, web zerbitzariak ezin baitu $2 guraso direktorioan idatzi.\n\nInstalatzaileak webgunea exekutatzen ari den bitartean zure erabiltzailea zehaztu du.\nEgin $3 direktorioan idazteko gai izatea jarraitzeko.\nUnix/Linux sistema batean:\n\n
cd $2\nmkdir $3\nchgrp $4 $3\nchmod g+w $3
", + "config-sqlite-parent-unwritable-nogroup": "Ezin da datu-direktorioa sortu $1, web zerbitzariak ezin baitu $2 guraso direktorioan idatzi.\n\nInstalatzaileak webgunea exekutatzen ari den bitartean zure erabiltzailea zehaztu dezake.\nEgin $3 direktorioa globalean idazteko gai izatea (horretarako eta besteentzako!) jarraitzeko.\nUnix/Linux sistema batean:\n\n
cd $2\nmkdir $3\nchmod a+w $3
", "config-sqlite-mkdir-error": "Arazo bat sortu da datuen direktorioa sortzerakoan \"$1\".\nLokalizazio egiaztatu eta berriro saiatu.", "config-sqlite-dir-unwritable": "Ezin izan da \"$1\" direktoriora idatzi.\nAldatu baimenak web-serbidoreak idatzi ahal izateko, eta berriro saiatu.", "config-sqlite-connection-error": "$1.\n\nDatu direktorioa eta datu-basea egiaztatu eta berriro saiatu.", @@ -160,9 +166,11 @@ "config-mysql-myisam": "MyISAM", "config-mysql-myisam-dep": "Oharra: MyISAM MySQL biltegiratze-motor gisa aukeratu duzu, MediaWikirekin erabiltzeko gomendagarria ez dena honengatik:\n*taula blokeoak direla-eta gauza gutxi onartu ohi du\n*beste motore batzuek baino ustelkeria gehiago izateko aukerak ditu\n*MediaWiki-ren kode baseak ez du beti kudeatzen MyISAM behar bezala\n\nZure MySQL instalazioa InnoDB onartzen badu, hori aukeratzeko gomendatzen da.\nZure MySQL instalazioa InnoDB ez badu onartzen, baliteke bertsioa berritzeko ordua izatea.", "config-mysql-only-myisam-dep": " Oharra: MyISAM makinaren MySQL biltegiratze motarako bakarra da, eta hau ez da MediaWiki-rekin erabiltzeko gomendatzen, honengatik:\n* maiztasunez taula blokeoek konkurrentzia ez dute onartzen \n* Beste motore batzuek baino ustelkeria gehiago izaten dute\n* MediaWiki-ren kodekak ez du beti kudeatzen MyISAM behar bezala\n\nZure MySQL instalazioak ez du InnoDB onartzen, agian bertsio berritzeko ordua da.", + "config-mysql-engine-help": "InnoDB ia beti aukerarik onena da, konkurrentzia-laguntza ona duelako.\n\nMyISAM erabiltzaile bakarreko edo irakurketa bakarreko instalazioetan azkarragoa izan daiteke.\nMyISAM datu-basea gehiagokotan hondatuta ageri da InnoDB datu-baseareakin baino.", "config-mysql-charset": "Datu-basearen karaktere multzoa:", "config-mysql-binary": "Bitarra", "config-mysql-utf8": "UTF-8", + "config-mysql-charset-help": "Modu bitarrean, MediaWiki-k UTF-8 testua datu-baseko eremu bitarretan gordetzen du.\nHau MySQL-en UTF-8 modua baino eraginkorragoa da eta Unicode karaktereen barruti osoa erabiltzea ahalbidetzen du.\n\nUTF-8 moduan, MySQL-k jakingo du zer karakterean zure datuak konfiguratzen dituen, aurkeztu eta behar bezala bihurtzeko, baina ez dizkizu karaktereak [https://en.wikipedia.org/wiki/Mapping_of_Unicode_character_planes Oinarrizko Eleaniztasun Plana]-ren gainetik gordetzen utziko.", "config-mssql-auth": "Autentifikazio mota:", "config-mssql-install-auth": "Aukeratu instalazio prozesuan zehar datu-basera konektatzeko erabiliko den autentifikazio mota.\n\"{{Int: config-mssql-windowsauth}}\" hautatzen baduzu, web zerbitzariak duen edozein erabiltzailek erabiliko duen kredentziala erabiliko da.", "config-mssql-web-auth": "Aukeratu instalazio prozesuan zehar datu-base zerbitzariari konektatzeko erabiliko den autentifikazio mota.\n\"{{Int: config-mssql-windowsauth}}\" hautatzen baduzu, web zerbitzariak duen edozein erabiltzailek erabiliko duen kredentziala erabiliko da.", @@ -178,6 +186,7 @@ "config-ns-other-default": "MyWiki", "config-project-namespace-help": "Wikipedia-ren adibidea jarraitzen, wiki askok beren orrien politika mantentzen dute beren edukien orrialdeetatik bereizita, '' 'proiektuaren izen-eremuan' ''.\nOrrialde honetako izenburu guztiek aurrizki jakin batekin hasten dira, hemen zehaztu ahal direnak.\nNormalean, aurrizkia wikiaren izenetik dator, baina ezin du \"#\" edo \":\" puntuazio-karaktereak eduki.", "config-ns-invalid": "Zehaztutako \"$1\" izena baliogabea da.\nZehaztu beste proiektu baten izenaren eremua.", + "config-ns-conflict": "\"$1\" zehaztutako izen-eremuak lehenetsitako MediaWiki izen-eremu batekin gatazkan ari da.\nZehaztu beste proiektu izen-eremu bat.", "config-admin-box": "Administratzaile kontua", "config-admin-name": "Zure erabiltzaile-izena:", "config-admin-password": "Pasahitza:", @@ -189,9 +198,14 @@ "config-admin-password-mismatch": "Sartutako bi pasahitzak ez datoz bat.", "config-admin-email": "E-posta helbidea:", "config-admin-email-help": "Sartu email bat baimena emateko mezuak jasotzeko, pasahitza aldatzeko and orrien aldaketeei buruz berri edukitzeko.\nHutsik utzi ahal duzu.", + "config-admin-error-user": "Barneko errorea \"$1\" izeneko administratzailea sortzerakoan.", "config-admin-error-password": "Barne-arazoa administratzailearen pasahitza sortzerakoan.\"$1\".
$2
", "config-admin-error-bademail": "Helbide elektroniko okerra idatzi duzu.", + "config-subscribe": "Harpidetu [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce posta zerrenda bidez egindako iragarki ohar] zerrendara.", + "config-subscribe-help": "Hau bolumen baxuko oharren iragarkietarako erabiltzen den zerrenda da, segurtasun iragarki garrantzitsuak barne.\nHarpidetu horretara eta zure MediaWiki instalazioa eguneratu bertsio berriak ateratzean.", + "config-subscribe-noemail": "Ohar iragarkien posta elektroniko zerrendara harpidetzen saiatu zara, helbide elektroniko bat eman gabe.\nEman ezazu helbide elektronikoa posta zerrendan harpidetzea nahi baduzu.", "config-pingback": "Elkarbanatu informazioa instalazio prozesuari buruz MediaWiki-ko sustatzaileekin.", + "config-pingback-help": "Aukera hau hautatzen baduzu, MediaWiki-k https://www.mediawiki.org guneari periodikoki ping egingo dio MediaWiki-ren instantzia honi buruzko oinarrizko datuekin. Datu horietan, adibidez, sistema mota, PHP bertsioa eta hautatutako datu-base motorra agertzen dira. Wikimedia Fundazioak datu hauek partekatzen dituu MediaWiki garatzaileekin etorkizuneko garapen-ahaleginak gidatzeko. Ondorengo datuak zure sistemara bidaliko dira:\n
$1
", "config-almost-done": "Ia amaitu duzu!\nFalta den konfigurazioa saltatu ahal duzu eta zuzenean wikia instalatu.", "config-optional-continue": "Galdera gehiago egin.", "config-optional-skip": "Aspertuta nago, wikia instalatu bakarrik.", @@ -200,16 +214,20 @@ "config-profile-no-anon": "Kontua sortzea beharrezkoa da", "config-profile-fishbowl": "Baimendutako editoreak bakarrik", "config-profile-private": "Wiki pribatua", + "config-profile-help": "Wikiak hobeto funtzionatzen dute ahalik eta jende gehiagok editatzeko aukera duenean.\nMediaWikian, erraza da azken aldaketak berrikustea eta erabiltzaile desegokiek egindako kalteak konpontzea.\n\nHala eta guztiz ere, askok MediaWiki rol ezberdinetarako baliagarri ikusi dute, nahiz eta batzuetan erraza ez izan wikiaren onureen inguruan konbentzitzea.\nBeraz, aukera zuk egiten duzu.\n\n{{int:config-profile-wiki}} ereduak edonork edita dezake, nahiz eta saioa hasi.\n{{int:config-profile-no-anon}} -ko wiki batek aparteko erantzukizuna ematen du, baina aldi baterako laguntzaileak alda ditzake.\n\n{{int:config-profile-fishbowl}} planteamenduak aukera ematen du baimendutako erabiltzaileek editatzeko, baina publikoak orrialdeak ikusi ditzake, historiak barne.\n{{int:config-profile-private}} bat soilik onartutako erabiltzaileei orriak ikusteko aukera ematen die, talde berak editatu ahal izateko.\n\nErabiltzaileen eskubideen ezarpen konplexuagoak eskuragarri daude instalazioa egin ondoren, ikus ezazu [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:User_rights eskuzko sarrera garrantzitsua].", "config-license": "Copyright eta lizentzia:", "config-license-none": "Ez jarri lizentzia orriaren baimenik", "config-license-cc-by-sa": "Creative Commons-eko esleipen-lizentzia", "config-license-cc-by": "Creative Commons Aitorpena", + "config-license-cc-by-nc-sa": "Creative Commons Attribution-NonCommercial-ShareAlike", "config-license-cc-0": "Creative Commons Zero (Jabari Publikoa)", "config-license-gfdl": "\nGNU Free Documentation License 1.3 edo berriagoa", "config-license-pd": "Domeinu Askea", "config-license-cc-choose": "Aukeratu Creative Commons lizentzia pertsonalizatua", + "config-license-help": "Wikilari publiko askok ekarpen guztiak jartzen dituzte [http://freedomdefined.org/Definition lizentzia aske] azpian.\nHonek komunitatearen jabetza zentzuaren kontzeptua sortzen laguntzen du eta epe luzerako ekarpena bultzatzen du.\nEz da, oro har, wiki pribatu edo korporatiborik behar.\n\nWikipediatik testua erabiltzeko aukera izan nahi baduzu eta Wikipediak zure wikietatik kopiatutako testua onartzeko gai izatea nahi baduzu, {{int:config-license-cc-by-sa}} aukeratu beharko zenuke.\n\nWikipedia lehenago erabili izan du GNU Dokumentazio Librearen Lizentzia.\nGFDL baliozko lizentzia da, baina ulertzeko zaila da.\nGFDLren baimenarekin lotutako edukiak berrerabiltzea ere zaila da.", "config-email-settings": "E-posta hobespenak", "config-enable-email": "Aktibatu irteerako emaila.", + "config-enable-email-help": "Lan egiteko email-a nahi baduzu, [http://www.php.net/manual/en/mail.configuration.php PHP's mail settings] ondo konfiguratu egin behar da. Email ezaugarririk ez baduzu nahi, hemen kendu ditzakezu.", "config-email-user": "Aktibatu erabiltzaileen arteko emaila.", "config-email-user-help": "Baimena eman erabiltzaileei beraien artean emailak bidaltzeko, lehentasunetan aukera aktibatuta badaukate.", "config-email-usertalk": "Aktibatu erabiltzaileen eztabaida orrien jakinarazpena", @@ -219,6 +237,7 @@ "config-email-auth": "Aktibatu emailaren autentifikazioa.", "config-email-auth-help": "Aukera hau aktibatuta badago, erabiltzaileak konfirmatu behar du bere emaila, sortzerakoen edota aldetzerakoan bidali zaion linka erabiltzen.\n\nBakarrik kautotaku emailak gai izango dira beste erabiltzaileen emailak jasotzeko edota jakinarazpen emailak aldatzeko.\n\nAukera hau hautatzea gomendagarria da Wiki publikoentzat, emaileen erramintien abusua dela eta.", "config-email-sender": "Itzuli helbide elektronikoa:", + "config-email-sender-help": "Idatzi helbide elektronikoa bueltan mezuak jasotzeko helbide elektroniko gisa.\nErreboteak bidaliko dira horra.\nPosta-zerbitzari askok gutxienez domeinu izenaren zati bat behar dute baliozkoa izan dadin.", "config-upload-settings": "Irudi eta fitxategi igoerak", "config-upload-enable": "Fitxategi igoera gaitu", "config-upload-help": "Fitxategiak kargatzeak zure zerbitzaria segurtasun arriskuei eragin diezaioke.\nInformazio gehiagorako, irakurri [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security security section] eskuliburuan.\n\nFitxategiak igotzea aktibatzeko, aldatu images subdirektorio modua MediaWiki-ren erroko direktorioaren azpian, web zerbitzariak honela idatz dezake.\nOndoren, gaitu aukera hau.", @@ -250,6 +269,7 @@ "config-skins-must-enable-some": "Gutxienez aukeratu behar duzu ikusizko estilo bat aktibatzeko.", "config-skins-must-enable-default": "Lehenetsia bezala aukeratu duzun ikusizko estilo aktibatuta egon behar da.", "config-install-alreadydone": "Oharra:Badirudi MediaWikia instalatu daukazula eta berriz instalatzen saiatzen ari zarela.\n\nMesedez, hurrengo orrian jarraitu", + "config-install-begin": "\"{{int:config-continue}}\" sakatuz, MediaWiki instalazioa hasiko duzu.\nAldaketak oraindik egin nahi badituzu, sakatu \"{{int:config-back}}\".", "config-install-step-done": "egina", "config-install-step-failed": "Huts egin du", "config-install-extensions": "Luzapenak barne", @@ -276,6 +296,7 @@ "config-install-interwiki-exists": "Oharra: Interwikiko taula badirudi sarrerak dituela. \nTaula estandarra saltatzen.", "config-install-stats": "Estatistikak hasten", "config-install-keys": "Gako sekretuak sortzen", + "config-insecure-keys": "Oharra: ($1) instalazioan zehar sortu {{PLURAL:$2|den|diren}} {{PLURAL:$2|gako segurua|gako seguruak}}ez d(ir)a guztiz segurua(k). Kontuan hartu {{PLURAL:$2|hau|hauek}} eskuz aldatzeko aukera.", "config-install-updates": "Saihestu egikaratzen behar ez diren aktualizazioak", "config-install-updates-failed": "Errore Sartzea eguneratze-gakoak taulen barruan huts egin du hurrengo errorearekin: $1", "config-install-sysop": "Administratzaile kontua sortzen", @@ -287,6 +308,7 @@ "config-install-mainpage-failed": "Orri nagusia ezin izan da txertatu: $1", "config-install-done": "Zorionak!\nMediaWiki instalatu duzu.\n\nInstalatzaileak LocalSettings.php fitxategia sortu egin du. \nZure konfigurazio guztia darama.\n\nDeskargatu egin beharko duzu eta zure wiki instalazio oinarrian jarri (index.php-rako direktorio berean). Deskarga automakikoki hasi behar izan da.\n\nDeskargatzeko aukerarik ez bazaizu eskaini, edo kantzelatu egin baduzu, hurrengo linkean klikatu berrabiarazteko deskarga:\n\n$3\n\nOharra: Orain ez baduzu egiten, sortutako konfigurazio fitxategi hau ez da erabilgarri egongo geroago instalazioa bertan behera uzten baduzu deskargatu gabe.\n\nBehin hori eginda, [$2 zure wikia sartu] ahal duzu.", "config-install-done-path": "Zorionak!\nMediaWiki instalatu duzu.\n\nInstalatzaileak sortu egin du LocalSettings.php\nZure konfigurazio guztia dauka.\n\nDeskargatu egin behar duzu eta jarri $4 -ean . Deskarga automakikoki hasiko da.\n\nEz badizu deskargatzeko aukerarik eman, edo kantzalatu egin baduzu, hurrengo linkean klikatu berrabiatzeko:\n\n$3\n\nOharra: Instalazio prozesuatik ateratzen bazara konfigurazio artxikoa deskargatu barik, gero ez da egongo eskuragarri.\n\nBehin hori eginda, [$2 enter your wiki] ahal duzu.", + "config-install-success": "MediaWiki arrakastaz instalatu da. Orain <$1$2> bisitatu dezakezu zure wikia ikusteko.\nGalderarik izanez gero, begiratu gure maiztasunez egiten diren galderen zerrenda:\n edo erabili orrialde honi lotuta dauden laguntza foroetako bat.", "config-download-localsettings": "Jaitsi LocalSettings.php", "config-help": "Laguntza", "config-help-tooltip": "sakatu zabaltzeko", diff --git a/includes/installer/i18n/fr.json b/includes/installer/i18n/fr.json index 0988546899..1ad2cbd3b4 100644 --- a/includes/installer/i18n/fr.json +++ b/includes/installer/i18n/fr.json @@ -75,31 +75,31 @@ "config-env-bad": "L’environnement a été vérifié.\nVous ne pouvez pas installer MediaWiki.", "config-env-php": "PHP $1 est installé.", "config-env-hhvm": "HHVM $1 est installé.", - "config-unicode-using-intl": "Utilisation de [http://pecl.php.net/intl l'extension PECL intl] pour la normalisation Unicode.", - "config-unicode-pure-php-warning": "Attention : L’[http://pecl.php.net/intl extension PECL intl] n’est pas disponible pour la normalisation d’Unicode, retour à la version lente implémentée en PHP.\nSi votre site web sera très fréquenté, vous devriez lire ceci : [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations ''Unicode normalization''] (en anglais).", - "config-unicode-update-warning": "Attention: La version installée du normalisateur Unicode utilise une ancienne version de la [http://site.icu-project.org/ bibliothèque logicielle ''ICU Project''].\nVous devriez faire une [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations mise à jour] si vous êtes concerné par l'usage d'Unicode.", + "config-unicode-using-intl": "Utilisation de [http://pecl.php.net/intl l’extension PECL intl] pour la normalisation Unicode.", + "config-unicode-pure-php-warning": "Attention : L’[http://pecl.php.net/intl extension PECL intl] n’est pas disponible pour la normalisation d’Unicode, retour à la version lente implémentée en PHP seulement.\nSi votre site web sera très fréquenté, vous devriez lire ceci : [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations ''Unicode normalization''] (en anglais).", + "config-unicode-update-warning": "Attention : la version installée du normalisateur Unicode utilise une ancienne version de la bibliothèque logicielle du [http://site.icu-project.org/ ''Projet ICU''].\nVous devriez faire une [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations mise à jour] si vous êtes concerné par l’usage d’Unicode.", "config-no-db": "Impossible de trouver un pilote de base de données approprié ! Vous devez installer un pilote de base de données pour PHP. {{PLURAL:$2|Le type suivant|Les types suivants}} de bases de données {{PLURAL:$2|est reconnu|sont reconnus}} : $1.\n\nSi vous avez compilé PHP vous-même, reconfigurez-le avec un client de base de données actif, par exemple en utilisant ./configure --with-mysqli. Si vous avez installé PHP depuis un paquet Debian ou Ubuntu, alors vous devrez aussi installer, par exemple, le paquet php5-mysql.", - "config-outdated-sqlite": "'''Attention''': vous avez SQLite $1, qui est inférieur à la version minimale requise $2. SQLite sera indisponible.", - "config-no-fts3": "'''Attention :''' SQLite est compilé sans le module [//sqlite.org/fts3.html FTS3] ; les fonctions de recherche ne seront pas disponibles sur ce moteur.", - "config-pcre-old": "'''Fatal :''' PCRE $1 ou ultérieur est nécessaire.\nVotre binaire PHP est lié avec PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/Plus d’information sur PCRE].", - "config-pcre-no-utf8": "Erreur fatale: le module PCRE de PHP semble être compilé sans la prise en charge de PCRE_UTF8.\nMédiaWiki a besoin de la gestion d’UTF-8 pour fonctionner correctement.", + "config-outdated-sqlite": "Attention : vous avez SQLite $1, qui est inférieur à la version minimale requise $2. SQLite sera indisponible.", + "config-no-fts3": "Attention : SQLite est compilé sans le module [//sqlite.org/fts3.html FTS3] ; les fonctions de recherche ne seront pas disponibles sur ce moteur.", + "config-pcre-old": "Erreur fatale : PCRE $1 ou ultérieur est nécessaire.\nVotre binaire PHP est lié avec PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/Plus d’information sur PCRE].", + "config-pcre-no-utf8": "Erreur fatale : le module PCRE de PHP semble être compilé sans la prise en charge de PCRE_UTF8.\nMediaWiki a besoin de la gestion d’UTF-8 pour fonctionner correctement.", "config-memory-raised": "Le paramètre memory_limit de PHP était à $1, porté à $2.", - "config-memory-bad": "'''Attention :''' Le paramètre memory_limit de PHP est à $1.\nCette valeur est probablement trop faible.\nIl est possible que l’installation échoue !", + "config-memory-bad": "Attention : Le paramètre memory_limit de PHP est à $1.\nCette valeur est probablement trop faible.\nIl est possible que l’installation échoue !", "config-xcache": "[http://xcache.lighttpd.net/ XCache] est installé", "config-apc": "[http://www.php.net/apc APC] est installé", "config-apcu": "[http://www.php.net/apcu APCu] est installé", "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] est installé", - "config-no-cache-apcu": "Attention : Impossible de trouver [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa mise en cache d'objets n'est pas activée.", - "config-mod-security": "'''Attention''': Votre serveur web a [http://modsecurity.org/ mod_security] activé. S’il est mal configuré, cela peut poser des problèmes à MediaWiki ou à d’autres applications qui permettent aux utilisateurs de publier un contenu quelconque.\nReportez-vous à [http://modsecurity.org/documentation/ la documentation de mod_security] ou contactez le soutien de votre hébergeur si vous rencontrez des erreurs aléatoires.", + "config-no-cache-apcu": "Attention : impossible de trouver [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa mise en cache d’objets n’est pas activée.", + "config-mod-security": "Attention : votre serveur web a [http://modsecurity.org/ mod_security] activé. S’il est mal configuré, cela peut poser des problèmes à MediaWiki ou à d’autres applications qui permettent aux utilisateurs de publier un contenu quelconque. Si possible, ceci devrait être désactivé. Sinon, reportez-vous à [http://modsecurity.org/documentation/ la documentation de mod_security] ou contactez l’assistance de votre hébergeur si vous rencontrez des erreurs aléatoires.", "config-diff3-bad": "GNU diff3 introuvable.", "config-git": "Logiciel de contrôle de version Git trouvé : $1.", "config-git-bad": "Logiciel de contrôle de version Git non trouvé.", - "config-imagemagick": "ImageMagick trouvé : $1.\nLa miniaturisation d'images sera activée si vous activez le téléversement de fichiers.", + "config-imagemagick": "ImageMagick trouvé : $1.\nLa génération de vignettes d’images sera activée si vous activez les téléversements.", "config-gd": "La bibliothèque graphique GD intégrée a été trouvée.\nLa miniaturisation d'images sera activée si vous activez le téléversement de fichiers.", - "config-no-scaling": "Impossible de trouver la bibliothèque GD ou ImageMagick.\nLa miniaturisation d'images sera désactivée.", - "config-no-uri": "'''Erreur :''' Impossible de déterminer l'URI du script actuel.\nInstallation interrompue.", - "config-no-cli-uri": "'''Attention''': Aucun --scriptpath n'a été spécifié; $1 sera utilisé par défaut", - "config-using-server": "Utilisation du nom de serveur \"$1\".", + "config-no-scaling": "Impossible de trouver la bibliothèque GD ou ImageMagick.\nLa miniaturisation d’images sera désactivée.", + "config-no-uri": "Erreur : impossible de déterminer l’URI du script actuel.\nInstallation interrompue.", + "config-no-cli-uri": "Attention : Aucun --scriptpath n’a été spécifié ; $1 sera utilisé par défaut", + "config-using-server": "Utilisation du nom de serveur « $1 ».", "config-using-uri": "Utilisation de l'URL de serveur \"$1$2\".", "config-uploads-not-safe": "Attention : Votre répertoire par défaut pour les téléversements, $1, est vulnérable, car il peut exécuter n’importe quel script.\nBien que MediaWiki vérifie tous les fichiers téléversés, il est fortement recommandé de [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security fermer cette faille de sécurité] (texte en anglais) avant d’activer les téléversements.", "config-no-cli-uploads-check": "'''Attention:''' Votre répertoire par défaut pour les imports($1) n'est pas contrôlé concernant la vulnérabilité d'exécution de scripts arbitraires lors de l'installation CLI.", diff --git a/includes/installer/i18n/gl.json b/includes/installer/i18n/gl.json index e6e4b67455..08434f6af1 100644 --- a/includes/installer/i18n/gl.json +++ b/includes/installer/i18n/gl.json @@ -312,6 +312,7 @@ "config-install-mainpage-failed": "Non se puido inserir a páxina principal: $1", "config-install-done": "Parabéns!\nInstalou MediaWiki.\n\nO programa de instalación xerou un ficheiro LocalSettings.php.\nEste ficheiro contén toda a súa configuración.\n\nTerá que descargalo e poñelo na base da instalación do seu wiki (no mesmo directorio ca index.php). A descarga debería comezar automaticamente.\n\nSe non comezou a descarga ou se a cancelou, pode facer que comece de novo premendo na ligazón que aparece a continuación:\n\n$3\n\nNota: Se non fai iso agora, este ficheiro de configuración xerado non estará dispoñible máis adiante se sae da instalación sen descargalo.\n\nCando faga todo isto, xa poderá [$2 entrar no seu wiki].", "config-install-done-path": "Parabéns!\nInstalou MediaWiki.\n\nO instalador xerou un ficheiro LocalSettings.php.\nEste contén toda a súa configuración.\n\nDeberá descargalo e poñerlo en $4. A descarga debería ter comezado automaticamente.\n\nSe non comenzou a descarga, ou se a cancelou, podes reiniciala descarga premendo na seguinte ligazón:\n\n$3\n\nNota: se non fai isto agora, este ficheiro de configuración xerado non estará dispoñible máis tarde se sae da instalación sen descargarlo.\n\nCando o teña feito, poderá [$2 entrar na súa wiki].", + "config-install-success": "MediaWiki instalouse con éxito. Agora podes \nvisitar <$1$2> para ver a túa wiki.\nSe tes dúbidas, revisa a nosa lista de preguntas frecuentes:\n ou usa un dos\nforos de axuda ligados nesa páxina.", "config-download-localsettings": "Descargar o LocalSettings.php", "config-help": "axuda", "config-help-tooltip": "prema para expandir", diff --git a/includes/installer/i18n/mk.json b/includes/installer/i18n/mk.json index f3f40b6bfd..ee5bb8de23 100644 --- a/includes/installer/i18n/mk.json +++ b/includes/installer/i18n/mk.json @@ -230,7 +230,7 @@ "config-email-user": "Овозможи е-пошта од корисник до корисник", "config-email-user-help": "Дозволи сите корисници да можат да си праќаат е-пошта ако ја имаат овозможено во нагодувањата.", "config-email-usertalk": "Овозможи известувања за промени во кориснички страници за разговор", - "config-email-usertalk-help": "Овозможи корисниците да добиваат известувања за промени во нивните кориснички страници за разговор ако ги имаат овозможено во нагодувањата.", + "config-email-usertalk-help": "Овозможи корисниците да добиваат известувања за промени во нивните кориснички разговорни страници ако ги имаат овозможено во нагодувањата.", "config-email-watchlist": "Овозможи известувања за список на набљудувања", "config-email-watchlist-help": "Овозможи корисниците да добиваат известувања за нивните набљудувани страници ако ги имаат овозможено во нагодувањата.", "config-email-auth": "Овозможи потврдување на е-пошта", diff --git a/includes/installer/i18n/nl.json b/includes/installer/i18n/nl.json index 6185ef4c22..66c86b8374 100644 --- a/includes/installer/i18n/nl.json +++ b/includes/installer/i18n/nl.json @@ -326,6 +326,7 @@ "config-install-mainpage-failed": "Het was niet mogelijk de hoofdpagina in te voegen: $1", "config-install-done": "Gefeliciteerd!\nU hebt MediaWiki geïnstalleerd.\n\nHet installatieprogramma heeft het bestand LocalSettings.php aangemaakt.\nDit bevat al uw instellingen.\n\nU moet het bestand downloaden en in de hoofdmap van uw wiki-installatie plaatsen, in dezelfde map als index.php.\nDe download zou automatisch moeten zijn gestart.\n\nAls de download niet is gestart of als u de download hebt geannuleerd, dan kunt u de download opnieuw starten door op de onderstaande koppeling te klikken:\n\n$3\n\nLet op: als u dit niet nu doet, dan is het bestand als u later de installatieprocedure afsluit zonder het bestand te downloaden niet meer beschikbaar.\n\nNa het plaatsen van het bestand met instellingen kunt u [$2 uw wiki gebruiken].", "config-install-done-path": "Gefeliciteerd!\nU hebt MediaWiki geïnstalleerd.\n\nHet installatieprogramma heeft het bestand LocalSettings.php aangemaakt.\nDit bevat al uw instellingen.\n\nU moet het bestand downloaden en in $4 plaatsen. De download zou automatisch moeten zijn gestart.\n\nAls de download niet is gestart of als u de download hebt geannuleerd, dan kunt u de download opnieuw starten door op de onderstaande koppeling te klikken:\n\n$3\n\nLet op: Als u dit niet nu doet, dan is het bestand als u later de installatieprocedure afsluit zonder het bestand te downloaden niet meer beschikbaar.\n\nNa het plaatsen van het bestand met instellingen kunt u [$2 uw wiki gebruiken].", + "config-install-success": "MediaWiki is geïnstalleerd. U kunt nu\n<$1$2> bezoeken om uw wiki te bekijken.\nAls u vragen heeft, bezoek dan onze lijst met veelgestelde vragen:\n, of gebruik een van de hulpforums vermeldt op die pagina.", "config-download-localsettings": "LocalSettings.php downloaden", "config-help": "hulp", "config-help-tooltip": "klik om uit te vouwen", diff --git a/includes/installer/i18n/pl.json b/includes/installer/i18n/pl.json index a19dbc315d..7d2ab8f979 100644 --- a/includes/installer/i18n/pl.json +++ b/includes/installer/i18n/pl.json @@ -323,11 +323,13 @@ "config-install-mainpage-failed": "Nie udało się wstawić strony głównej: $1", "config-install-done": "'''Gratulacje!\nUdało Ci się zainstalować MediaWiki.\n\nInstalator wygenerował plik konfiguracyjny LocalSettings.php.\n\nMusisz go pobrać i umieścić w katalogu głównym Twojej instalacji wiki (tym samym katalogu co index.php). Pobieranie powinno zacząć się automatycznie.\n\nJeżeli pobieranie nie zostało zaproponowane lub jeśli użytkownik je anulował, można ponownie uruchomić pobranie klikając poniższe łącze:\n\n$3\n\nUwaga: Jeśli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie już dostępny po zakończeniu instalacji.\n\nPo załadowaniu pliku konfiguracyjnego możesz [$2 wejść na wiki].", "config-install-done-path": "Gratulacje!\nZainstalowałeś właśnie MediaWiki.\n\nInstalator wygenerował plik LocalSettings.php.\nZawiera całą Twoją konfigurację.\n\nMusisz go pobrać i umieścić w $4. Pobieranie powinno rozpocząć się automatycznie.\n\nJeżeli nie pojawiła się informacja o pobieraniu lub jeżeli ja anulowałeś, kliknij poniższy link:\n\n$3\n\nUwaga: Jeżeli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie potem dostępny, jeżeli wyjdziesz z instalacji bez jego pobrania.\n\nGdy to będzie zrobione, możesz [$2 wejść na swoją wiki].", + "config-install-success": "MediaWiki została pomyślnie zainstalowana. Możesz teraz\nodwiedzić <$1$2>, aby zobaczyć swoją wiki.\nJeśli masz pytania, sprawdź naszą listę najczęściej zadawanych pytań:\n lub użyj jednej z\nform wsparcia odsyłanej z tej strony.", "config-download-localsettings": "Pobierz LocalSettings.php", "config-help": "pomoc", "config-help-tooltip": "kliknij, aby rozwinąć", "config-nofile": "Nie udało się odnaleźć pliku \"$1\". Czy nie został usunięty?", "config-extension-link": "Czy wiesz, że twoja wiki obsługuje [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions rozszerzenia]?\n\nMożesz przejrzeć [https://www.mediawiki.org/wiki/Category:Extensions_by_category rozszerzenia według kategorii] lub [https://www.mediawiki.org/wiki/Extension_Matrix Extension Matrix], aby zobaczyć pełną listę rozszerzeń.", + "config-skins-screenshots": "$1 (zrzut ekranu: $2)", "config-screenshot": "zrzut ekranu", "mainpagetext": "Instalacja MediaWiki powiodła się.", "mainpagedocfooter": "Zapoznaj się z [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Podręcznikiem użytkownika] zawierającym informacje o tym jak korzystać z oprogramowania wiki.\n\n== Na początek ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista ustawień konfiguracyjnych]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Komunikaty o nowych wersjach MediaWiki (lista dyskusyjna)]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Przetłumacz MediaWiki na swój język]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Dowiedz się, jak walczyć ze spamem na swojej wiki]" diff --git a/includes/installer/i18n/pt.json b/includes/installer/i18n/pt.json index 5f3db68a6e..d455fe1890 100644 --- a/includes/installer/i18n/pt.json +++ b/includes/installer/i18n/pt.json @@ -19,7 +19,8 @@ "Diniscoelho", "Ruila", "Seb35", - "MokaAkashiyaPT" + "MokaAkashiyaPT", + "Athena in Wonderland" ] }, "config-desc": "O instalador do MediaWiki", @@ -71,7 +72,7 @@ "config-no-db": "Não foi possível encontrar um controlador apropriado da base de dados! Precisa de instalar um controlador da base de dados para o PHP. {{PLURAL:$2|É aceite o seguinte tipo|São aceites os seguintes tipos}} de base de dados: $1.\n\nSe fez a compilação do PHP, reconfigure-o com um cliente de base de dados ativado; por exemplo, usando ./configure --with-mysqli.\nSe instalou o PHP a partir de um pacote Debian ou Ubuntu, então precisa de instalar também, por exemplo, o pacote php5-mysql.", "config-outdated-sqlite": "Aviso: Tem a versão $1 do SQLite, que é anterior à versão mínima necessária, a $2. O SQLite não estará disponível.", "config-no-fts3": "Aviso: O SQLite foi compilado sem o módulo [//sqlite.org/fts3.html FTS3]; as funcionalidades de pesquisa não estarão disponíveis nesta instalação.", - "config-pcre-old": "Erro fatal: É necessário o PCRE $1 ou versão posterior.\nO link do seu binário PHP foi feito com o PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Mais informações].", + "config-pcre-old": "Erro fatal: É necessário o PCRE $1 ou versão posterior.\nO seu binário PHP foi linkado com o PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Mais informações].", "config-pcre-no-utf8": "'''Erro fatal''': O módulo PCRE do PHP parece ter sido compilado sem suporte PCRE_UTF8.\nO MediaWiki necessita do suporte UTF-8 para funcionar corretamente.", "config-memory-raised": "A configuração memory_limit do PHP era $1; foi aumentada para $2.", "config-memory-bad": "Aviso: A configuração memory_limit do PHP é $1.\nIsto é provavelmente demasiado baixo.\nA instalação poderá falhar!", @@ -131,7 +132,7 @@ "config-type-sqlite": "SQLite", "config-type-oracle": "Oracle", "config-type-mssql": "Microsoft SQL Server", - "config-support-info": "O MediaWiki suporta as seguintes plataformas de base de dados:\n\n$1\n\nSe a plataforma que pretende usar não está listada abaixo, siga as instruções nos links acima para ativar o suporte.", + "config-support-info": "O MediaWiki suporta as seguintes plataformas de base de dados:\n\n$1\n\nSe a plataforma que pretende usar não está listada abaixo, siga as instruções nas hiperligações acima para ativar o suporte.", "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] é a plataforma primária do MediaWiki e é a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MySQL. ([http://www.php.net/manual/en/mysql.installation.php Como compilar PHP com suporte a MySQL])", "config-dbsupport-postgres": "* O [{{int:version-db-postgres-url}} PostgreSQL] é uma plataforma popular de base de dados de código aberto, alternativa ao MySQL. ([http://www.php.net/manual/en/pgsql.installation.php Como compilar o PHP com suporte PostgreSQL])", "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([http://www.php.net/manual/en/pdo.installation.php Como compilar o PHP com suporte SQLite], usa PDO)", @@ -249,7 +250,7 @@ "config-email-watchlist": "Ativar notificação de alterações às páginas vigiadas", "config-email-watchlist-help": "Permitir que os utilizadores recebam notificações de alterações às suas páginas vigiadas, se tiverem ativado esta funcionalidade nas suas preferências.", "config-email-auth": "Ativar autenticação do correio eletrónico", - "config-email-auth-help": "Se esta opção for ativada, os utilizadores têm de confirmar o seu endereço de correio eletrónico usando um link que lhes é enviado sempre que o definirem ou alterarem.\nSó os endereços de correio eletrónico autenticados podem receber mensagens eletrónicas dos outros utilizadores ou alterar as mensagens de notificação.\nÉ '''recomendado''' que esta opção seja ativada nas wikis de acesso público para impedir o uso abusivo das funcionalidades de correio eletrónico.", + "config-email-auth-help": "Se esta opção for ativada, os utilizadores têm de confirmar o seu endereço de correio eletrónico usando uma hiperligação que lhes é enviada sempre que o definirem ou alterarem.\nSó os endereços de correio eletrónico autenticados podem receber mensagens eletrónicas dos outros utilizadores ou alterar as mensagens de notificação.\nÉ '''recomendado''' que esta opção seja ativada nas wikis de acesso público para impedir o uso abusivo das funcionalidades de correio eletrónico.", "config-email-sender": "Endereço de correio eletrónico de retorno:", "config-email-sender-help": "Introduza o endereço de correio eletrónico que será usado como endereço de retorno nas mensagens eletrónicas de saída.\nÉ para este endereço que serão enviadas as mensagens que não podem ser entregues.\nMuitos servidores de correio eletrónico exigem que pelo menos a parte do nome do domínio seja válida. \\", "config-upload-settings": "Carregamento de imagens e ficheiros", @@ -307,7 +308,7 @@ "config-install-tables": "A criar as tabelas", "config-install-tables-exist": "Aviso: As tabelas do MediaWiki parecem já existir.\nA criação das tabelas será saltada.", "config-install-tables-failed": "Erro: A criação das tabelas falhou com o seguinte erro: $1", - "config-install-interwiki": "A preencher a tabela padrão de links interwikis", + "config-install-interwiki": "A preencher a tabela padrão de hiperligações interwikis", "config-install-interwiki-list": "Não foi possível ler o ficheiro interwiki.list.", "config-install-interwiki-exists": "Aviso: A tabela de interwikis parece já conter entradas.\nO preenchimento padrão desta tabela será saltado.", "config-install-stats": "A inicializar as estatísticas", @@ -322,7 +323,7 @@ "config-install-mainpage-exists": "A página principal já existe; a saltar este passo", "config-install-extension-tables": "A criar as tabelas das extensões ativadas", "config-install-mainpage-failed": "Não foi possível inserir a página principal: $1", - "config-install-done": "Parabéns!\nTerminou a instalação do MediaWiki.\n\nO instalador gerou um ficheiro LocalSettings.php.\nEste ficheiro contém todas as configurações.\n\nPrecisa de descarregar o ficheiro e colocá-lo no diretório de raiz da sua instalação (o mesmo diretório onde está o ficheiro index.php). Este descarregamento deverá ter sido iniciado automaticamente.\n\nSe o descarregamento não foi iniciado, ou se o cancelou, pode recomeçá-lo clicando na ligação abaixo:\n\n$3\n\nNota: Se não o descarregar agora, o ficheiro que foi gerado deixará de estar disponível quando sair do processo de instalação.\n\nDepois de terminar o passo anterior, pode [$2 entrar na wiki].", + "config-install-done": "Parabéns!\nTerminou a instalação do MediaWiki.\n\nO instalador gerou um ficheiro LocalSettings.php.\nEste ficheiro contém todas as configurações.\n\nPrecisa de descarregar o ficheiro e colocá-lo no diretório de raiz da sua instalação (o mesmo diretório onde está o ficheiro index.php). Este descarregamento deverá ter sido iniciado automaticamente.\n\nSe o descarregamento não foi iniciado, ou se o cancelou, pode recomeçá-lo clicando na hiperligação abaixo:\n\n$3\n\nNota: Se não o descarregar agora, o ficheiro que foi gerado deixará de estar disponível quando sair do processo de instalação.\n\nDepois de terminar o passo anterior, pode [$2 entrar na wiki].", "config-install-done-path": "Parabéns!\nTerminou a instalação do MediaWiki.\n\nO instalador gerou um ficheiro LocalSettings.php.\nEste ficheiro contém todas as configurações.\n\nPrecisa de descarregar o ficheiro e colocá-lo no diretório $4. Este descarregamento deverá ter sido iniciado automaticamente.\n\nSe o descarregamento não foi iniciado, ou se o cancelou, pode recomeçá-lo clicando na ligação abaixo:\n\n$3\n\nNota: Se não fizer o descarregamento agora, o ficheiro que foi gerado deixará de estar disponível quando sair do processo de instalação.\n\nDepois de terminar o passo anterior, pode [$2 entrar na wiki].", "config-install-success": "O MediaWiki foi instalado com sucesso. Já pode consultar <$1$2> para visualizar a sua wiki.\nSe tiver dúvidas, veja a nossa lista de perguntas frequentes:\n ou utilize um dos fóruns de suporte vinculados nessa página.", "config-download-localsettings": "Descarregar LocalSettings.php", diff --git a/includes/installer/i18n/zh-hant.json b/includes/installer/i18n/zh-hant.json index 96f0ef136b..2b8b83d0fc 100644 --- a/includes/installer/i18n/zh-hant.json +++ b/includes/installer/i18n/zh-hant.json @@ -38,7 +38,7 @@ "config-no-session": "您的連線階段資料遺失!\n請檢查 php.ini 設定檔並確認 session.save_path 所設定的目錄是否合適。", "config-your-language": "您的語言:", "config-your-language-help": "請選擇接下來安裝程序中要使用的語言。", - "config-wiki-language": "Wiki 語言:", + "config-wiki-language": "wiki 語言:", "config-wiki-language-help": "選擇將要安裝的 Wiki 多數情況主要使用的語言。", "config-back": "← 返回", "config-continue": "繼續 →", @@ -56,7 +56,7 @@ "config-page-releasenotes": "發佈說明", "config-page-copying": "複製", "config-page-upgradedoc": "升級", - "config-page-existingwiki": "現有 Wiki", + "config-page-existingwiki": "現有的 wiki", "config-help-restart": "是否要清除所有已輸入且儲存的資料,並重新開始安裝程序嗎?", "config-restart": "是的,重新開始", "config-welcome": "=== 環境檢查 ===\n現在會做基本的檢查,檢查環境是否符合 MediaWiki 安裝所需。\n若您要尋求如何完成安裝的協助,請記得提供以下訊息。", @@ -101,7 +101,7 @@ "config-db-host-help": "如果您的資料庫安裝在其他伺服器上,請在此輸入該主機的名稱或 IP 位址。\n\n如果您使用共用的網頁主機,您的主機提供商應會在說明文件上告訴您正確的主機名稱。\n\n如果您安裝在 Windows 伺服器並且使用 MySQL,伺服器名稱可能無法使用使用 \"localhost\"。若確實無法使用,請改嘗試使用本機的 IP 位址 \"127.0.0.1\"。\n\n如果您使用 PostgreSQL,將此欄位空白以使用 Unix socket 來連線。", "config-db-host-oracle": "資料庫的 TNS:", "config-db-host-oracle-help": "請輸入有效的 [http://download.oracle.com/docs/cd/B28359_01/network.111/b28317/tnsnames.htm 本地連線名稱],並確認安裝程式可以讀取 tnsnames.ora 檔案。
如果您使用的客戶端程式庫為 10g 或者更新的版本,您也可使用 [http://download.oracle.com/docs/cd/E11882_01/network.112/e10836/naming.htm 簡易連線] 的命名方法進行連線。", - "config-db-wiki-settings": "此 Wiki 的 ID", + "config-db-wiki-settings": "識別此 wiki", "config-db-name": "資料庫名稱:", "config-db-name-help": "請輸入一個可以辨識您的 Wiki 的名稱,\n請勿包含空格。\n\n如果您使用的是共用的網頁主機,您的主機提供商會給您一個指定的資料庫名稱,或者讓您透過管理介面建立資料庫。", "config-db-name-oracle": "資料庫 Schema:", @@ -187,12 +187,12 @@ "config-mssql-web-auth": "請選擇一般操作中要用來連線資料庫使用的身份驗證類型。\n若您選擇 \"{{int:config-mssql-windowsauth}}\",不論網頁伺服器是使用何種身份執行都會使用這組驗證資料。", "config-mssql-sqlauth": "SQL Server 身份驗證", "config-mssql-windowsauth": "Windows 身份驗證", - "config-site-name": "Wiki 的名稱:", + "config-site-name": "wiki 的名稱:", "config-site-name-help": "您所填入的內容會出現在瀏覽器的標題列以及各種其他地方。", "config-site-name-blank": "請輸入網站名稱。", "config-project-namespace": "專案命名空間:", "config-ns-generic": "專案", - "config-ns-site-name": "同 Wiki 名稱:$1", + "config-ns-site-name": "與 wiki 名稱一致:$1", "config-ns-other": "其他 (請註明)", "config-ns-other-default": "我的 wiki", "config-project-namespace-help": "許多 Wiki 以維基百科 (Wikipedia) 做為範例將政策頁面從內容頁面抽離,放置在 \"'''專案命名空間'''\" 中。\n所有在此命名空間裡的頁面都會有特定的字首,您可以在此處設定。\n通常這些字首是由該 Wiki 的名稱所衍伸出來,但無法使用標點符號,如 \"#\" 或 \":\"。", @@ -221,7 +221,7 @@ "config-optional-continue": "多問我一些問題吧。", "config-optional-skip": "我已經不耐煩了,請趕緊安裝 Wiki。", "config-profile": "使用者權限基本資料:", - "config-profile-wiki": "開放式 Wiki", + "config-profile-wiki": "開放式 wiki", "config-profile-no-anon": "需要註冊帳號", "config-profile-fishbowl": "僅授權的編輯者", "config-profile-private": "私人 wiki", diff --git a/includes/jobqueue/jobs/EnqueueJob.php b/includes/jobqueue/jobs/EnqueueJob.php index 5ffb01b45a..ea7a8d7801 100644 --- a/includes/jobqueue/jobs/EnqueueJob.php +++ b/includes/jobqueue/jobs/EnqueueJob.php @@ -24,11 +24,10 @@ /** * Router job that takes jobs and enqueues them to their proper queues * - * This can be used for several things: - * - a) Making multi-job enqueues more robust by atomically enqueueing - * a single job that pushes the actual jobs (with retry logic) - * - b) Masking the latency of pushing jobs to different queues/wikis - * - c) Low-latency enqueues to push jobs from warm to hot datacenters + * This can be used for getting sets of multiple jobs or sets of jobs intended for multiple + * queues to be inserted more robustly. This is a single job that, upon running, enqueues the + * wrapped jobs. If some of those fail to enqueue then the EnqueueJob will be retried. Due to + * the possibility of duplicate enqueues, the wrapped jobs should be idempotent. * * @ingroup JobQueue * @since 1.25 diff --git a/includes/libs/JavaScriptMinifier.php b/includes/libs/JavaScriptMinifier.php index 141a5153d4..bbba33a7ea 100644 --- a/includes/libs/JavaScriptMinifier.php +++ b/includes/libs/JavaScriptMinifier.php @@ -74,11 +74,10 @@ class JavaScriptMinifier { * or when required to guard against semicolon insertion. * * @param string $s JavaScript code to minify - * @param bool $statementsOnOwnLine Whether to put each statement on its own line * @param int $maxLineLength Maximum length of a single line, or -1 for no maximum. * @return String Minified code */ - public static function minify( $s, $statementsOnOwnLine = false, $maxLineLength = 1000 ) { + public static function minify( $s, $maxLineLength = 1000 ) { // First we declare a few tables that contain our parsing rules // $opChars : characters, which can be combined without whitespace in between them @@ -387,23 +386,6 @@ class JavaScriptMinifier { ) ); - // Rules for when newlines should be inserted if - // $statementsOnOwnLine is enabled. - // $newlineBefore is checked before switching state, - // $newlineAfter is checked after - $newlineBefore = array( - self::STATEMENT => array( - self::TYPE_BRACE_CLOSE => true, - ), - ); - $newlineAfter = array( - self::STATEMENT => array( - self::TYPE_BRACE_OPEN => true, - self::TYPE_PAREN_CLOSE => true, - self::TYPE_SEMICOLON => true, - ), - ); - // $divStates : Contains all states that can be followed by a division operator $divStates = array( self::EXPRESSION_OP => true, @@ -467,18 +449,24 @@ class JavaScriptMinifier { // We have to distinguish between regexp literals and division operators // A division operator is only possible in certain states } elseif( $ch === '/' && !isset( $divStates[$state] ) ) { - // Regexp literal, search to the end, skipping over backslash escapes and - // character classes + // Regexp literal for( ; ; ) { do{ + // Skip until we find "/" (end of regexp), "\" (backslash escapes), + // or "[" (start of character classes). $end += strcspn( $s, '/[\\', $end ) + 2; + // If backslash escape, keep searching... } while( $end - 2 < $length && $s[$end - 2] === '\\' ); $end--; + // If the end, stop here. if( $end - 1 >= $length || $s[$end - 1] === '/' ) { break; } + // (Implicit else), we must've found the start of a char class, + // skip until we find "]" (end of char class), or "\" (backslash escape) do{ $end += strcspn( $s, ']\\', $end ) + 2; + // If backslash escape, keep searching... } while( $end - 2 < $length && $s[$end - 2] === '\\' ); $end--; }; @@ -580,15 +568,6 @@ class JavaScriptMinifier { $pos = $end; $newlineFound = false; - // Output a newline after the token if required - // This is checked before AND after switching state - $newlineAdded = false; - if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][$type] ) ) { - $out .= "\n"; - $lineLength = 0; - $newlineAdded = true; - } - // Now that we have output our token, transition into the new state. if( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) { $stack[] = $push[$state][$type]; @@ -598,12 +577,6 @@ class JavaScriptMinifier { } elseif( isset( $goto[$state][$type] ) ) { $state = $goto[$state][$type]; } - - // Check for newline insertion again - if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][$type] ) ) { - $out .= "\n"; - $lineLength = 0; - } } return $out; } diff --git a/includes/libs/MWMessagePack.php b/includes/libs/MWMessagePack.php index a9da3660b0..be7e93d53e 100644 --- a/includes/libs/MWMessagePack.php +++ b/includes/libs/MWMessagePack.php @@ -53,137 +53,137 @@ class MWMessagePack { } switch ( gettype( $value ) ) { - case 'NULL': - return "\xC0"; + case 'NULL': + return "\xC0"; - case 'boolean': - return $value ? "\xC3" : "\xC2"; + case 'boolean': + return $value ? "\xC3" : "\xC2"; - case 'double': - case 'float': - return self::$bigendian - ? "\xCB" . pack( 'd', $value ) - : "\xCB" . strrev( pack( 'd', $value ) ); + case 'double': + case 'float': + return self::$bigendian + ? "\xCB" . pack( 'd', $value ) + : "\xCB" . strrev( pack( 'd', $value ) ); - case 'string': - $length = strlen( $value ); - if ( $length < 32 ) { - return pack( 'Ca*', 0xA0 | $length, $value ); - } elseif ( $length <= 0xFFFF ) { - return pack( 'Cna*', 0xDA, $length, $value ); - } elseif ( $length <= 0xFFFFFFFF ) { - return pack( 'CNa*', 0xDB, $length, $value ); - } - throw new InvalidArgumentException( __METHOD__ - . ": string too long (length: $length; max: 4294967295)" ); - - case 'integer': - if ( $value >= 0 ) { - if ( $value <= 0x7F ) { - // positive fixnum - return chr( $value ); - } - if ( $value <= 0xFF ) { - // uint8 - return pack( 'CC', 0xCC, $value ); - } - if ( $value <= 0xFFFF ) { - // uint16 - return pack( 'Cn', 0xCD, $value ); - } - if ( $value <= 0xFFFFFFFF ) { - // uint32 - return pack( 'CN', 0xCE, $value ); - } - if ( $value <= 0xFFFFFFFFFFFFFFFF ) { - // uint64 - $hi = ( $value & 0xFFFFFFFF00000000 ) >> 32; - $lo = $value & 0xFFFFFFFF; - return self::$bigendian - ? pack( 'CNN', 0xCF, $lo, $hi ) - : pack( 'CNN', 0xCF, $hi, $lo ); - } - } else { - if ( $value >= -32 ) { - // negative fixnum - return pack( 'c', $value ); - } - if ( $value >= -0x80 ) { - // int8 - return pack( 'Cc', 0xD0, $value ); - } - if ( $value >= -0x8000 ) { - // int16 - $p = pack( 's', $value ); - return self::$bigendian - ? pack( 'Ca2', 0xD1, $p ) - : pack( 'Ca2', 0xD1, strrev( $p ) ); - } - if ( $value >= -0x80000000 ) { - // int32 - $p = pack( 'l', $value ); - return self::$bigendian - ? pack( 'Ca4', 0xD2, $p ) - : pack( 'Ca4', 0xD2, strrev( $p ) ); - } - if ( $value >= -0x8000000000000000 ) { - // int64 - // pack() does not support 64-bit ints either so pack into two 32-bits - $p1 = pack( 'l', $value & 0xFFFFFFFF ); - $p2 = pack( 'l', ( $value >> 32 ) & 0xFFFFFFFF ); - return self::$bigendian - ? pack( 'Ca4a4', 0xD3, $p1, $p2 ) - : pack( 'Ca4a4', 0xD3, strrev( $p2 ), strrev( $p1 ) ); + case 'string': + $length = strlen( $value ); + if ( $length < 32 ) { + return pack( 'Ca*', 0xA0 | $length, $value ); + } elseif ( $length <= 0xFFFF ) { + return pack( 'Cna*', 0xDA, $length, $value ); + } elseif ( $length <= 0xFFFFFFFF ) { + return pack( 'CNa*', 0xDB, $length, $value ); } - } - throw new InvalidArgumentException( __METHOD__ . ": invalid integer '$value'" ); - - case 'array': - $buffer = ''; - $length = count( $value ); - if ( $length > 0xFFFFFFFF ) { throw new InvalidArgumentException( __METHOD__ - . ": array too long (length: $length, max: 4294967295)" ); - } + . ": string too long (length: $length; max: 4294967295)" ); - $index = 0; - foreach ( $value as $k => $v ) { - if ( $index !== $k || $index === $length ) { - break; + case 'integer': + if ( $value >= 0 ) { + if ( $value <= 0x7F ) { + // positive fixnum + return chr( $value ); + } + if ( $value <= 0xFF ) { + // uint8 + return pack( 'CC', 0xCC, $value ); + } + if ( $value <= 0xFFFF ) { + // uint16 + return pack( 'Cn', 0xCD, $value ); + } + if ( $value <= 0xFFFFFFFF ) { + // uint32 + return pack( 'CN', 0xCE, $value ); + } + if ( $value <= 0xFFFFFFFFFFFFFFFF ) { + // uint64 + $hi = ( $value & 0xFFFFFFFF00000000 ) >> 32; + $lo = $value & 0xFFFFFFFF; + return self::$bigendian + ? pack( 'CNN', 0xCF, $lo, $hi ) + : pack( 'CNN', 0xCF, $hi, $lo ); + } } else { - $index++; + if ( $value >= -32 ) { + // negative fixnum + return pack( 'c', $value ); + } + if ( $value >= -0x80 ) { + // int8 + return pack( 'Cc', 0xD0, $value ); + } + if ( $value >= -0x8000 ) { + // int16 + $p = pack( 's', $value ); + return self::$bigendian + ? pack( 'Ca2', 0xD1, $p ) + : pack( 'Ca2', 0xD1, strrev( $p ) ); + } + if ( $value >= -0x80000000 ) { + // int32 + $p = pack( 'l', $value ); + return self::$bigendian + ? pack( 'Ca4', 0xD2, $p ) + : pack( 'Ca4', 0xD2, strrev( $p ) ); + } + if ( $value >= -0x8000000000000000 ) { + // int64 + // pack() does not support 64-bit ints either so pack into two 32-bits + $p1 = pack( 'l', $value & 0xFFFFFFFF ); + $p2 = pack( 'l', ( $value >> 32 ) & 0xFFFFFFFF ); + return self::$bigendian + ? pack( 'Ca4a4', 0xD3, $p1, $p2 ) + : pack( 'Ca4a4', 0xD3, strrev( $p2 ), strrev( $p1 ) ); + } } - } - $associative = $index !== $length; + throw new InvalidArgumentException( __METHOD__ . ": invalid integer '$value'" ); - if ( $associative ) { - if ( $length < 16 ) { - $buffer .= pack( 'C', 0x80 | $length ); - } elseif ( $length <= 0xFFFF ) { - $buffer .= pack( 'Cn', 0xDE, $length ); - } else { - $buffer .= pack( 'CN', 0xDF, $length ); + case 'array': + $buffer = ''; + $length = count( $value ); + if ( $length > 0xFFFFFFFF ) { + throw new InvalidArgumentException( __METHOD__ + . ": array too long (length: $length, max: 4294967295)" ); } + + $index = 0; foreach ( $value as $k => $v ) { - $buffer .= self::pack( $k ); - $buffer .= self::pack( $v ); + if ( $index !== $k || $index === $length ) { + break; + } else { + $index++; + } } - } else { - if ( $length < 16 ) { - $buffer .= pack( 'C', 0x90 | $length ); - } elseif ( $length <= 0xFFFF ) { - $buffer .= pack( 'Cn', 0xDC, $length ); + $associative = $index !== $length; + + if ( $associative ) { + if ( $length < 16 ) { + $buffer .= pack( 'C', 0x80 | $length ); + } elseif ( $length <= 0xFFFF ) { + $buffer .= pack( 'Cn', 0xDE, $length ); + } else { + $buffer .= pack( 'CN', 0xDF, $length ); + } + foreach ( $value as $k => $v ) { + $buffer .= self::pack( $k ); + $buffer .= self::pack( $v ); + } } else { - $buffer .= pack( 'CN', 0xDD, $length ); - } - foreach ( $value as $v ) { - $buffer .= self::pack( $v ); + if ( $length < 16 ) { + $buffer .= pack( 'C', 0x90 | $length ); + } elseif ( $length <= 0xFFFF ) { + $buffer .= pack( 'Cn', 0xDC, $length ); + } else { + $buffer .= pack( 'CN', 0xDD, $length ); + } + foreach ( $value as $v ) { + $buffer .= self::pack( $v ); + } } - } - return $buffer; + return $buffer; - default: - throw new InvalidArgumentException( __METHOD__ . ': unsupported type ' . gettype( $value ) ); + default: + throw new InvalidArgumentException( __METHOD__ . ': unsupported type ' . gettype( $value ) ); } } } diff --git a/includes/libs/mime/XmlTypeCheck.php b/includes/libs/mime/XmlTypeCheck.php index ea7f9a6ca7..97611086b0 100644 --- a/includes/libs/mime/XmlTypeCheck.php +++ b/includes/libs/mime/XmlTypeCheck.php @@ -479,23 +479,23 @@ class XmlTypeCheck { continue; } switch ( $field ) { - case 'typepublic': - case 'typesystem': - $parsed['type'] = $value; - break; - case 'pubquote': - case 'pubapos': - $parsed['publicid'] = $value; - break; - case 'pubsysquote': - case 'pubsysapos': - case 'sysquote': - case 'sysapos': - $parsed['systemid'] = $value; - break; - case 'internal': - $parsed['internal'] = $value; - break; + case 'typepublic': + case 'typesystem': + $parsed['type'] = $value; + break; + case 'pubquote': + case 'pubapos': + $parsed['publicid'] = $value; + break; + case 'pubsysquote': + case 'pubsysapos': + case 'sysquote': + case 'sysapos': + $parsed['systemid'] = $value; + break; + case 'internal': + $parsed['internal'] = $value; + break; } } return $parsed; diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php index 8f2c72a141..562819ec4e 100644 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -225,8 +225,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { */ public static function newEmpty() { return new static( [ - 'cache' => new EmptyBagOStuff(), - 'pool' => 'empty' + 'cache' => new EmptyBagOStuff() ] ); } @@ -901,6 +900,40 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * ); * @endcode * + * Example usage (key holding an LRU subkey:value map; this can avoid flooding cache with + * keys for an unlimited set of (constraint,situation) pairs, thereby avoiding elevated + * cache evictions and wasted memory): + * @code + * $catSituationTolerabilityCache = $this->cache->getWithSetCallback( + * // Group by constraint ID/hash, cat family ID/hash, or something else useful + * $this->cache->makeKey( 'cat-situation-tolerablity-checks', $groupKey ), + * WANObjectCache::TTL_DAY, // rarely used groups should fade away + * // The $scenarioKey format is $constraintId: + * function ( $cacheMap ) use ( $scenarioKey, $constraintId, $situation ) { + * $lruCache = MapCacheLRU::newFromArray( $cacheMap ?: [], self::CACHE_SIZE ); + * $result = $lruCache->get( $scenarioKey ); // triggers LRU bump if present + * if ( $result === null || $this->isScenarioResultExpired( $result ) ) { + * $result = $this->checkScenarioTolerability( $constraintId, $situation ); + * $lruCache->set( $scenarioKey, $result, 3 / 8 ); + * } + * // Save the new LRU cache map and reset the map's TTL + * return $lruCache->toArray(); + * }, + * [ + * // Once map is > 1 sec old, consider refreshing + * 'ageNew' => 1, + * // Update within 5 seconds after "ageNew" given a 1hz cache check rate + * 'hotTTR' => 5, + * // Avoid querying cache servers multiple times in a request; this also means + * // that a request can only alter the value of any given constraint key once + * 'pcTTL' => WANObjectCache::TTL_PROC_LONG + * ] + * ); + * $tolerability = isset( $catSituationTolerabilityCache[$scenarioKey] ) + * ? $catSituationTolerabilityCache[$scenarioKey] + * : $this->checkScenarioTolerability( $constraintId, $situation ); + * @endcode + * * @see WANObjectCache::get() * @see WANObjectCache::set() * @@ -1341,8 +1374,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * This works the same as getWithSetCallback() except: * - a) The $keys argument expects the result of WANObjectCache::makeMultiKeys() * - b) The $callback argument expects a callback returning a map of (ID => new value) - * for all entity IDs in $regenById and it takes the following arguments: - * - $ids: a list of entity IDs to regenerate + * for all entity IDs in $ids and it takes the following arguments: + * - $ids: a list of entity IDs that require cache regeneration * - &$ttls: a reference to the (entity ID => new TTL) map * - &$setOpts: a reference to options for set() which can be altered * - c) The return value is a map of (cache key => value) in the order of $keyedIds @@ -1679,7 +1712,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * $ttl = ( $newList === $oldValue ) * // No change: cache for 150% of the age of $oldValue * ? $cache->adaptiveTTL( $oldAsOf, $maxTTL, $minTTL, 1.5 ) - * // Changed: cache for %50 of the age of $oldValue + * // Changed: cache for 50% of the age of $oldValue * : $cache->adaptiveTTL( $oldAsOf, $maxTTL, $minTTL, .5 ); * } * diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 7f0718c952..15e02ad085 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -908,6 +908,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } if ( $isWrite ) { + if ( $this->getLBInfo( 'replica' ) === true ) { + throw new DBError( + $this, + 'Write operations are not allowed on replica database connections.' + ); + } # In theory, non-persistent writes are allowed in read-only mode, but due to things # like https://bugs.mysql.com/bug.php?id=33669 that might not work anyway... $reason = $this->getReadOnlyReason(); diff --git a/includes/libs/xmp/XMP.php b/includes/libs/xmp/XMP.php index be823a8bc5..931c085867 100644 --- a/includes/libs/xmp/XMP.php +++ b/includes/libs/xmp/XMP.php @@ -129,11 +129,17 @@ class XMPReader implements LoggerAwareInterface { */ private $logger; + /** + * @var string + */ + private $filename; + /** * Primary job is to initialize the XMLParser * @param LoggerInterface|null $logger + * @param string $filename */ - function __construct( LoggerInterface $logger = null ) { + function __construct( LoggerInterface $logger = null, $filename = 'unknown' ) { if ( !function_exists( 'xml_parser_create_ns' ) ) { // this should already be checked by this point throw new RuntimeException( 'XMP support requires XML Parser' ); @@ -143,6 +149,7 @@ class XMPReader implements LoggerAwareInterface { } else { $this->setLogger( new NullLogger() ); } + $this->filename = $filename; $this->items = XMPInfo::getItems(); @@ -372,11 +379,13 @@ class XMPReader implements LoggerAwareInterface { $this->logger->info( '{method} : Error reading XMP content: {error} ' . - '(line: {line} column: {column} byte offset: {offset})', + '(file: {file}, line: {line} column: {column} ' . + 'byte offset: {offset})', [ 'method' => __METHOD__, 'error_code' => $code, 'error' => $error, + 'file' => $this->filename, 'line' => $line, 'column' => $col, 'offset' => $offset, @@ -392,6 +401,7 @@ class XMPReader implements LoggerAwareInterface { [ 'method' => __METHOD__, 'exception' => $e, + 'file' => $this->filename, 'content' => $content, ] ); @@ -421,7 +431,11 @@ class XMPReader implements LoggerAwareInterface { ) { $this->logger->info( __METHOD__ . " Ignoring XMPExtended block due to wrong guid (guid= '{guid}')", - [ 'guid' => 'guid' ] ); + [ + 'guid' => $guid, + 'file' => $this->filename, + ] + ); return false; } @@ -433,7 +447,8 @@ class XMPReader implements LoggerAwareInterface { $len['offset'] > $len['length'] ) { $this->logger->info( - __METHOD__ . 'Error reading extended XMP block, invalid length or offset.' + __METHOD__ . 'Error reading extended XMP block, invalid length or offset.', + [ 'file' => $this->filename ] ); return false; @@ -451,7 +466,9 @@ class XMPReader implements LoggerAwareInterface { if ( $len['offset'] !== $this->extendedXMPOffset ) { $this->logger->info( __METHOD__ . 'Ignoring XMPExtended block due to wrong order. (Offset was ' - . $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')' ); + . $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')', + [ 'file' => $this->filename ] + ); return false; } @@ -472,7 +489,10 @@ class XMPReader implements LoggerAwareInterface { $atEnd = false; } - $this->logger->debug( __METHOD__ . 'Parsing a XMPExtended block' ); + $this->logger->debug( + __METHOD__ . 'Parsing a XMPExtended block', + [ 'file' => $this->filename ] + ); return $this->parse( $actualContent, $atEnd ); } @@ -668,19 +688,28 @@ class XMPReader implements LoggerAwareInterface { if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) { // This can happen if all the members of the struct failed validation. - $this->logger->debug( __METHOD__ . " <$ns:$tag> has no valid members." ); + $this->logger->debug( + __METHOD__ . " <$ns:$tag> has no valid members.", + [ 'file' => $this->filename ] + ); } elseif ( is_callable( $validate ) ) { $val =& $this->results['xmp-' . $info['map_group']][$finalName]; call_user_func_array( $validate, [ $info, &$val, false ] ); if ( is_null( $val ) ) { // the idea being the validation function will unset the variable if // its invalid. - $this->logger->info( __METHOD__ . " <$ns:$tag> failed validation." ); + $this->logger->info( + __METHOD__ . " <$ns:$tag> failed validation.", + [ 'file' => $this->filename ] + ); unset( $this->results['xmp-' . $info['map_group']][$finalName] ); } } else { - $this->logger->warning( __METHOD__ . " Validation function for $finalName (" - . $validate[0] . '::' . $validate[1] . '()) is not callable.' ); + $this->logger->warning( + __METHOD__ . " Validation function for $finalName (" . + $validate[0] . '::' . $validate[1] . '()) is not callable.', + [ 'file' => $this->filename ] + ); } } @@ -719,7 +748,10 @@ class XMPReader implements LoggerAwareInterface { array_shift( $this->mode ); if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) { - $this->logger->debug( __METHOD__ . " Empty compund element $finalName." ); + $this->logger->debug( + __METHOD__ . " Empty compund element $finalName.", + [ 'file' => $this->filename ] + ); return; } @@ -787,7 +819,10 @@ class XMPReader implements LoggerAwareInterface { if ( $elm === self::NS_RDF . ' type' ) { // these aren't really supported properly yet. // However, it appears they almost never used. - $this->logger->info( __METHOD__ . ' encountered ' ); + $this->logger->info( + __METHOD__ . ' encountered ', + [ 'file' => $this->filename ] + ); } if ( strpos( $elm, ' ' ) === false ) { @@ -795,7 +830,10 @@ class XMPReader implements LoggerAwareInterface { // However, there is a bug in an adobe product // that forgets the namespace on some things. // (Luckily they are unimportant things). - $this->logger->info( __METHOD__ . " Encountered which has no namespace. Skipping." ); + $this->logger->info( + __METHOD__ . " Encountered which has no namespace. Skipping.", + [ 'file' => $this->filename ] + ); return; } @@ -841,7 +879,10 @@ class XMPReader implements LoggerAwareInterface { $this->endElementModeQDesc( $elm ); break; default: - $this->logger->warning( __METHOD__ . " no mode (elm = $elm)" ); + $this->logger->info( + __METHOD__ . " no mode (elm = $elm)", + [ 'file' => $this->filename ] + ); break; } } @@ -891,8 +932,11 @@ class XMPReader implements LoggerAwareInterface { array_unshift( $this->mode, self::MODE_LI ); } elseif ( $elm === self::NS_RDF . ' Bag' ) { # T29105 - $this->logger->info( __METHOD__ . ' Expected an rdf:Seq, but got an rdf:Bag. Pretending' - . ' it is a Seq, since some buggy software is known to screw this up.' ); + $this->logger->info( + __METHOD__ . ' Expected an rdf:Seq, but got an rdf:Bag. Pretending' . + ' it is a Seq, since some buggy software is known to screw this up.', + [ 'file' => $this->filename ] + ); array_unshift( $this->mode, self::MODE_LI ); } else { throw new RuntimeException( "Expected but got $elm." ); @@ -956,7 +1000,12 @@ class XMPReader implements LoggerAwareInterface { // something else we don't recognize, like a qualifier maybe. $this->logger->info( __METHOD__ . " Encountered element <{element}> where only expecting character data as value of {curitem}", - [ 'element' => $elm, 'curitem' => $this->curItem[0] ] ); + [ + 'element' => $elm, + 'curitem' => $this->curItem[0], + 'file' => $this->filename, + ] + ); array_unshift( $this->mode, self::MODE_IGNORE ); array_unshift( $this->curItem, $elm ); } @@ -1006,9 +1055,9 @@ class XMPReader implements LoggerAwareInterface { // a child of a struct), then something weird is // happening, so ignore this element and its children. - $this->logger->warning( + $this->logger->info( 'Encountered <{element}> outside of its expected parent. Ignoring.', - [ 'element' => "$ns:$tag" ] + [ 'element' => "$ns:$tag", 'file' => $this->filename ] ); array_unshift( $this->mode, self::MODE_IGNORE ); @@ -1031,7 +1080,7 @@ class XMPReader implements LoggerAwareInterface { } else { // This element is not on our list of allowed elements so ignore. $this->logger->debug( __METHOD__ . ' Ignoring unrecognized element <{element}>.', - [ 'element' => "$ns:$tag" ] ); + [ 'element' => "$ns:$tag", 'file' => $this->filename ] ); array_unshift( $this->mode, self::MODE_IGNORE ); array_unshift( $this->curItem, $ns . ' ' . $tag ); @@ -1208,12 +1257,18 @@ class XMPReader implements LoggerAwareInterface { // on page 25 of part 1 of the xmp standard. // Also it seems as if exiv2 and exiftool do not support // this either (That or I misunderstand the standard) - $this->logger->info( __METHOD__ . ' Encountered which isn\'t currently supported' ); + $this->logger->info( + __METHOD__ . ' Encountered which isn\'t currently supported', + [ 'file' => $this->filename ] + ); } if ( strpos( $elm, ' ' ) === false ) { // This probably shouldn't happen. - $this->logger->info( __METHOD__ . " Encountered <$elm> which has no namespace. Skipping." ); + $this->logger->info( + __METHOD__ . " Encountered <$elm> which has no namespace. Skipping.", + [ 'file' => $this->filename ] + ); return; } @@ -1295,8 +1350,11 @@ class XMPReader implements LoggerAwareInterface { if ( strpos( $name, ' ' ) === false ) { // This shouldn't happen, but so far some old software forgets namespace // on rdf:about. - $this->logger->info( __METHOD__ . ' Encountered non-namespaced attribute: ' - . " $name=\"$val\". Skipping. " ); + $this->logger->info( + __METHOD__ . ' Encountered non-namespaced attribute: ' . + " $name=\"$val\". Skipping. ", + [ 'file' => $this->filename ] + ); continue; } list( $ns, $tag ) = explode( ' ', $name, 2 ); @@ -1313,7 +1371,10 @@ class XMPReader implements LoggerAwareInterface { } $this->saveValue( $ns, $tag, $val ); } else { - $this->logger->debug( __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." ); + $this->logger->debug( + __METHOD__ . " Ignoring unrecognized element <$ns:$tag>.", + [ 'file' => $this->filename ] + ); } } } @@ -1346,13 +1407,19 @@ class XMPReader implements LoggerAwareInterface { // the reasoning behind using &$val instead of using the return value // is to be consistent between here and validating structures. if ( is_null( $val ) ) { - $this->logger->info( __METHOD__ . " <$ns:$tag> failed validation." ); + $this->logger->info( + __METHOD__ . " <$ns:$tag> failed validation.", + [ 'file' => $this->filename ] + ); return; } } else { - $this->logger->warning( __METHOD__ . " Validation function for $finalName (" - . $validate[0] . '::' . $validate[1] . '()) is not callable.' ); + $this->logger->warning( + __METHOD__ . " Validation function for $finalName (" . + $validate[0] . '::' . $validate[1] . '()) is not callable.', + [ 'file' => $this->filename ] + ); } } diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php index ac39e6f3d4..617a910295 100644 --- a/includes/media/Bitmap.php +++ b/includes/media/Bitmap.php @@ -112,14 +112,14 @@ class BitmapHandler extends TransformationalImageHandler { */ protected function imageMagickSubsampling( $pixelFormat ) { switch ( $pixelFormat ) { - case 'yuv444': - return [ '1x1', '1x1', '1x1' ]; - case 'yuv422': - return [ '2x1', '1x1', '1x1' ]; - case 'yuv420': - return [ '2x2', '1x1', '1x1' ]; - default: - throw new MWException( 'Invalid pixel format for JPEG output' ); + case 'yuv444': + return [ '1x1', '1x1', '1x1' ]; + case 'yuv422': + return [ '2x1', '1x1', '1x1' ]; + case 'yuv420': + return [ '2x2', '1x1', '1x1' ]; + default: + throw new MWException( 'Invalid pixel format for JPEG output' ); } } diff --git a/includes/media/BitmapMetadataHandler.php b/includes/media/BitmapMetadataHandler.php index 35c97518ea..2ed5db36f7 100644 --- a/includes/media/BitmapMetadataHandler.php +++ b/includes/media/BitmapMetadataHandler.php @@ -170,7 +170,7 @@ class BitmapMetadataHandler { } } if ( isset( $seg['XMP'] ) && $showXMP ) { - $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) ); + $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ), $filename ); $xmp->parse( $seg['XMP'] ); foreach ( $seg['XMP_ext'] as $xmpExt ) { /* Support for extended xmp in jpeg files @@ -205,7 +205,7 @@ class BitmapMetadataHandler { if ( isset( $array['text']['xmp']['x-default'] ) && $array['text']['xmp']['x-default'] !== '' && $showXMP ) { - $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) ); + $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ), $filename ); $xmp->parse( $array['text']['xmp']['x-default'] ); $xmpRes = $xmp->getResults(); foreach ( $xmpRes as $type => $xmpSection ) { @@ -238,7 +238,7 @@ class BitmapMetadataHandler { } if ( $baseArray['xmp'] !== '' && XMPReader::isSupported() ) { - $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) ); + $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ), $filename ); $xmp->parse( $baseArray['xmp'] ); $xmpRes = $xmp->getResults(); foreach ( $xmpRes as $type => $xmpSection ) { diff --git a/includes/media/XCF.php b/includes/media/XCF.php index c41952408f..3587eba656 100644 --- a/includes/media/XCF.php +++ b/includes/media/XCF.php @@ -162,18 +162,17 @@ class XCFHandler extends BitmapHandler { // Unclear from base media type if it has an alpha layer, // so just assume that it does since it "potentially" could. switch ( $header['base_type'] ) { - case 0: - $metadata['colorType'] = 'truecolour-alpha'; - break; - case 1: - $metadata['colorType'] = 'greyscale-alpha'; - break; - case 2: - $metadata['colorType'] = 'index-coloured'; - break; - default: - $metadata['colorType'] = 'unknown'; - + case 0: + $metadata['colorType'] = 'truecolour-alpha'; + break; + case 1: + $metadata['colorType'] = 'greyscale-alpha'; + break; + case 2: + $metadata['colorType'] = 'index-coloured'; + break; + default: + $metadata['colorType'] = 'unknown'; } } else { // Marker to prevent repeated attempted extraction diff --git a/includes/page/PageArchive.php b/includes/page/PageArchive.php index c03d6b21d6..0ef038f8dc 100644 --- a/includes/page/PageArchive.php +++ b/includes/page/PageArchive.php @@ -176,44 +176,32 @@ class PageArchive { * @return ResultWrapper */ public function listRevisions() { - $dbr = wfGetDB( DB_REPLICA ); - $commentQuery = CommentStore::newKey( 'ar_comment' )->getJoin(); - - $tables = [ 'archive' ] + $commentQuery['tables']; - - $fields = [ - 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', - 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1', - 'ar_page_id' - ] + $commentQuery['fields']; - - if ( $this->config->get( 'ContentHandlerUseDB' ) ) { - $fields[] = 'ar_content_format'; - $fields[] = 'ar_content_model'; - } - - $conds = [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ]; + $revisionStore = MediaWikiServices::getInstance()->getRevisionStore(); + $queryInfo = $revisionStore->getArchiveQueryInfo(); + $conds = [ + 'ar_namespace' => $this->title->getNamespace(), + 'ar_title' => $this->title->getDBkey(), + ]; $options = [ 'ORDER BY' => 'ar_timestamp DESC' ]; - $join_conds = [] + $commentQuery['joins']; - ChangeTags::modifyDisplayQuery( - $tables, - $fields, + $queryInfo['tables'], + $queryInfo['fields'], $conds, - $join_conds, + $queryInfo['joins'], $options, '' ); - return $dbr->select( $tables, - $fields, + $dbr = wfGetDB( DB_REPLICA ); + return $dbr->select( + $queryInfo['tables'], + $queryInfo['fields'], $conds, __METHOD__, $options, - $join_conds + $queryInfo['joins'] ); } @@ -267,7 +255,11 @@ class PageArchive { ); if ( $row ) { - return Revision::newFromArchiveRow( $row, [ 'title' => $this->title ] ); + return Revision::newFromArchiveRow( + $row, + [ 'title' => $this->title ], + $this->title + ); } return null; @@ -542,47 +534,23 @@ class PageArchive { $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps ); } - $commentQuery = CommentStore::newKey( 'ar_comment' )->getJoin(); - - $tables = [ 'archive', 'revision' ] + $commentQuery['tables']; - - $fields = [ - 'ar_id', - 'ar_rev_id', - 'rev_id', - 'ar_text', - 'ar_user', - 'ar_user_text', - 'ar_timestamp', - 'ar_minor_edit', - 'ar_flags', - 'ar_text_id', - 'ar_deleted', - 'ar_page_id', - 'ar_len', - 'ar_sha1' - ] + $commentQuery['fields']; - - if ( $this->config->get( 'ContentHandlerUseDB' ) ) { - $fields[] = 'ar_content_format'; - $fields[] = 'ar_content_model'; - } - - $join_conds = [ - 'revision' => [ 'LEFT JOIN', 'ar_rev_id=rev_id' ], - ] + $commentQuery['joins']; + $revisionStore = MediaWikiServices::getInstance()->getRevisionStore(); + $queryInfo = $revisionStore->getArchiveQueryInfo(); + $queryInfo['tables'][] = 'revision'; + $queryInfo['fields'][] = 'rev_id'; + $queryInfo['joins']['revision'] = [ 'LEFT JOIN', 'ar_rev_id=rev_id' ]; /** * Select each archived revision... */ $result = $dbw->select( - $tables, - $fields, + $queryInfo['tables'], + $queryInfo['fields'], $oldWhere, __METHOD__, /* options */ [ 'ORDER BY' => 'ar_timestamp' ], - $join_conds + $queryInfo['joins'] ); $rev_count = $result->numRows(); @@ -640,10 +608,12 @@ class PageArchive { $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook // grab the content to check consistency with global state before restoring the page. - $revision = Revision::newFromArchiveRow( $latestRestorableRow, + $revision = Revision::newFromArchiveRow( + $latestRestorableRow, [ 'title' => $article->getTitle(), // used to derive default content model - ] + ], + $article->getTitle() ); $user = User::newFromName( $revision->getUserText( Revision::RAW ), false ); $content = $revision->getContent( Revision::RAW ); @@ -706,12 +676,15 @@ class PageArchive { } // Insert one revision at a time...maintaining deletion status // unless we are specifically removing all restrictions... - $revision = Revision::newFromArchiveRow( $row, + $revision = Revision::newFromArchiveRow( + $row, [ 'page' => $pageId, 'title' => $this->title, 'deleted' => $unsuppress ? 0 : $row->ar_deleted - ] ); + ], + $this->title + ); // This will also copy the revision to ip_changes if it was an IP edit. $revision->insertOn( $dbw ); diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index ff997ab238..6af7945730 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -23,6 +23,7 @@ use MediaWiki\Edit\PreparedEdit; use \MediaWiki\Logger\LoggerFactory; use \MediaWiki\MediaWikiServices; +use Wikimedia\Assert\Assert; use Wikimedia\Rdbms\FakeResultWrapper; use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\DBError; @@ -194,15 +195,15 @@ class WikiPage implements Page, IDBAccessObject { */ private static function convertSelectType( $type ) { switch ( $type ) { - case 'fromdb': - return self::READ_NORMAL; - case 'fromdbmaster': - return self::READ_LATEST; - case 'forupdate': - return self::READ_LOCKING; - default: - // It may already be an integer or whatever else - return $type; + case 'fromdb': + return self::READ_NORMAL; + case 'fromdbmaster': + return self::READ_LATEST; + case 'forupdate': + return self::READ_LOCKING; + default: + // It may already be an integer or whatever else + return $type; } } @@ -671,7 +672,7 @@ class WikiPage implements Page, IDBAccessObject { $revision = Revision::newFromPageId( $this->getId(), $latest, $flags ); } else { $dbr = wfGetDB( DB_REPLICA ); - $revision = Revision::newKnownCurrent( $dbr, $this->getId(), $latest ); + $revision = Revision::newKnownCurrent( $dbr, $this->getTitle(), $latest ); } if ( $revision ) { // sanity @@ -1264,8 +1265,11 @@ class WikiPage implements Page, IDBAccessObject { $conditions['page_latest'] = $lastRevision; } + $revId = $revision->getId(); + Assert::parameter( $revId > 0, '$revision->getId()', 'must be > 0' ); + $row = [ /* SET */ - 'page_latest' => $revision->getId(), + 'page_latest' => $revId, 'page_touched' => $dbw->timestamp( $revision->getTimestamp() ), 'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0, 'page_is_redirect' => $rt !== null ? 1 : 0, @@ -1624,6 +1628,11 @@ class WikiPage implements Page, IDBAccessObject { $tags[] = $tag; } + // Check for undo tag + if ( $undidRevId !== 0 && in_array( 'mw-undo', ChangeTags::getSoftwareTags() ) ) { + $tags[] = 'mw-undo'; + } + // Provide autosummaries if summary is not provided and autosummaries are enabled if ( $wgUseAutomaticEditSummaries && ( $flags & EDIT_AUTOSUMMARY ) && $summary == '' ) { $summary = $handler->getAutosummary( $old_content, $content, $flags ); diff --git a/includes/parser/BlockLevelPass.php b/includes/parser/BlockLevelPass.php index fab9ab7fb1..761d5be89b 100644 --- a/includes/parser/BlockLevelPass.php +++ b/includes/parser/BlockLevelPass.php @@ -417,130 +417,130 @@ class BlockLevelPass { $c = $str[$i]; switch ( $state ) { - case self::COLON_STATE_TEXT: - switch ( $c ) { - case "<": - # Could be either a tag or an tag - $state = self::COLON_STATE_TAGSTART; - break; - case ":": - if ( $ltLevel === 0 ) { - # We found it! - $before = substr( $str, 0, $i ); - $after = substr( $str, $i + 1 ); - return $i; + case self::COLON_STATE_TEXT: + switch ( $c ) { + case "<": + # Could be either a tag or an tag + $state = self::COLON_STATE_TAGSTART; + break; + case ":": + if ( $ltLevel === 0 ) { + # We found it! + $before = substr( $str, 0, $i ); + $after = substr( $str, $i + 1 ); + return $i; + } + # Embedded in a tag; don't break it. + break; + default: + # Skip ahead looking for something interesting + if ( !preg_match( '/:|<|-\{/', $str, $m, PREG_OFFSET_CAPTURE, $i ) ) { + # Nothing else interesting + return false; + } + if ( $m[0][0] === '-{' ) { + $state = self::COLON_STATE_LC; + $lcLevel++; + $i = $m[0][1] + 1; + } else { + # Skip ahead to next interesting character. + $i = $m[0][1] - 1; + } + break; } - # Embedded in a tag; don't break it. break; - default: - # Skip ahead looking for something interesting - if ( !preg_match( '/:|<|-\{/', $str, $m, PREG_OFFSET_CAPTURE, $i ) ) { - # Nothing else interesting - return false; - } - if ( $m[0][0] === '-{' ) { - $state = self::COLON_STATE_LC; + case self::COLON_STATE_LC: + # In language converter markup -{ ... }- + if ( !preg_match( '/-\{|\}-/', $str, $m, PREG_OFFSET_CAPTURE, $i ) ) { + # Nothing else interesting to find; abort! + # We're nested in language converter markup, but there + # are no close tags left. Abort! + break 2; + } elseif ( $m[0][0] === '-{' ) { + $i = $m[0][1] + 1; $lcLevel++; + } elseif ( $m[0][0] === '}-' ) { $i = $m[0][1] + 1; - } else { - # Skip ahead to next interesting character. - $i = $m[0][1] - 1; + $lcLevel--; + if ( $lcLevel === 0 ) { + $state = self::COLON_STATE_TEXT; + } } break; - } - break; - case self::COLON_STATE_LC: - # In language converter markup -{ ... }- - if ( !preg_match( '/-\{|\}-/', $str, $m, PREG_OFFSET_CAPTURE, $i ) ) { - # Nothing else interesting to find; abort! - # We're nested in language converter markup, but there - # are no close tags left. Abort! - break 2; - } elseif ( $m[0][0] === '-{' ) { - $i = $m[0][1] + 1; - $lcLevel++; - } elseif ( $m[0][0] === '}-' ) { - $i = $m[0][1] + 1; - $lcLevel--; - if ( $lcLevel === 0 ) { - $state = self::COLON_STATE_TEXT; + case self::COLON_STATE_TAG: + # In a + switch ( $c ) { + case ">": + $ltLevel++; + $state = self::COLON_STATE_TEXT; + break; + case "/": + # Slash may be followed by >? + $state = self::COLON_STATE_TAGSLASH; + break; + default: + # ignore } - } - break; - case self::COLON_STATE_TAG: - # In a - switch ( $c ) { - case ">": - $ltLevel++; - $state = self::COLON_STATE_TEXT; break; - case "/": - # Slash may be followed by >? - $state = self::COLON_STATE_TAGSLASH; + case self::COLON_STATE_TAGSTART: + switch ( $c ) { + case "/": + $state = self::COLON_STATE_CLOSETAG; + break; + case "!": + $state = self::COLON_STATE_COMMENT; + break; + case ">": + # Illegal early close? This shouldn't happen D: + $state = self::COLON_STATE_TEXT; + break; + default: + $state = self::COLON_STATE_TAG; + } break; - default: - # ignore - } - break; - case self::COLON_STATE_TAGSTART: - switch ( $c ) { - case "/": - $state = self::COLON_STATE_CLOSETAG; + case self::COLON_STATE_CLOSETAG: + # In a + if ( $c === ">" ) { + if ( $ltLevel > 0 ) { + $ltLevel--; + } else { + # ignore the excess close tag, but keep looking for + # colons. (This matches Parsoid behavior.) + wfDebug( __METHOD__ . ": Invalid input; too many close tags\n" ); + } + $state = self::COLON_STATE_TEXT; + } break; - case "!": - $state = self::COLON_STATE_COMMENT; + case self::COLON_STATE_TAGSLASH: + if ( $c === ">" ) { + # Yes, a self-closed tag + $state = self::COLON_STATE_TEXT; + } else { + # Probably we're jumping the gun, and this is an attribute + $state = self::COLON_STATE_TAG; + } break; - case ">": - # Illegal early close? This shouldn't happen D: - $state = self::COLON_STATE_TEXT; + case self::COLON_STATE_COMMENT: + if ( $c === "-" ) { + $state = self::COLON_STATE_COMMENTDASH; + } break; - default: - $state = self::COLON_STATE_TAG; - } - break; - case self::COLON_STATE_CLOSETAG: - # In a - if ( $c === ">" ) { - if ( $ltLevel > 0 ) { - $ltLevel--; + case self::COLON_STATE_COMMENTDASH: + if ( $c === "-" ) { + $state = self::COLON_STATE_COMMENTDASHDASH; } else { - # ignore the excess close tag, but keep looking for - # colons. (This matches Parsoid behavior.) - wfDebug( __METHOD__ . ": Invalid input; too many close tags\n" ); + $state = self::COLON_STATE_COMMENT; } - $state = self::COLON_STATE_TEXT; - } - break; - case self::COLON_STATE_TAGSLASH: - if ( $c === ">" ) { - # Yes, a self-closed tag - $state = self::COLON_STATE_TEXT; - } else { - # Probably we're jumping the gun, and this is an attribute - $state = self::COLON_STATE_TAG; - } - break; - case self::COLON_STATE_COMMENT: - if ( $c === "-" ) { - $state = self::COLON_STATE_COMMENTDASH; - } - break; - case self::COLON_STATE_COMMENTDASH: - if ( $c === "-" ) { - $state = self::COLON_STATE_COMMENTDASHDASH; - } else { - $state = self::COLON_STATE_COMMENT; - } - break; - case self::COLON_STATE_COMMENTDASHDASH: - if ( $c === ">" ) { - $state = self::COLON_STATE_TEXT; - } else { - $state = self::COLON_STATE_COMMENT; - } - break; - default: - throw new MWException( "State machine error in " . __METHOD__ ); + break; + case self::COLON_STATE_COMMENTDASHDASH: + if ( $c === ">" ) { + $state = self::COLON_STATE_TEXT; + } else { + $state = self::COLON_STATE_COMMENT; + } + break; + default: + throw new MWException( "State machine error in " . __METHOD__ ); } } if ( $ltLevel > 0 || $lcLevel > 0 ) { diff --git a/includes/parser/LinkHolderArray.php b/includes/parser/LinkHolderArray.php index bc5182c1dd..816f7f7912 100644 --- a/includes/parser/LinkHolderArray.php +++ b/includes/parser/LinkHolderArray.php @@ -134,7 +134,7 @@ class LinkHolderArray { $maxId = $newKey > $maxId ? $newKey : $maxId; } } - $texts = preg_replace_callback( '/()/', + $texts = preg_replace_callback( '/()/', [ $this, 'mergeForeignCallback' ], $texts ); # Renumber interwiki links @@ -143,7 +143,7 @@ class LinkHolderArray { $this->interwikis[$newKey] = $entry; $maxId = $newKey > $maxId ? $newKey : $maxId; } - $texts = preg_replace_callback( '/()/', + $texts = preg_replace_callback( '/()/', [ $this, 'mergeForeignCallback' ], $texts ); # Set the parent link ID to be beyond the highest used ID @@ -172,7 +172,7 @@ class LinkHolderArray { # Internal links $pos = 0; while ( $pos < strlen( $text ) ) { - if ( !preg_match( '//', + if ( !preg_match( '//', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) { break; @@ -186,7 +186,7 @@ class LinkHolderArray { # Interwiki links $pos = 0; while ( $pos < strlen( $text ) ) { - if ( !preg_match( '//', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) { + if ( !preg_match( '//', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) { break; } $key = $m[1][0]; @@ -249,12 +249,12 @@ class LinkHolderArray { // Use a globally unique ID to keep the objects mergable $key = $this->parent->nextLinkID(); $this->interwikis[$key] = $entry; - $retVal = "{$trail}"; + $retVal = "{$trail}"; } else { $key = $this->parent->nextLinkID(); $ns = $nt->getNamespace(); $this->internals[$ns][$key] = $entry; - $retVal = "{$trail}"; + $retVal = "{$trail}"; } $this->size++; } @@ -374,7 +374,7 @@ class LinkHolderArray { $title = $entry['title']; $query = isset( $entry['query'] ) ? $entry['query'] : []; $key = "$ns:$index"; - $searchkey = ""; + $searchkey = ""; $displayText = $entry['text']; if ( isset( $entry['selflink'] ) ) { $replacePairs[$searchkey] = Linker::makeSelfLinkObj( $title, $displayText, $query ); @@ -408,7 +408,7 @@ class LinkHolderArray { # Do the thing $text = preg_replace_callback( - '/()/', + '/()/', $replacer->cb(), $text ); @@ -437,7 +437,7 @@ class LinkHolderArray { $replacer = new HashtableReplacer( $replacePairs, 1 ); $text = preg_replace_callback( - '//', + '//', $replacer->cb(), $text ); } @@ -612,7 +612,7 @@ class LinkHolderArray { */ public function replaceText( $text ) { $text = preg_replace_callback( - '//', + '//', [ $this, 'replaceTextCallback' ], $text ); diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 75c1faf712..078de3c601 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -406,13 +406,6 @@ class Parser { $text, Title $title, ParserOptions $options, $linestart = true, $clearState = true, $revid = null ) { - /** - * First pass--just handle sections, pass the rest off - * to internalParse() which does all the real work. - */ - - global $wgShowHostnames; - if ( $clearState ) { // We use U+007F DELETE to construct strip markers, so we have to make // sure that this character does not occur in the input text. @@ -474,7 +467,7 @@ class Parser { } } - # Done parsing! Compute runtime adaptive expiry if set + # Compute runtime adaptive expiry if set $this->mOutput->finalizeAdaptiveCacheExpiry(); # Warn if too many heavyweight parser functions were used @@ -485,110 +478,9 @@ class Parser { ); } - # Information on include size limits, for the benefit of users who try to skirt them + # Information on limits, for the benefit of users who try to skirt them if ( $this->mOptions->getEnableLimitReport() ) { - $max = $this->mOptions->getMaxIncludeSize(); - - $cpuTime = $this->mOutput->getTimeSinceStart( 'cpu' ); - if ( $cpuTime !== null ) { - $this->mOutput->setLimitReportData( 'limitreport-cputime', - sprintf( "%.3f", $cpuTime ) - ); - } - - $wallTime = $this->mOutput->getTimeSinceStart( 'wall' ); - $this->mOutput->setLimitReportData( 'limitreport-walltime', - sprintf( "%.3f", $wallTime ) - ); - - $this->mOutput->setLimitReportData( 'limitreport-ppvisitednodes', - [ $this->mPPNodeCount, $this->mOptions->getMaxPPNodeCount() ] - ); - $this->mOutput->setLimitReportData( 'limitreport-ppgeneratednodes', - [ $this->mGeneratedPPNodeCount, $this->mOptions->getMaxGeneratedPPNodeCount() ] - ); - $this->mOutput->setLimitReportData( 'limitreport-postexpandincludesize', - [ $this->mIncludeSizes['post-expand'], $max ] - ); - $this->mOutput->setLimitReportData( 'limitreport-templateargumentsize', - [ $this->mIncludeSizes['arg'], $max ] - ); - $this->mOutput->setLimitReportData( 'limitreport-expansiondepth', - [ $this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth() ] - ); - $this->mOutput->setLimitReportData( 'limitreport-expensivefunctioncount', - [ $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ] - ); - Hooks::run( 'ParserLimitReportPrepare', [ $this, $this->mOutput ] ); - - $limitReport = "NewPP limit report\n"; - if ( $wgShowHostnames ) { - $limitReport .= 'Parsed by ' . wfHostname() . "\n"; - } - $limitReport .= 'Cached time: ' . $this->mOutput->getCacheTime() . "\n"; - $limitReport .= 'Cache expiry: ' . $this->mOutput->getCacheExpiry() . "\n"; - $limitReport .= 'Dynamic content: ' . - ( $this->mOutput->hasDynamicContent() ? 'true' : 'false' ) . - "\n"; - - foreach ( $this->mOutput->getLimitReportData() as $key => $value ) { - if ( Hooks::run( 'ParserLimitReportFormat', - [ $key, &$value, &$limitReport, false, false ] - ) ) { - $keyMsg = wfMessage( $key )->inLanguage( 'en' )->useDatabase( false ); - $valueMsg = wfMessage( [ "$key-value-text", "$key-value" ] ) - ->inLanguage( 'en' )->useDatabase( false ); - if ( !$valueMsg->exists() ) { - $valueMsg = new RawMessage( '$1' ); - } - if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) { - $valueMsg->params( $value ); - $limitReport .= "{$keyMsg->text()}: {$valueMsg->text()}\n"; - } - } - } - // Since we're not really outputting HTML, decode the entities and - // then re-encode the things that need hiding inside HTML comments. - $limitReport = htmlspecialchars_decode( $limitReport ); - // Run deprecated hook - Hooks::run( 'ParserLimitReport', [ $this, &$limitReport ], '1.22' ); - - // Sanitize for comment. Note '‐' in the replacement is U+2010, - // which looks much like the problematic '-'. - $limitReport = str_replace( [ '-', '&' ], [ '‐', '&' ], $limitReport ); - $text .= "\n\n"; - - // Add on template profiling data in human/machine readable way - $dataByFunc = $this->mProfiler->getFunctionStats(); - uasort( $dataByFunc, function ( $a, $b ) { - return $a['real'] < $b['real']; // descending order - } ); - $profileReport = []; - foreach ( array_slice( $dataByFunc, 0, 10 ) as $item ) { - $profileReport[] = sprintf( "%6.2f%% %8.3f %6d %s", - $item['%real'], $item['real'], $item['calls'], - htmlspecialchars( $item['name'] ) ); - } - $text .= "\n"; - - $this->mOutput->setLimitReportData( 'limitreport-timingprofile', $profileReport ); - - // Add other cache related metadata - if ( $wgShowHostnames ) { - $this->mOutput->setLimitReportData( 'cachereport-origin', wfHostname() ); - } - $this->mOutput->setLimitReportData( 'cachereport-timestamp', - $this->mOutput->getCacheTime() ); - $this->mOutput->setLimitReportData( 'cachereport-ttl', - $this->mOutput->getCacheExpiry() ); - $this->mOutput->setLimitReportData( 'cachereport-transientcontent', - $this->mOutput->hasDynamicContent() ); - - if ( $this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) { - wfDebugLog( 'generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' . - $this->mTitle->getPrefixedDBkey() ); - } + $text .= $this->makeLimitReport(); } # Wrap non-interface parser output in a
so it can be targeted @@ -611,6 +503,120 @@ class Parser { return $this->mOutput; } + /** + * Set the limit report data in the current ParserOutput, and return the + * limit report HTML comment. + * + * @return string + */ + protected function makeLimitReport() { + global $wgShowHostnames; + + $maxIncludeSize = $this->mOptions->getMaxIncludeSize(); + + $cpuTime = $this->mOutput->getTimeSinceStart( 'cpu' ); + if ( $cpuTime !== null ) { + $this->mOutput->setLimitReportData( 'limitreport-cputime', + sprintf( "%.3f", $cpuTime ) + ); + } + + $wallTime = $this->mOutput->getTimeSinceStart( 'wall' ); + $this->mOutput->setLimitReportData( 'limitreport-walltime', + sprintf( "%.3f", $wallTime ) + ); + + $this->mOutput->setLimitReportData( 'limitreport-ppvisitednodes', + [ $this->mPPNodeCount, $this->mOptions->getMaxPPNodeCount() ] + ); + $this->mOutput->setLimitReportData( 'limitreport-ppgeneratednodes', + [ $this->mGeneratedPPNodeCount, $this->mOptions->getMaxGeneratedPPNodeCount() ] + ); + $this->mOutput->setLimitReportData( 'limitreport-postexpandincludesize', + [ $this->mIncludeSizes['post-expand'], $maxIncludeSize ] + ); + $this->mOutput->setLimitReportData( 'limitreport-templateargumentsize', + [ $this->mIncludeSizes['arg'], $maxIncludeSize ] + ); + $this->mOutput->setLimitReportData( 'limitreport-expansiondepth', + [ $this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth() ] + ); + $this->mOutput->setLimitReportData( 'limitreport-expensivefunctioncount', + [ $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ] + ); + Hooks::run( 'ParserLimitReportPrepare', [ $this, $this->mOutput ] ); + + $limitReport = "NewPP limit report\n"; + if ( $wgShowHostnames ) { + $limitReport .= 'Parsed by ' . wfHostname() . "\n"; + } + $limitReport .= 'Cached time: ' . $this->mOutput->getCacheTime() . "\n"; + $limitReport .= 'Cache expiry: ' . $this->mOutput->getCacheExpiry() . "\n"; + $limitReport .= 'Dynamic content: ' . + ( $this->mOutput->hasDynamicContent() ? 'true' : 'false' ) . + "\n"; + + foreach ( $this->mOutput->getLimitReportData() as $key => $value ) { + if ( Hooks::run( 'ParserLimitReportFormat', + [ $key, &$value, &$limitReport, false, false ] + ) ) { + $keyMsg = wfMessage( $key )->inLanguage( 'en' )->useDatabase( false ); + $valueMsg = wfMessage( [ "$key-value-text", "$key-value" ] ) + ->inLanguage( 'en' )->useDatabase( false ); + if ( !$valueMsg->exists() ) { + $valueMsg = new RawMessage( '$1' ); + } + if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) { + $valueMsg->params( $value ); + $limitReport .= "{$keyMsg->text()}: {$valueMsg->text()}\n"; + } + } + } + // Since we're not really outputting HTML, decode the entities and + // then re-encode the things that need hiding inside HTML comments. + $limitReport = htmlspecialchars_decode( $limitReport ); + // Run deprecated hook + Hooks::run( 'ParserLimitReport', [ $this, &$limitReport ], '1.22' ); + + // Sanitize for comment. Note '‐' in the replacement is U+2010, + // which looks much like the problematic '-'. + $limitReport = str_replace( [ '-', '&' ], [ '‐', '&' ], $limitReport ); + $text = "\n\n"; + + // Add on template profiling data in human/machine readable way + $dataByFunc = $this->mProfiler->getFunctionStats(); + uasort( $dataByFunc, function ( $a, $b ) { + return $a['real'] < $b['real']; // descending order + } ); + $profileReport = []; + foreach ( array_slice( $dataByFunc, 0, 10 ) as $item ) { + $profileReport[] = sprintf( "%6.2f%% %8.3f %6d %s", + $item['%real'], $item['real'], $item['calls'], + htmlspecialchars( $item['name'] ) ); + } + $text .= "\n"; + + $this->mOutput->setLimitReportData( 'limitreport-timingprofile', $profileReport ); + + // Add other cache related metadata + if ( $wgShowHostnames ) { + $this->mOutput->setLimitReportData( 'cachereport-origin', wfHostname() ); + } + $this->mOutput->setLimitReportData( 'cachereport-timestamp', + $this->mOutput->getCacheTime() ); + $this->mOutput->setLimitReportData( 'cachereport-ttl', + $this->mOutput->getCacheExpiry() ); + $this->mOutput->setLimitReportData( 'cachereport-transientcontent', + $this->mOutput->hasDynamicContent() ); + + if ( $this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) { + wfDebugLog( 'generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' . + $this->mTitle->getPrefixedDBkey() ); + } + return $text; + } + /** * Half-parse wikitext to half-parsed HTML. This recursive parser entry point * can be called from an extension tag hook. @@ -3511,13 +3517,7 @@ class Parser { * @return Revision|bool False if missing */ public static function statelessFetchRevision( Title $title, $parser = false ) { - $pageId = $title->getArticleID(); - $revId = $title->getLatestRevID(); - - $rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $pageId, $revId ); - if ( $rev ) { - $rev->setTitle( $title ); - } + $rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title ); return $rev; } @@ -3964,7 +3964,7 @@ class Parser { $this->mForceTocPosition = true; # Set a placeholder. At the end we'll fill it in with the TOC. - $text = $mw->replace( '', $text, 1 ); + $text = $mw->replace( '', $text, 1 ); # Only keep the first one. $text = $mw->replace( '', $text ); @@ -4227,7 +4227,7 @@ class Parser { # Decode HTML entities $safeHeadline = Sanitizer::decodeCharReferences( $safeHeadline ); - $safeHeadline = $this->normalizeSectionName( $safeHeadline ); + $safeHeadline = self::normalizeSectionName( $safeHeadline ); $fallbackHeadline = Sanitizer::escapeIdForAttribute( $safeHeadline, Sanitizer::ID_FALLBACK ); $linkAnchor = Sanitizer::escapeIdForLink( $safeHeadline ); @@ -4410,7 +4410,7 @@ class Parser { $full .= implode( '', $sections ); if ( $this->mForceTocPosition ) { - return str_replace( '', $toc, $full ); + return str_replace( '', $toc, $full ); } else { return $full; } @@ -5042,40 +5042,40 @@ class Parser { $paramName = $paramMap[$magicName]; switch ( $paramName ) { - case 'gallery-internal-alt': - $alt = $this->stripAltText( $match, false ); - break; - case 'gallery-internal-link': - $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) ); - $chars = self::EXT_LINK_URL_CLASS; - $addr = self::EXT_LINK_ADDR; - $prots = $this->mUrlProtocols; - // check to see if link matches an absolute url, if not then it must be a wiki link. - if ( preg_match( '/^-{R|(.*)}-$/', $linkValue ) ) { - // Result of LanguageConverter::markNoConversion - // invoked on an external link. - $linkValue = substr( $linkValue, 4, -2 ); - } - if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) { - $link = $linkValue; - $this->mOutput->addExternalLink( $link ); - } else { - $localLinkTitle = Title::newFromText( $linkValue ); - if ( $localLinkTitle !== null ) { - $this->mOutput->addLink( $localLinkTitle ); - $link = $localLinkTitle->getLinkURL(); + case 'gallery-internal-alt': + $alt = $this->stripAltText( $match, false ); + break; + case 'gallery-internal-link': + $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) ); + $chars = self::EXT_LINK_URL_CLASS; + $addr = self::EXT_LINK_ADDR; + $prots = $this->mUrlProtocols; + // check to see if link matches an absolute url, if not then it must be a wiki link. + if ( preg_match( '/^-{R|(.*)}-$/', $linkValue ) ) { + // Result of LanguageConverter::markNoConversion + // invoked on an external link. + $linkValue = substr( $linkValue, 4, -2 ); + } + if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) { + $link = $linkValue; + $this->mOutput->addExternalLink( $link ); + } else { + $localLinkTitle = Title::newFromText( $linkValue ); + if ( $localLinkTitle !== null ) { + $this->mOutput->addLink( $localLinkTitle ); + $link = $localLinkTitle->getLinkURL(); + } + } + break; + default: + // Must be a handler specific parameter. + if ( $handler->validateParam( $paramName, $match ) ) { + $handlerOptions[$paramName] = $match; + } else { + // Guess not, consider it as caption. + wfDebug( "$parameterMatch failed parameter validation\n" ); + $label = '|' . $parameterMatch; } - } - break; - default: - // Must be a handler specific parameter. - if ( $handler->validateParam( $paramName, $match ) ) { - $handlerOptions[$paramName] = $match; - } else { - // Guess not, consider it as caption. - wfDebug( "$parameterMatch failed parameter validation\n" ); - $label = '|' . $parameterMatch; - } } } else { @@ -5237,52 +5237,52 @@ class Parser { } else { # Validate internal parameters switch ( $paramName ) { - case 'manualthumb': - case 'alt': - case 'class': - # @todo FIXME: Possibly check validity here for - # manualthumb? downstream behavior seems odd with - # missing manual thumbs. - $validated = true; - $value = $this->stripAltText( $value, $holders ); - break; - case 'link': - $chars = self::EXT_LINK_URL_CLASS; - $addr = self::EXT_LINK_ADDR; - $prots = $this->mUrlProtocols; - if ( $value === '' ) { - $paramName = 'no-link'; - $value = true; + case 'manualthumb': + case 'alt': + case 'class': + # @todo FIXME: Possibly check validity here for + # manualthumb? downstream behavior seems odd with + # missing manual thumbs. $validated = true; - } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) { - if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) { - $paramName = 'link-url'; - $this->mOutput->addExternalLink( $value ); - if ( $this->mOptions->getExternalLinkTarget() ) { - $params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget(); - } - $validated = true; - } - } else { - $linkTitle = Title::newFromText( $value ); - if ( $linkTitle ) { - $paramName = 'link-title'; - $value = $linkTitle; - $this->mOutput->addLink( $linkTitle ); + $value = $this->stripAltText( $value, $holders ); + break; + case 'link': + $chars = self::EXT_LINK_URL_CLASS; + $addr = self::EXT_LINK_ADDR; + $prots = $this->mUrlProtocols; + if ( $value === '' ) { + $paramName = 'no-link'; + $value = true; $validated = true; + } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) { + if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) { + $paramName = 'link-url'; + $this->mOutput->addExternalLink( $value ); + if ( $this->mOptions->getExternalLinkTarget() ) { + $params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget(); + } + $validated = true; + } + } else { + $linkTitle = Title::newFromText( $value ); + if ( $linkTitle ) { + $paramName = 'link-title'; + $value = $linkTitle; + $this->mOutput->addLink( $linkTitle ); + $validated = true; + } } - } - break; - case 'frameless': - case 'framed': - case 'thumbnail': - // use first appearing option, discard others. - $validated = !$seenformat; - $seenformat = true; - break; - default: - # Most other things appear to be empty or numeric... - $validated = ( $value === false || is_numeric( trim( $value ) ) ); + break; + case 'frameless': + case 'framed': + case 'thumbnail': + // use first appearing option, discard others. + $validated = !$seenformat; + $seenformat = true; + break; + default: + # Most other things appear to be empty or numeric... + $validated = ( $value === false || is_numeric( trim( $value ) ) ); } } @@ -5776,23 +5776,42 @@ class Parser { return $this->mDefaultSort; } + private static function getSectionNameFromStrippedText( $text ) { + $text = Sanitizer::normalizeSectionNameWhitespace( $text ); + $text = Sanitizer::decodeCharReferences( $text ); + $text = self::normalizeSectionName( $text ); + return $text; + } + + private static function makeAnchor( $sectionName ) { + return '#' . Sanitizer::escapeIdForLink( $sectionName ); + } + + private static function makeLegacyAnchor( $sectionName ) { + global $wgFragmentMode; + if ( isset( $wgFragmentMode[1] ) && $wgFragmentMode[1] === 'legacy' ) { + // ForAttribute() and ForLink() are the same for legacy encoding + $id = Sanitizer::escapeIdForAttribute( $text, Sanitizer::ID_FALLBACK ); + } else { + $id = Sanitizer::escapeIdForLink( $text ); + } + + return "#$id"; + } + /** * Try to guess the section anchor name based on a wikitext fragment * presumably extracted from a heading, for example "Header" from * "== Header ==". * * @param string $text - * - * @return string + * @return string Anchor (starting with '#') */ public function guessSectionNameFromWikiText( $text ) { # Strip out wikitext links(they break the anchor) $text = $this->stripSectionName( $text ); - $text = Sanitizer::normalizeSectionNameWhitespace( $text ); - $text = Sanitizer::decodeCharReferences( $text ); - $text = $this->normalizeSectionName( $text ); - - return '#' . Sanitizer::escapeIdForLink( $text ); + $sectionName = self::getSectionNameFromStrippedText( $text ); + return self::makeAnchor( $sectionName ); } /** @@ -5802,25 +5821,23 @@ class Parser { * than UTF-8, resulting in breakage. * * @param string $text The section name - * @return string An anchor + * @return string Anchor (starting with '#') */ public function guessLegacySectionNameFromWikiText( $text ) { - global $wgFragmentMode; - # Strip out wikitext links(they break the anchor) $text = $this->stripSectionName( $text ); - $text = Sanitizer::normalizeSectionNameWhitespace( $text ); - $text = Sanitizer::decodeCharReferences( $text ); - $text = $this->normalizeSectionName( $text ); - - if ( isset( $wgFragmentMode[1] ) && $wgFragmentMode[1] === 'legacy' ) { - // ForAttribute() and ForLink() are the same for legacy encoding - $id = Sanitizer::escapeIdForAttribute( $text, Sanitizer::ID_FALLBACK ); - } else { - $id = Sanitizer::escapeIdForLink( $text ); - } + $sectionName = self::getSectionNameFromStrippedText( $text ); + return self::makeLegacyAnchor( $sectionName ); + } - return "#$id"; + /** + * Like guessSectionNameFromWikiText(), but takes already-stripped text as input. + * @param string $text Section name (plain text) + * @return string Anchor (starting with '#') + */ + public static function guessSectionNameFromStrippedText( $text ) { + $sectionName = self::getSectionNameFromStrippedText( $text ); + return self::makeAnchor( $sectionName ); } /** @@ -5829,7 +5846,7 @@ class Parser { * @param string $text * @return string */ - private function normalizeSectionName( $text ) { + private static function normalizeSectionName( $text ) { # T90902: ensure the same normalization is applied for IDs as to links $titleParser = MediaWikiServices::getInstance()->getTitleParser(); try { diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index ff9c28d2bb..153a7708f4 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -596,7 +596,7 @@ class ParserOutput extends CacheTime { # Replace unnecessary URL escape codes with the referenced character # This prevents spammers from hiding links from the filters - $url = parser::normalizeLinkUrl( $url ); + $url = Parser::normalizeLinkUrl( $url ); $registerExternalLink = true; if ( !$wgRegisterInternalExternals ) { diff --git a/includes/parser/Preprocessor_Hash.php b/includes/parser/Preprocessor_Hash.php index b45b02606d..145fbbc592 100644 --- a/includes/parser/Preprocessor_Hash.php +++ b/includes/parser/Preprocessor_Hash.php @@ -1958,18 +1958,18 @@ class PPNode_Hash_Tree implements PPNode { continue; } switch ( $child[self::NAME] ) { - case 'name': - $bits['name'] = new self( $children, $i ); - break; - case 'attr': - $bits['attr'] = new self( $children, $i ); - break; - case 'inner': - $bits['inner'] = new self( $children, $i ); - break; - case 'close': - $bits['close'] = new self( $children, $i ); - break; + case 'name': + $bits['name'] = new self( $children, $i ); + break; + case 'attr': + $bits['attr'] = new self( $children, $i ); + break; + case 'inner': + $bits['inner'] = new self( $children, $i ); + break; + case 'close': + $bits['close'] = new self( $children, $i ); + break; } } if ( !isset( $bits['name'] ) ) { @@ -2037,15 +2037,15 @@ class PPNode_Hash_Tree implements PPNode { continue; } switch ( $child[self::NAME] ) { - case 'title': - $bits['title'] = new self( $children, $i ); - break; - case 'part': - $parts[] = new self( $children, $i ); - break; - case '@lineStart': - $bits['lineStart'] = '1'; - break; + case 'title': + $bits['title'] = new self( $children, $i ); + break; + case 'part': + $parts[] = new self( $children, $i ); + break; + case '@lineStart': + $bits['lineStart'] = '1'; + break; } } if ( !isset( $bits['title'] ) ) { diff --git a/includes/registration/ExtensionProcessor.php b/includes/registration/ExtensionProcessor.php index 5dc0b400fb..fe617c54bb 100644 --- a/includes/registration/ExtensionProcessor.php +++ b/includes/registration/ExtensionProcessor.php @@ -378,9 +378,10 @@ class ExtensionProcessor implements Processor { protected function extractExtensionMessagesFiles( $dir, array $info ) { if ( isset( $info['ExtensionMessagesFiles'] ) ) { - $this->globals["wgExtensionMessagesFiles"] += array_map( function ( $file ) use ( $dir ) { - return "$dir/$file"; - }, $info['ExtensionMessagesFiles'] ); + foreach ( $info['ExtensionMessagesFiles'] as &$file ) { + $file = "$dir/$file"; + } + $this->globals["wgExtensionMessagesFiles"] += $info['ExtensionMessagesFiles']; } } diff --git a/includes/registration/ExtensionRegistry.php b/includes/registration/ExtensionRegistry.php index 740fed4eac..6308461438 100644 --- a/includes/registration/ExtensionRegistry.php +++ b/includes/registration/ExtensionRegistry.php @@ -196,6 +196,7 @@ class ExtensionRegistry { public function readFromQueue( array $queue ) { global $wgVersion; $autoloadClasses = []; + $autoloadNamespaces = []; $autoloaderPaths = []; $processor = new ExtensionProcessor(); $versionChecker = new VersionChecker( $wgVersion ); @@ -226,10 +227,15 @@ class ExtensionRegistry { $incompatible[] = "$path: unsupported manifest_version: {$version}"; } - $autoload = $this->processAutoLoader( dirname( $path ), $info ); - // Set up the autoloader now so custom processors will work - $GLOBALS['wgAutoloadClasses'] += $autoload; - $autoloadClasses += $autoload; + $dir = dirname( $path ); + if ( isset( $info['AutoloadClasses'] ) ) { + $autoload = $this->processAutoLoader( $dir, $info['AutoloadClasses'] ); + $GLOBALS['wgAutoloadClasses'] += $autoload; + $autoloadClasses += $autoload; + } + if ( isset( $info['AutoloadNamespaces'] ) ) { + $autoloadNamespaces += $this->processAutoLoader( $dir, $info['AutoloadNamespaces'] ); + } // get all requirements/dependencies for this extension $requires = $processor->getRequirements( $info ); @@ -241,7 +247,7 @@ class ExtensionRegistry { // Get extra paths for later inclusion $autoloaderPaths = array_merge( $autoloaderPaths, - $processor->getExtraAutoloaderPaths( dirname( $path ), $info ) ); + $processor->getExtraAutoloaderPaths( $dir, $info ) ); // Compatible, read and extract info $processor->extractInfo( $path, $info, $version ); } @@ -268,6 +274,7 @@ class ExtensionRegistry { $data['globals']['wgAutoloadClasses'] = []; $data['autoload'] = $autoloadClasses; $data['autoloaderPaths'] = $autoloaderPaths; + $data['autoloaderNS'] = $autoloadNamespaces; return $data; } @@ -315,6 +322,10 @@ class ExtensionRegistry { } } + if ( isset( $info['autoloaderNS'] ) ) { + AutoLoader::$psr4Namespaces += $info['autoloaderNS']; + } + foreach ( $info['defines'] as $name => $val ) { define( $name, $val ); } @@ -399,20 +410,17 @@ class ExtensionRegistry { } /** - * Register classes with the autoloader + * Fully expand autoloader paths * * @param string $dir - * @param array $info + * @param array $files * @return array */ - protected function processAutoLoader( $dir, array $info ) { - if ( isset( $info['AutoloadClasses'] ) ) { - // Make paths absolute, relative to the JSON file - return array_map( function ( $file ) use ( $dir ) { - return "$dir/$file"; - }, $info['AutoloadClasses'] ); - } else { - return []; + protected function processAutoLoader( $dir, array $files ) { + // Make paths absolute, relative to the JSON file + foreach ( $files as &$file ) { + $file = "$dir/$file"; } + return $files; } } diff --git a/includes/resourceloader/ResourceLoaderWikiModule.php b/includes/resourceloader/ResourceLoaderWikiModule.php index bebc1887dc..6eddfc0923 100644 --- a/includes/resourceloader/ResourceLoaderWikiModule.php +++ b/includes/resourceloader/ResourceLoaderWikiModule.php @@ -183,12 +183,10 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule { * @return Content|null */ protected function getContentObj( Title $title ) { - $revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title->getArticleID(), - $title->getLatestRevID() ); + $revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title ); if ( !$revision ) { return null; } - $revision->setTitle( $title ); $content = $revision->getContent( Revision::RAW ); if ( !$content ) { wfDebugLog( 'resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!' ); diff --git a/includes/search/SearchEngine.php b/includes/search/SearchEngine.php index 3c8fe608b4..4253193878 100644 --- a/includes/search/SearchEngine.php +++ b/includes/search/SearchEngine.php @@ -112,11 +112,11 @@ abstract class SearchEngine { */ public function supports( $feature ) { switch ( $feature ) { - case 'search-update': - return true; - case 'title-suffix-filter': - default: - return false; + case 'search-update': + return true; + case 'title-suffix-filter': + default: + return false; } } diff --git a/includes/search/SearchMySQL.php b/includes/search/SearchMySQL.php index 77dcfe9ce2..2810bcef7e 100644 --- a/includes/search/SearchMySQL.php +++ b/includes/search/SearchMySQL.php @@ -209,10 +209,10 @@ class SearchMySQL extends SearchDatabase { public function supports( $feature ) { switch ( $feature ) { - case 'title-suffix-filter': - return true; - default: - return parent::supports( $feature ); + case 'title-suffix-filter': + return true; + default: + return parent::supports( $feature ); } } diff --git a/includes/shell/Command.php b/includes/shell/Command.php index 998b3ed905..2f0ea42238 100644 --- a/includes/shell/Command.php +++ b/includes/shell/Command.php @@ -36,7 +36,7 @@ class Command { use LoggerAwareTrait; /** @var string */ - private $command = ''; + protected $command = ''; /** @var array */ private $limits = [ @@ -269,9 +269,10 @@ class Command { * String together all the options and build the final command * to execute * + * @param string $command Already-escaped command to run * @return array [ command, whether to use log pipe ] */ - protected function buildFinalCommand() { + protected function buildFinalCommand( $command ) { $envcmd = ''; foreach ( $this->env as $k => $v ) { if ( wfIsWindows() ) { @@ -291,7 +292,7 @@ class Command { } $useLogPipe = false; - $cmd = $envcmd . trim( $this->command ); + $cmd = $envcmd . trim( $command ); if ( is_executable( '/bin/bash' ) ) { $time = intval( $this->limits['time'] ); @@ -335,7 +336,7 @@ class Command { $profileMethod = $this->method ?: wfGetCaller(); - list( $cmd, $useLogPipe ) = $this->buildFinalCommand(); + list( $cmd, $useLogPipe ) = $this->buildFinalCommand( $this->command ); $this->logger->debug( __METHOD__ . ": $cmd" ); diff --git a/includes/shell/FirejailCommand.php b/includes/shell/FirejailCommand.php index 79f679d87b..a71b376389 100644 --- a/includes/shell/FirejailCommand.php +++ b/includes/shell/FirejailCommand.php @@ -59,10 +59,15 @@ class FirejailCommand extends Command { /** * @inheritDoc */ - protected function buildFinalCommand() { + protected function buildFinalCommand( $command ) { // If there are no restrictions, don't use firejail if ( $this->restrictions === 0 ) { - return parent::buildFinalCommand(); + $splitCommand = explode( ' ', $command, 2 ); + $this->logger->debug( + "firejail: Command {$splitCommand[0]} {params} has no restrictions", + [ 'params' => isset( $splitCommand[1] ) ? $splitCommand[1] : '' ] + ); + return parent::buildFinalCommand( $command ); } if ( $this->firejail === false ) { @@ -110,6 +115,10 @@ class FirejailCommand extends Command { } } + if ( $this->hasRestriction( Shell::NO_LOCALSETTINGS ) ) { + $cmd[] = '--blacklist=' . realpath( MW_CONFIG_FILE ); + } + if ( $this->hasRestriction( Shell::NO_ROOT ) ) { $cmd[] = '--noroot'; } @@ -122,6 +131,10 @@ class FirejailCommand extends Command { if ( $this->hasRestriction( Shell::NO_EXECVE ) ) { $seccomp[] = '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 ) { @@ -136,11 +149,10 @@ class FirejailCommand extends Command { $cmd[] = '--net=none'; } - list( $fullCommand, $useLogPipe ) = parent::buildFinalCommand(); - $builtCmd = implode( ' ', $cmd ); - return [ "$builtCmd -- $fullCommand", $useLogPipe ]; + // Prefix the firejail command in front of the wanted command + return parent::buildFinalCommand( "$builtCmd -- {$command}" ); } } diff --git a/includes/shell/Shell.php b/includes/shell/Shell.php index 084e10e793..05463dbf35 100644 --- a/includes/shell/Shell.php +++ b/includes/shell/Shell.php @@ -45,13 +45,13 @@ class Shell { * Apply a default set of restrictions for improved * security out of the box. * - * Equal to NO_ROOT | SECCOMP | PRIVATE_DEV + * Equal to NO_ROOT | SECCOMP | PRIVATE_DEV | NO_LOCALSETTINGS * * @note This value will change over time to provide increased security * by default, and is not guaranteed to be backwards-compatible. * @since 1.31 */ - const RESTRICT_DEFAULT = 7; + const RESTRICT_DEFAULT = 39; /** * Disallow any root access. Any setuid binaries @@ -92,6 +92,13 @@ class Shell { */ const NO_EXECVE = 16; + /** + * Deny access to LocalSettings.php (MW_CONFIG_FILE) + * + * @since 1.31 + */ + const NO_LOCALSETTINGS = 32; + /** * Returns a new instance of Command class * diff --git a/includes/skins/BaseTemplate.php b/includes/skins/BaseTemplate.php index 8d5ce10dd1..f0b336a31a 100644 --- a/includes/skins/BaseTemplate.php +++ b/includes/skins/BaseTemplate.php @@ -182,44 +182,44 @@ abstract class BaseTemplate extends QuickTemplate { continue; } switch ( $boxName ) { - case 'SEARCH': - // Search is a special case, skins should custom implement this - $boxes[$boxName] = [ - 'id' => 'p-search', - 'header' => $this->getMsg( 'search' )->text(), - 'generated' => false, - 'content' => true, - ]; - break; - case 'TOOLBOX': - $msgObj = $this->getMsg( 'toolbox' ); - $boxes[$boxName] = [ - 'id' => 'p-tb', - 'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox', - 'generated' => false, - 'content' => $this->getToolbox(), - ]; - break; - case 'LANGUAGES': - if ( $this->data['language_urls'] !== false ) { - $msgObj = $this->getMsg( 'otherlanguages' ); + case 'SEARCH': + // Search is a special case, skins should custom implement this $boxes[$boxName] = [ - 'id' => 'p-lang', - 'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages', + 'id' => 'p-search', + 'header' => $this->getMsg( 'search' )->text(), 'generated' => false, - 'content' => $this->data['language_urls'] ?: [], + 'content' => true, ]; - } - break; - default: - $msgObj = $this->getMsg( $boxName ); - $boxes[$boxName] = [ - 'id' => "p-$boxName", - 'header' => $msgObj->exists() ? $msgObj->text() : $boxName, - 'generated' => true, - 'content' => $content, - ]; - break; + break; + case 'TOOLBOX': + $msgObj = $this->getMsg( 'toolbox' ); + $boxes[$boxName] = [ + 'id' => 'p-tb', + 'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox', + 'generated' => false, + 'content' => $this->getToolbox(), + ]; + break; + case 'LANGUAGES': + if ( $this->data['language_urls'] !== false ) { + $msgObj = $this->getMsg( 'otherlanguages' ); + $boxes[$boxName] = [ + 'id' => 'p-lang', + 'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages', + 'generated' => false, + 'content' => $this->data['language_urls'] ?: [], + ]; + } + break; + default: + $msgObj = $this->getMsg( $boxName ); + $boxes[$boxName] = [ + 'id' => "p-$boxName", + 'header' => $msgObj->exists() ? $msgObj->text() : $boxName, + 'generated' => true, + 'content' => $content, + ]; + break; } } diff --git a/includes/skins/SkinTemplate.php b/includes/skins/SkinTemplate.php index 532ee518a5..badd7a2ead 100644 --- a/includes/skins/SkinTemplate.php +++ b/includes/skins/SkinTemplate.php @@ -524,15 +524,48 @@ class SkinTemplate extends Skin { * @return string */ public function getPersonalToolsList() { + return $this->makePersonalToolsList(); + } + + /** + * Get the HTML for the personal tools list + * + * @since 1.31 + * + * @param array $personalTools + * @param array $options + * @return string + */ + public function makePersonalToolsList( $personalTools = null, $options = [] ) { $tpl = $this->setupTemplateForOutput(); $tpl->set( 'personal_urls', $this->buildPersonalUrls() ); $html = ''; - foreach ( $tpl->getPersonalTools() as $key => $item ) { - $html .= $tpl->makeListItem( $key, $item ); + + if ( $personalTools === null ) { + $personalTools = $tpl->getPersonalTools(); + } + + foreach ( $personalTools as $key => $item ) { + $html .= $tpl->makeListItem( $key, $item, $options ); } + return $html; } + /** + * Get personal tools for the user + * + * @since 1.31 + * + * @return array Array of personal tools + */ + public function getStructuredPersonalTools() { + $tpl = $this->setupTemplateForOutput(); + $tpl->set( 'personal_urls', $this->buildPersonalUrls() ); + + return $tpl->getPersonalTools(); + } + /** * Format language name for use in sidebar interlanguage links list. * By default it is capitalized. diff --git a/includes/specialpage/ChangesListSpecialPage.php b/includes/specialpage/ChangesListSpecialPage.php index b6d1028778..303184de17 100644 --- a/includes/specialpage/ChangesListSpecialPage.php +++ b/includes/specialpage/ChangesListSpecialPage.php @@ -553,7 +553,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { public function execute( $subpage ) { $this->rcSubpage = $subpage; - $this->considerActionsForDefaultSavedQuery(); + $this->considerActionsForDefaultSavedQuery( $subpage ); $opts = $this->getOptions(); try { @@ -570,8 +570,15 @@ abstract class ChangesListSpecialPage extends SpecialPage { // Used by "live update" and "view newest" to check // if there's new changes with minimal data transfer if ( $this->getRequest()->getBool( 'peek' ) ) { - $code = $rows->numRows() > 0 ? 200 : 204; + $code = $rows->numRows() > 0 ? 200 : 204; $this->getOutput()->setStatusCode( $code ); + + if ( $this->getUser()->isAnon() !== + $this->getRequest()->getFuzzyBool( 'isAnon' ) + ) { + $this->getOutput()->setStatusCode( 205 ); + } + return; } @@ -622,9 +629,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { * Check whether or not the page should load defaults, and if so, whether * a default saved query is relevant to be redirected to. If it is relevant, * redirect properly with all necessary query parameters. + * + * @param string $subpage */ - protected function considerActionsForDefaultSavedQuery() { - if ( !$this->isStructuredFilterUiEnabled() ) { + protected function considerActionsForDefaultSavedQuery( $subpage ) { + if ( !$this->isStructuredFilterUiEnabled() || $this->including() ) { return; } @@ -670,7 +679,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { // but are still valid and requested in the URL $query = array_merge( $this->getRequest()->getValues(), $query ); unset( $query[ 'title' ] ); - $this->getOutput()->redirect( $this->getPageTitle()->getCanonicalURL( $query ) ); + $this->getOutput()->redirect( $this->getPageTitle( $subpage )->getCanonicalURL( $query ) ); } else { // There's a default, but the version is not 2, and the server can't // actually recognize the query itself. This happens if it is before @@ -697,7 +706,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { */ protected function includeRcFiltersApp() { $out = $this->getOutput(); - if ( $this->isStructuredFilterUiEnabled() ) { + if ( $this->isStructuredFilterUiEnabled() && !$this->including() ) { $jsData = $this->getStructuredFilterJsData(); $messages = []; @@ -1642,7 +1651,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { ] ); $out->addModules( 'mediawiki.special.changeslist.legend.js' ); - if ( $this->isStructuredFilterUiEnabled() ) { + if ( $this->isStructuredFilterUiEnabled() && !$this->including() ) { $out->addModules( 'mediawiki.rcfilters.filters.ui' ); $out->addModuleStyles( 'mediawiki.rcfilters.filters.base.styles' ); } diff --git a/includes/specials/SpecialEmailuser.php b/includes/specials/SpecialEmailuser.php index 30eb38d04a..06ca04f5cd 100644 --- a/includes/specials/SpecialEmailuser.php +++ b/includes/specials/SpecialEmailuser.php @@ -224,15 +224,52 @@ class SpecialEmailUser extends UnlistedSpecialPage { wfDebug( "Target is invalid user.\n" ); return 'notarget'; - } elseif ( !$target->isEmailConfirmed() ) { + } + + if ( !$target->isEmailConfirmed() ) { wfDebug( "User has no valid email.\n" ); return 'noemail'; - } elseif ( !$target->canReceiveEmail() ) { + } + + if ( !$target->canReceiveEmail() ) { wfDebug( "User does not allow user emails.\n" ); return 'nowikiemail'; - } elseif ( $sender !== null ) { + } + + if ( $target->getEditCount() === 0 && + ( $sender === null || !$sender->isAllowed( 'sendemail-new-users' ) ) + ) { + // Determine if target has any other logged actions. + $dbr = wfGetDB( DB_REPLICA ); + $log_id = $dbr->selectField( + 'logging', + 'log_id', + [ + 'log_user' => $target->getId(), + "NOT (log_type = 'newusers' AND log_action = 'autocreate')", + ], + __METHOD__, + [ 'LIMIT' => 1 ] + ); + + if ( !$log_id ) { + wfDebug( "User has no logged actions on this wiki.\n" ); + + return 'nowikiemail'; + } + } + + if ( $sender !== null && !$target->getOption( 'email-allow-new-users' ) && + $sender->isNewbie() + ) { + wfDebug( "User does not allow user emails from new users.\n" ); + + return 'nowikiemail'; + } + + if ( $sender !== null ) { $blacklist = $target->getOption( 'email-blacklist', [] ); if ( $blacklist ) { $lookup = CentralIdLookup::factory(); diff --git a/includes/specials/SpecialListgrouprights.php b/includes/specials/SpecialListgrouprights.php index 2ca4190a76..cc62d6146a 100644 --- a/includes/specials/SpecialListgrouprights.php +++ b/includes/specials/SpecialListgrouprights.php @@ -137,7 +137,7 @@ class SpecialListGroupRights extends SpecialPage { } private function outputNamespaceProtectionInfo() { - global $wgParser, $wgContLang; + global $wgContLang; $out = $this->getOutput(); $namespaceProtection = $this->getConfig()->get( 'NamespaceProtection' ); @@ -145,11 +145,11 @@ class SpecialListGroupRights extends SpecialPage { return; } - $header = $this->msg( 'listgrouprights-namespaceprotection-header' )->parse(); + $header = $this->msg( 'listgrouprights-namespaceprotection-header' )->text(); $out->addHTML( Html::rawElement( 'h2', [], Html::element( 'span', [ 'class' => 'mw-headline', - 'id' => $wgParser->guessSectionNameFromWikiText( $header ) + 'id' => substr( Parser::guessSectionNameFromStrippedText( $header ), 1 ) ], $header ) ) . Xml::openElement( 'table', [ 'class' => 'wikitable' ] ) . Html::element( diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php index 671ab6fb55..1639386e3b 100644 --- a/includes/specials/SpecialNewpages.php +++ b/includes/specials/SpecialNewpages.php @@ -290,15 +290,16 @@ class SpecialNewpages extends IncludableSpecialPage { /** * @param stdClass $result Result row from recent changes - * @return Revision|bool + * @param Title $title + * @return bool|Revision */ - protected function revisionFromRcResult( stdClass $result ) { + protected function revisionFromRcResult( stdClass $result, Title $title ) { return new Revision( [ 'comment' => CommentStore::newKey( 'rc_comment' )->getComment( $result )->text, 'deleted' => $result->rc_deleted, 'user_text' => $result->rc_user_text, 'user' => $result->rc_user, - ] ); + ], 0, $title ); } /** @@ -313,8 +314,7 @@ class SpecialNewpages extends IncludableSpecialPage { // Revision deletion works on revisions, // so cast our recent change row to a revision row. - $rev = $this->revisionFromRcResult( $result ); - $rev->setTitle( $title ); + $rev = $this->revisionFromRcResult( $result, $title ); $classes = []; $attribs = [ 'data-mw-revid' => $result->rev_id ]; diff --git a/includes/specials/SpecialProtectedpages.php b/includes/specials/SpecialProtectedpages.php index 8e20d88372..987bcdda79 100644 --- a/includes/specials/SpecialProtectedpages.php +++ b/includes/specials/SpecialProtectedpages.php @@ -42,7 +42,7 @@ class SpecialProtectedpages extends SpecialPage { $request = $this->getRequest(); $type = $request->getVal( $this->IdType ); $level = $request->getVal( $this->IdLevel ); - $sizetype = $request->getVal( 'sizetype' ); + $sizetype = $request->getVal( 'size-mode' ); $size = $request->getIntOrNull( 'size' ); $ns = $request->getIntOrNull( 'namespace' ); $indefOnly = $request->getBool( 'indefonly' ) ? 1 : 0; @@ -95,24 +95,24 @@ class SpecialProtectedpages extends SpecialPage { protected function showOptions( $namespace, $type = 'edit', $level, $sizetype, $size, $indefOnly, $cascadeOnly, $noRedirect ) { - $title = $this->getPageTitle(); + $formDescriptor = [ + 'namespace' => $this->getNamespaceMenu( $namespace ), + 'typemenu' => $this->getTypeMenu( $type ), + 'levelmenu' => $this->getLevelMenu( $level ), - return Xml::openElement( 'form', [ 'method' => 'get', 'action' => wfScript() ] ) . - Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', [], $this->msg( 'protectedpages' )->text() ) . - Html::hidden( 'title', $title->getPrefixedDBkey() ) . "\n" . - $this->getNamespaceMenu( $namespace ) . "\n" . - $this->getTypeMenu( $type ) . "\n" . - $this->getLevelMenu( $level ) . "\n" . - "
\n" . - $this->getExpiryCheck( $indefOnly ) . "\n" . - $this->getCascadeCheck( $cascadeOnly ) . "\n" . - $this->getRedirectCheck( $noRedirect ) . "\n" . - "
\n" . - $this->getSizeLimit( $sizetype, $size ) . "\n" . - Xml::submitButton( $this->msg( 'protectedpages-submit' )->text() ) . "\n" . - Xml::closeElement( 'fieldset' ) . - Xml::closeElement( 'form' ); + 'expirycheck' => $this->getExpiryCheck( $indefOnly ), + 'cascadecheck' => $this->getCascadeCheck( $cascadeOnly ), + 'redirectcheck' => $this->getRedirectCheck( $noRedirect ), + + 'sizelimit' => $this->getSizeLimit( $sizetype, $size ), + ]; + $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() ); + $htmlForm + ->setMethod( 'get' ) + ->setWrapperLegendMsg( 'protectedpages' ) + ->setSubmitText( $this->msg( 'protectedpages-submit' )->text() ); + + return $htmlForm->prepareForm()->getHTML( false ); } /** @@ -120,96 +120,80 @@ class SpecialProtectedpages extends SpecialPage { * selector, sans the MediaWiki namespace * * @param string|null $namespace Pre-select namespace - * @return string + * @return array */ protected function getNamespaceMenu( $namespace = null ) { - return Html::rawElement( 'span', [ 'class' => 'mw-input-with-label' ], - Html::namespaceSelector( - [ - 'selected' => $namespace, - 'all' => '', - 'label' => $this->msg( 'namespace' )->text() - ], [ - 'name' => 'namespace', - 'id' => 'namespace', - 'class' => 'namespaceselector', - ] - ) - ); + return [ + 'class' => 'HTMLSelectNamespace', + 'name' => 'namespace', + 'id' => 'namespace', + 'cssclass' => 'namespaceselector', + 'selected' => $namespace, + 'all' => '', + 'label' => $this->msg( 'namespace' )->text(), + ]; } /** * @param bool $indefOnly - * @return string Formatted HTML + * @return array */ protected function getExpiryCheck( $indefOnly ) { - return '' . Xml::checkLabel( - $this->msg( 'protectedpages-indef' )->text(), - 'indefonly', - 'indefonly', - $indefOnly - ) . "\n"; + return [ + 'type' => 'check', + 'label' => $this->msg( 'protectedpages-indef' )->text(), + 'name' => 'indefonly', + 'id' => 'indefonly', + 'value' => $indefOnly + ]; } /** * @param bool $cascadeOnly - * @return string Formatted HTML + * @return array */ protected function getCascadeCheck( $cascadeOnly ) { - return '' . Xml::checkLabel( - $this->msg( 'protectedpages-cascade' )->text(), - 'cascadeonly', - 'cascadeonly', - $cascadeOnly - ) . "\n"; + return [ + 'type' => 'check', + 'label' => $this->msg( 'protectedpages-cascade' )->text(), + 'name' => 'cascadeonly', + 'id' => 'cascadeonly', + 'value' => $cascadeOnly + ]; } /** * @param bool $noRedirect - * @return string Formatted HTML + * @return array */ protected function getRedirectCheck( $noRedirect ) { - return '' . Xml::checkLabel( - $this->msg( 'protectedpages-noredirect' )->text(), - 'noredirect', - 'noredirect', - $noRedirect - ) . "\n"; + return [ + 'type' => 'check', + 'label' => $this->msg( 'protectedpages-noredirect' )->text(), + 'name' => 'noredirect', + 'id' => 'noredirect', + 'value' => $noRedirect, + ]; } /** * @param string $sizetype "min" or "max" * @param mixed $size - * @return string Formatted HTML + * @return array */ protected function getSizeLimit( $sizetype, $size ) { $max = $sizetype === 'max'; - return '' . Xml::radioLabel( - $this->msg( 'minimum-size' )->text(), - 'sizetype', - 'min', - 'wpmin', - !$max - ) . - ' ' . - Xml::radioLabel( - $this->msg( 'maximum-size' )->text(), - 'sizetype', - 'max', - 'wpmax', - $max - ) . - ' ' . - Xml::input( 'size', 9, $size, [ 'id' => 'wpsize' ] ) . - ' ' . - Xml::label( $this->msg( 'pagesize' )->text(), 'wpsize' ) . "\n"; + return [ + 'class' => 'HTMLSizeFilterField', + 'name' => 'size', + ]; } /** * Creates the input label of the restriction type * @param string $pr_type Protection type - * @return string Formatted HTML + * @return array */ protected function getTypeMenu( $pr_type ) { $m = []; // Temporary array @@ -224,21 +208,23 @@ class SpecialProtectedpages extends SpecialPage { // Third pass generates sorted XHTML content foreach ( $m as $text => $type ) { - $selected = ( $type == $pr_type ); - $options[] = Xml::option( $text, $type, $selected ) . "\n"; + $options[$text] = $type; } - return '' . - Xml::label( $this->msg( 'restriction-type' )->text(), $this->IdType ) . ' ' . - Xml::tags( 'select', - [ 'id' => $this->IdType, 'name' => $this->IdType ], - implode( "\n", $options ) ) . ""; + return [ + 'type' => 'select', + 'options' => $options, + 'value' => $pr_type, + 'label' => $this->msg( 'restriction-type' )->text(), + 'name' => $this->IdType, + 'id' => $this->IdType, + ]; } /** * Creates the input label of the restriction level * @param string $pr_level Protection level - * @return string Formatted HTML + * @return array */ protected function getLevelMenu( $pr_level ) { // Temporary array @@ -256,15 +242,17 @@ class SpecialProtectedpages extends SpecialPage { // Third pass generates sorted XHTML content foreach ( $m as $text => $type ) { - $selected = ( $type == $pr_level ); - $options[] = Xml::option( $text, $type, $selected ); + $options[$text] = $type; } - return '' . - Xml::label( $this->msg( 'restriction-level' )->text(), $this->IdLevel ) . ' ' . - Xml::tags( 'select', - [ 'id' => $this->IdLevel, 'name' => $this->IdLevel ], - implode( "\n", $options ) ) . ""; + return [ + 'type' => 'select', + 'options' => $options, + 'value' => $pr_level, + 'label' => $this->msg( 'restriction-level' )->text(), + 'name' => $this->IdLevel, + 'id' => $this->IdLevel + ]; } protected function getGroupName() { diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php index 9e0daf55e5..d4aef6c0d1 100644 --- a/includes/specials/SpecialRecentchangeslinked.php +++ b/includes/specials/SpecialRecentchangeslinked.php @@ -30,8 +30,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges { /** @var bool|Title */ protected $rclTargetTitle; - protected $rclTarget; - function __construct() { parent::__construct( 'Recentchangeslinked' ); } @@ -46,7 +44,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges { public function parseParameters( $par, FormOptions $opts ) { $opts['target'] = $par; - $this->rclTarget = $par; } /** @@ -297,20 +294,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges { return $this->prefixSearchString( $search, $limit, $offset ); } - /** - * Get a self-referential title object - * with consideration to the given subpage. - * - * @param string|bool $subpage - * @return Title - * @since 1.23 - */ - public function getPageTitle( $subpage = false ) { - $subpage = $subpage ? $subpage : $this->rclTarget; - - return parent::getPageTitle( $subpage ); - } - protected function outputNoResults() { if ( $this->getTargetTitle() === false ) { $this->getOutput()->addHTML( diff --git a/includes/specials/SpecialUncategorizedcategories.php b/includes/specials/SpecialUncategorizedcategories.php index 5ff9e04ef7..2dcb77f824 100644 --- a/includes/specials/SpecialUncategorizedcategories.php +++ b/includes/specials/SpecialUncategorizedcategories.php @@ -60,7 +60,7 @@ class UncategorizedCategoriesPage extends UncategorizedPagesPage { $title = Title::makeTitleSafe( NS_CATEGORY, $titleStr ); } if ( $title ) { - $this->exceptionList[] = $title->getDBKey(); + $this->exceptionList[] = $title->getDBkey(); } } } diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index 0b0bbc007b..f821effdf0 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -465,11 +465,11 @@ class SpecialUndelete extends SpecialPage { if ( $isText ) { // source view for textual content - $sourceView = new OOUI\MultilineTextInputWidget( [ - 'readOnly' => true, - 'rows' => 25, - 'value' => $content->getNativeData() . "\n" - ] ); + $sourceView = Xml::element( 'textarea', [ + 'readonly' => 'readonly', + 'cols' => 80, + 'rows' => 25 + ], $content->getNativeData() . "\n" ); $buttonFields[] = new OOUI\ButtonInputWidget( [ 'type' => 'submit', diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php index a5f9ab3ec1..fd066ac7f3 100644 --- a/includes/specials/SpecialUserrights.php +++ b/includes/specials/SpecialUserrights.php @@ -761,7 +761,7 @@ class UserrightsPage extends SpecialPage { /** * Adds a table with checkboxes where you can select what groups to add/remove * - * @param array $usergroups Associative array of (group name as string => + * @param UserGroupMembership[] $usergroups Associative array of (group name as string => * UserGroupMembership object) for groups the user belongs to * @param User $user * @return Array with 2 elements: the XHTML table element with checkxboes, and diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index e8e828df08..2ad70a67a8 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -117,11 +117,6 @@ class SpecialWatchlist extends ChangesListSpecialPage { ); } - public function isStructuredFilterUiEnabledByDefault() { - return $this->getConfig()->get( 'StructuredChangeFiltersOnWatchlist' ) && - $this->getUser()->getDefaultOption( 'rcenhancedfilters' ); - } - /** * Return an array of subpages that this special page will accept. * diff --git a/includes/specials/pagers/UsersPager.php b/includes/specials/pagers/UsersPager.php index a68fe66881..45d9a7fc84 100644 --- a/includes/specials/pagers/UsersPager.php +++ b/includes/specials/pagers/UsersPager.php @@ -33,7 +33,7 @@ class UsersPager extends AlphabeticPager { /** - * @var array A array with user ids as key and a array of groups as value + * @var array[] A array with user ids as key and a array of groups as value */ protected $userGroupCache; @@ -391,8 +391,8 @@ class UsersPager extends AlphabeticPager { * and the relevant UserGroupMembership objects * * @param int $uid User id - * @param array|null $cache - * @return array (group name => UserGroupMembership object) + * @param array[]|null $cache + * @return UserGroupMembership[] (group name => UserGroupMembership object) */ protected static function getGroupMemberships( $uid, $cache = null ) { if ( $cache === null ) { diff --git a/includes/templates/AtomHeader.mustache b/includes/templates/AtomHeader.mustache new file mode 100644 index 0000000000..60ab75e3d7 --- /dev/null +++ b/includes/templates/AtomHeader.mustache @@ -0,0 +1,8 @@ + + {{{feedID}}} + {{{title}}} + + + {{{timestamp}}}Z + {{{description}}} + MediaWiki {{{version}}} diff --git a/includes/templates/AtomItem.mustache b/includes/templates/AtomItem.mustache new file mode 100644 index 0000000000..32d2f01d66 --- /dev/null +++ b/includes/templates/AtomItem.mustache @@ -0,0 +1,10 @@ + + {{{uniqueID}}} + {{{title}}} + + {{#date}}{{{.}}}Z{{/date}} + + {{{description}}} + {{#author}}{{{.}}}{{/author}} + {{! FIXME: Need to add comments }} + diff --git a/includes/templates/RSSHeader.mustache b/includes/templates/RSSHeader.mustache new file mode 100644 index 0000000000..385369dfc2 --- /dev/null +++ b/includes/templates/RSSHeader.mustache @@ -0,0 +1,8 @@ + + + {{{title}}} + {{{url}}} + {{{description}}} + {{{language}}} + MediaWiki {{{version}}} + {{{timestamp}}} diff --git a/includes/templates/RSSItem.mustache b/includes/templates/RSSItem.mustache new file mode 100644 index 0000000000..d00c100600 --- /dev/null +++ b/includes/templates/RSSItem.mustache @@ -0,0 +1,9 @@ + + {{{title}}} + {{{url}}} + {{{uniqueID}}} + {{{description}}} + {{#date}}{{{.}}}{{/date}} + {{#author}}{{{.}}}{{/author}} + {{#comments}}{{{.}}}{{/comments}} + diff --git a/includes/tidy/Balancer.php b/includes/tidy/Balancer.php index 82c35bb881..e570633778 100644 --- a/includes/tidy/Balancer.php +++ b/includes/tidy/Balancer.php @@ -2052,73 +2052,73 @@ class Balancer { return true; } elseif ( $token === 'tag' ) { switch ( $value ) { - case 'font': - if ( isset( $attribs['color'] ) - || isset( $attribs['face'] ) - || isset( $attribs['size'] ) - ) { - break; - } - // otherwise, fall through - case 'b': - case 'big': - case 'blockquote': - case 'body': - case 'br': - case 'center': - case 'code': - case 'dd': - case 'div': - case 'dl': - case 'dt': - case 'em': - case 'embed': - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - case 'head': - case 'hr': - case 'i': - case 'img': - case 'li': - case 'listing': - case 'menu': - case 'meta': - case 'nobr': - case 'ol': - case 'p': - case 'pre': - case 'ruby': - case 's': - case 'small': - case 'span': - case 'strong': - case 'strike': - case 'sub': - case 'sup': - case 'table': - case 'tt': - case 'u': - case 'ul': - case 'var': - if ( $this->fragmentContext ) { - break; - } - while ( true ) { - $this->stack->pop(); - $node = $this->stack->currentNode; - if ( - $node->isMathmlTextIntegrationPoint() || - $node->isHtmlIntegrationPoint() || - $node->isHtml() + case 'font': + if ( isset( $attribs['color'] ) + || isset( $attribs['face'] ) + || isset( $attribs['size'] ) ) { break; } - } - return $this->insertToken( $token, $value, $attribs, $selfClose ); + // otherwise, fall through + case 'b': + case 'big': + case 'blockquote': + case 'body': + case 'br': + case 'center': + case 'code': + case 'dd': + case 'div': + case 'dl': + case 'dt': + case 'em': + case 'embed': + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + case 'head': + case 'hr': + case 'i': + case 'img': + case 'li': + case 'listing': + case 'menu': + case 'meta': + case 'nobr': + case 'ol': + case 'p': + case 'pre': + case 'ruby': + case 's': + case 'small': + case 'span': + case 'strong': + case 'strike': + case 'sub': + case 'sup': + case 'table': + case 'tt': + case 'u': + case 'ul': + case 'var': + if ( $this->fragmentContext ) { + break; + } + while ( true ) { + $this->stack->pop(); + $node = $this->stack->currentNode; + if ( + $node->isMathmlTextIntegrationPoint() || + $node->isHtmlIntegrationPoint() || + $node->isHtml() + ) { + break; + } + } + return $this->insertToken( $token, $value, $attribs, $selfClose ); } // "Any other start tag" $adjusted = ( $this->fragmentContext && $this->stack->length() === 1 ) ? @@ -2270,56 +2270,56 @@ class Balancer { } if ( $node->isHtml() ) { switch ( $node->localName ) { - case 'select': - $stackLength = $this->stack->length(); - for ( $j = $i + 1; $j < $stackLength - 1; $j++ ) { - $ancestor = $this->stack->node( $stackLength - $j - 1 ); - if ( $ancestor->isHtmlNamed( 'template' ) ) { - break; + case 'select': + $stackLength = $this->stack->length(); + for ( $j = $i + 1; $j < $stackLength - 1; $j++ ) { + $ancestor = $this->stack->node( $stackLength - $j - 1 ); + if ( $ancestor->isHtmlNamed( 'template' ) ) { + break; + } + if ( $ancestor->isHtmlNamed( 'table' ) ) { + $this->switchMode( 'inSelectInTableMode' ); + return; + } } - if ( $ancestor->isHtmlNamed( 'table' ) ) { - $this->switchMode( 'inSelectInTableMode' ); - return; - } - } - $this->switchMode( 'inSelectMode' ); - return; - case 'tr': - $this->switchMode( 'inRowMode' ); - return; - case 'tbody': - case 'tfoot': - case 'thead': - $this->switchMode( 'inTableBodyMode' ); - return; - case 'caption': - $this->switchMode( 'inCaptionMode' ); - return; - case 'colgroup': - $this->switchMode( 'inColumnGroupMode' ); - return; - case 'table': - $this->switchMode( 'inTableMode' ); - return; - case 'template': - $this->switchMode( - array_slice( $this->templateInsertionModes, -1 )[0] - ); - return; - case 'body': - $this->switchMode( 'inBodyMode' ); - return; - // OMITTED: - // OMITTED: - // OMITTED: - default: - if ( !$last ) { - // OMITTED: - if ( $node->isA( BalanceSets::$tableCellSet ) ) { - $this->switchMode( 'inCellMode' ); - return; + $this->switchMode( 'inSelectMode' ); + return; + case 'tr': + $this->switchMode( 'inRowMode' ); + return; + case 'tbody': + case 'tfoot': + case 'thead': + $this->switchMode( 'inTableBodyMode' ); + return; + case 'caption': + $this->switchMode( 'inCaptionMode' ); + return; + case 'colgroup': + $this->switchMode( 'inColumnGroupMode' ); + return; + case 'table': + $this->switchMode( 'inTableMode' ); + return; + case 'template': + $this->switchMode( + array_slice( $this->templateInsertionModes, -1 )[0] + ); + return; + case 'body': + $this->switchMode( 'inBodyMode' ); + return; + // OMITTED: + // OMITTED: + // OMITTED: + default: + if ( !$last ) { + // OMITTED: + if ( $node->isA( BalanceSets::$tableCellSet ) ) { + $this->switchMode( 'inCellMode' ); + return; + } } - } } } if ( $last ) { @@ -2378,52 +2378,52 @@ class Balancer { // Fall through to handle non-whitespace below. } elseif ( $token === 'tag' ) { switch ( $value ) { - case 'meta': - // OMITTED: in a full HTML parser, this might change the encoding. - // falls through - // OMITTED: - case 'base': - case 'basefont': - case 'bgsound': - case 'link': - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - return true; - // OMITTED: - // OMITTED: <noscript> - case 'noframes': - case 'style': - return $this->parseRawText( $value, $attribs ); - // OMITTED: <script> - case 'template': - $this->stack->insertHTMLElement( $value, $attribs ); - $this->afe->insertMarker(); - // OMITTED: frameset_ok - $this->switchMode( 'inTemplateMode' ); - $this->templateInsertionModes[] = $this->parseMode; - return true; - // OMITTED: <head> + case 'meta': + // OMITTED: in a full HTML parser, this might change the encoding. + // falls through + // OMITTED: <html> + case 'base': + case 'basefont': + case 'bgsound': + case 'link': + $this->stack->insertHTMLElement( $value, $attribs ); + $this->stack->pop(); + return true; + // OMITTED: <title> + // OMITTED: <noscript> + case 'noframes': + case 'style': + return $this->parseRawText( $value, $attribs ); + // OMITTED: <script> + case 'template': + $this->stack->insertHTMLElement( $value, $attribs ); + $this->afe->insertMarker(); + // OMITTED: frameset_ok + $this->switchMode( 'inTemplateMode' ); + $this->templateInsertionModes[] = $this->parseMode; + return true; + // OMITTED: <head> } } elseif ( $token === 'endtag' ) { switch ( $value ) { - // OMITTED: <head> - // OMITTED: <body> - // OMITTED: <html> - case 'br': - break; // handle at the bottom of the function - case 'template': - if ( $this->stack->indexOf( $value ) < 0 ) { - return true; // Ignore the token. - } - $this->stack->generateImpliedEndTags( null, true /* thorough */ ); - $this->stack->popTag( $value ); - $this->afe->clearToMarker(); - array_pop( $this->templateInsertionModes ); - $this->resetInsertionMode(); - return true; - default: - // ignore any other end tag - return true; + // OMITTED: <head> + // OMITTED: <body> + // OMITTED: <html> + case 'br': + break; // handle at the bottom of the function + case 'template': + if ( $this->stack->indexOf( $value ) < 0 ) { + return true; // Ignore the token. + } + $this->stack->generateImpliedEndTags( null, true /* thorough */ ); + $this->stack->popTag( $value ); + $this->afe->clearToMarker(); + array_pop( $this->templateInsertionModes ); + $this->resetInsertionMode(); + return true; + default: + // ignore any other end tag + return true; } } elseif ( $token === 'comment' ) { $this->stack->insertComment( $value ); @@ -2449,505 +2449,505 @@ class Balancer { return true; } elseif ( $token === 'tag' ) { switch ( $value ) { - // OMITTED: <html> - case 'base': - case 'basefont': - case 'bgsound': - case 'link': - case 'meta': - case 'noframes': - // OMITTED: <script> - case 'style': - case 'template': - // OMITTED: <title> - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); - // OMITTED: <body> - // OMITTED: <frameset> - - case 'address': - case 'article': - case 'aside': - case 'blockquote': - case 'center': - case 'details': - case 'dialog': - case 'dir': - case 'div': - case 'dl': - case 'fieldset': - case 'figcaption': - case 'figure': - case 'footer': - case 'header': - case 'hgroup': - case 'main': - case 'nav': - case 'ol': - case 'p': - case 'section': - case 'summary': - case 'ul': - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; - - case 'menu': - if ( $this->stack->inButtonScope( "p" ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { - $this->stack->pop(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + // OMITTED: <html> + case 'base': + case 'basefont': + case 'bgsound': + case 'link': + case 'meta': + case 'noframes': + // OMITTED: <script> + case 'style': + case 'template': + // OMITTED: <title> + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + // OMITTED: <body> + // OMITTED: <frameset> - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - if ( $this->stack->currentNode->isA( BalanceSets::$headingSet ) ) { - $this->stack->pop(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + case 'address': + case 'article': + case 'aside': + case 'blockquote': + case 'center': + case 'details': + case 'dialog': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'figcaption': + case 'figure': + case 'footer': + case 'header': + case 'hgroup': + case 'main': + case 'nav': + case 'ol': + case 'p': + case 'section': + case 'summary': + case 'ul': + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'pre': - case 'listing': - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - $this->ignoreLinefeed = true; - // OMITTED: frameset_ok - return true; + case 'menu': + if ( $this->stack->inButtonScope( "p" ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { + $this->stack->pop(); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'form': - if ( - $this->formElementPointer && - $this->stack->indexOf( 'template' ) < 0 - ) { - return true; // in a form, not in a template. - } - if ( $this->stack->inButtonScope( "p" ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $elt = $this->stack->insertHTMLElement( $value, $attribs ); - if ( $this->stack->indexOf( 'template' ) < 0 ) { - $this->formElementPointer = $elt; - } - return true; + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + if ( $this->stack->currentNode->isA( BalanceSets::$headingSet ) ) { + $this->stack->pop(); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'li': - // OMITTED: frameset_ok - foreach ( $this->stack as $node ) { - if ( $node->isHtmlNamed( 'li' ) ) { - $this->inBodyMode( 'endtag', 'li' ); - break; + case 'pre': + case 'listing': + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); } + $this->stack->insertHTMLElement( $value, $attribs ); + $this->ignoreLinefeed = true; + // OMITTED: frameset_ok + return true; + + case 'form': if ( - $node->isA( BalanceSets::$specialSet ) && - !$node->isA( BalanceSets::$addressDivPSet ) + $this->formElementPointer && + $this->stack->indexOf( 'template' ) < 0 ) { - break; + return true; // in a form, not in a template. } - } - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; - - case 'dd': - case 'dt': - // OMITTED: frameset_ok - foreach ( $this->stack as $node ) { - if ( $node->isHtmlNamed( 'dd' ) ) { - $this->inBodyMode( 'endtag', 'dd' ); - break; + if ( $this->stack->inButtonScope( "p" ) ) { + $this->inBodyMode( 'endtag', 'p' ); } - if ( $node->isHtmlNamed( 'dt' ) ) { - $this->inBodyMode( 'endtag', 'dt' ); - break; + $elt = $this->stack->insertHTMLElement( $value, $attribs ); + if ( $this->stack->indexOf( 'template' ) < 0 ) { + $this->formElementPointer = $elt; } - if ( - $node->isA( BalanceSets::$specialSet ) && - !$node->isA( BalanceSets::$addressDivPSet ) - ) { - break; + return true; + + case 'li': + // OMITTED: frameset_ok + foreach ( $this->stack as $node ) { + if ( $node->isHtmlNamed( 'li' ) ) { + $this->inBodyMode( 'endtag', 'li' ); + break; + } + if ( + $node->isA( BalanceSets::$specialSet ) && + !$node->isA( BalanceSets::$addressDivPSet ) + ) { + break; + } } - } - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - // OMITTED: <plaintext> + case 'dd': + case 'dt': + // OMITTED: frameset_ok + foreach ( $this->stack as $node ) { + if ( $node->isHtmlNamed( 'dd' ) ) { + $this->inBodyMode( 'endtag', 'dd' ); + break; + } + if ( $node->isHtmlNamed( 'dt' ) ) { + $this->inBodyMode( 'endtag', 'dt' ); + break; + } + if ( + $node->isA( BalanceSets::$specialSet ) && + !$node->isA( BalanceSets::$addressDivPSet ) + ) { + break; + } + } + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'button': - if ( $this->stack->inScope( 'button' ) ) { - $this->inBodyMode( 'endtag', 'button' ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - } - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + // OMITTED: <plaintext> - case 'a': - $activeElement = $this->afe->findElementByTag( 'a' ); - if ( $activeElement ) { - $this->inBodyMode( 'endtag', 'a' ); - if ( $this->afe->isInList( $activeElement ) ) { - $this->afe->remove( $activeElement ); - // Don't flatten here, since when we fall - // through below we might foster parent - // the new <a> tag inside this one. - $this->stack->removeElement( $activeElement, false ); + case 'button': + if ( $this->stack->inScope( 'button' ) ) { + $this->inBodyMode( 'endtag', 'button' ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); } - } - // Falls through - case 'b': - case 'big': - case 'code': - case 'em': - case 'font': - case 'i': - case 's': - case 'small': - case 'strike': - case 'strong': - case 'tt': - case 'u': - $this->afe->reconstruct( $this->stack ); - $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ) ); - return true; - - case 'nobr': - $this->afe->reconstruct( $this->stack ); - if ( $this->stack->inScope( 'nobr' ) ) { - $this->inBodyMode( 'endtag', 'nobr' ); $this->afe->reconstruct( $this->stack ); - } - $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ) ); - return true; - - case 'applet': - case 'marquee': - case 'object': - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->afe->insertMarker(); - // OMITTED: frameset_ok - return true; + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'table': - // The document is never in "quirks mode"; see simplifications - // above. - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - // OMITTED: frameset_ok - $this->switchMode( 'inTableMode' ); - return true; + case 'a': + $activeElement = $this->afe->findElementByTag( 'a' ); + if ( $activeElement ) { + $this->inBodyMode( 'endtag', 'a' ); + if ( $this->afe->isInList( $activeElement ) ) { + $this->afe->remove( $activeElement ); + // Don't flatten here, since when we fall + // through below we might foster parent + // the new <a> tag inside this one. + $this->stack->removeElement( $activeElement, false ); + } + } + // Falls through + case 'b': + case 'big': + case 'code': + case 'em': + case 'font': + case 'i': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + $this->afe->reconstruct( $this->stack ); + $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ) ); + return true; - case 'area': - case 'br': - case 'embed': - case 'img': - case 'keygen': - case 'wbr': - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - // OMITTED: frameset_ok - return true; + case 'nobr': + $this->afe->reconstruct( $this->stack ); + if ( $this->stack->inScope( 'nobr' ) ) { + $this->inBodyMode( 'endtag', 'nobr' ); + $this->afe->reconstruct( $this->stack ); + } + $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ) ); + return true; - case 'input': - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - // OMITTED: frameset_ok - // (hence we don't need to examine the tag's "type" attribute) - return true; + case 'applet': + case 'marquee': + case 'object': + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->afe->insertMarker(); + // OMITTED: frameset_ok + return true; - case 'param': - case 'source': - case 'track': - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - return true; + case 'table': + // The document is never in "quirks mode"; see simplifications + // above. + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + $this->stack->insertHTMLElement( $value, $attribs ); + // OMITTED: frameset_ok + $this->switchMode( 'inTableMode' ); + return true; - case 'hr': - if ( $this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'endtag', 'p' ); - } - if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { + case 'area': + case 'br': + case 'embed': + case 'img': + case 'keygen': + case 'wbr': + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); $this->stack->pop(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - return true; - - case 'image': - // warts! - return $this->inBodyMode( $token, 'img', $attribs, $selfClose ); - - case 'textarea': - $this->stack->insertHTMLElement( $value, $attribs ); - $this->ignoreLinefeed = true; - $this->inRCDATA = $value; // emulate rcdata tokenizer mode - // OMITTED: frameset_ok - return true; - - // OMITTED: <xmp> - // OMITTED: <iframe> - // OMITTED: <noembed> - // OMITTED: <noscript> - - case 'select': - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - switch ( $this->parseMode ) { - case 'inTableMode': - case 'inCaptionMode': - case 'inTableBodyMode': - case 'inRowMode': - case 'inCellMode': - $this->switchMode( 'inSelectInTableMode' ); + // OMITTED: frameset_ok return true; - default: - $this->switchMode( 'inSelectMode' ); + + case 'input': + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->stack->pop(); + // OMITTED: frameset_ok + // (hence we don't need to examine the tag's "type" attribute) return true; - } - case 'optgroup': - case 'option': - if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { - $this->inBodyMode( 'endtag', 'option' ); - } - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + case 'param': + case 'source': + case 'track': + $this->stack->insertHTMLElement( $value, $attribs ); + $this->stack->pop(); + return true; - case 'menuitem': - if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { + case 'hr': + if ( $this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'endtag', 'p' ); + } + if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { + $this->stack->pop(); + } + $this->stack->insertHTMLElement( $value, $attribs ); $this->stack->pop(); - } - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + return true; - case 'rb': - case 'rtc': - if ( $this->stack->inScope( 'ruby' ) ) { - $this->stack->generateImpliedEndTags(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + case 'image': + // warts! + return $this->inBodyMode( $token, 'img', $attribs, $selfClose ); - case 'rp': - case 'rt': - if ( $this->stack->inScope( 'ruby' ) ) { - $this->stack->generateImpliedEndTags( 'rtc' ); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; + case 'textarea': + $this->stack->insertHTMLElement( $value, $attribs ); + $this->ignoreLinefeed = true; + $this->inRCDATA = $value; // emulate rcdata tokenizer mode + // OMITTED: frameset_ok + return true; - case 'math': - $this->afe->reconstruct( $this->stack ); - // We skip the spec's "adjust MathML attributes" and - // "adjust foreign attributes" steps, since the browser will - // do this later when it parses the output and it doesn't affect - // balancing. - $this->stack->insertForeignElement( - BalanceSets::MATHML_NAMESPACE, $value, $attribs - ); - if ( $selfClose ) { - // emit explicit </math> tag. - $this->stack->pop(); - } - return true; + // OMITTED: <xmp> + // OMITTED: <iframe> + // OMITTED: <noembed> + // OMITTED: <noscript> - case 'svg': - $this->afe->reconstruct( $this->stack ); - // We skip the spec's "adjust SVG attributes" and - // "adjust foreign attributes" steps, since the browser will - // do this later when it parses the output and it doesn't affect - // balancing. - $this->stack->insertForeignElement( - BalanceSets::SVG_NAMESPACE, $value, $attribs - ); - if ( $selfClose ) { - // emit explicit </svg> tag. - $this->stack->pop(); - } - return true; + case 'select': + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); + switch ( $this->parseMode ) { + case 'inTableMode': + case 'inCaptionMode': + case 'inTableBodyMode': + case 'inRowMode': + case 'inCellMode': + $this->switchMode( 'inSelectInTableMode' ); + return true; + default: + $this->switchMode( 'inSelectMode' ); + return true; + } - case 'caption': - case 'col': - case 'colgroup': - // OMITTED: <frame> - case 'head': - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - // Ignore table tags if we're not inTableMode - return true; - } + case 'optgroup': + case 'option': + if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { + $this->inBodyMode( 'endtag', 'option' ); + } + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - // Handle any other start tag here - $this->afe->reconstruct( $this->stack ); - $this->stack->insertHTMLElement( $value, $attribs ); - return true; - } elseif ( $token === 'endtag' ) { - switch ( $value ) { - // </body>,</html> are unsupported. - - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); - - case 'address': - case 'article': - case 'aside': - case 'blockquote': - case 'button': - case 'center': - case 'details': - case 'dialog': - case 'dir': - case 'div': - case 'dl': - case 'fieldset': - case 'figcaption': - case 'figure': - case 'footer': - case 'header': - case 'hgroup': - case 'listing': - case 'main': - case 'menu': - case 'nav': - case 'ol': - case 'pre': - case 'section': - case 'summary': - case 'ul': - // Ignore if there is not a matching open tag - if ( !$this->stack->inScope( $value ) ) { + case 'menuitem': + if ( $this->stack->currentNode->isHtmlNamed( 'menuitem' ) ) { + $this->stack->pop(); + } + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); return true; - } - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( $value ); - return true; - case 'form': - if ( $this->stack->indexOf( 'template' ) < 0 ) { - $openform = $this->formElementPointer; - $this->formElementPointer = null; - if ( !$openform || !$this->stack->inScope( $openform ) ) { - return true; + case 'rb': + case 'rtc': + if ( $this->stack->inScope( 'ruby' ) ) { + $this->stack->generateImpliedEndTags(); } - $this->stack->generateImpliedEndTags(); - // Don't flatten yet if we're removing a <form> element - // out-of-order. (eg. `<form><div></form>`) - $flatten = ( $this->stack->currentNode === $openform ); - $this->stack->removeElement( $openform, $flatten ); - } else { - if ( !$this->stack->inScope( 'form' ) ) { - return true; + $this->stack->insertHTMLElement( $value, $attribs ); + return true; + + case 'rp': + case 'rt': + if ( $this->stack->inScope( 'ruby' ) ) { + $this->stack->generateImpliedEndTags( 'rtc' ); } - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( 'form' ); - } - return true; + $this->stack->insertHTMLElement( $value, $attribs ); + return true; - case 'p': - if ( !$this->stack->inButtonScope( 'p' ) ) { - $this->inBodyMode( 'tag', 'p', [] ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - } - $this->stack->generateImpliedEndTags( $value ); - $this->stack->popTag( $value ); - return true; + case 'math': + $this->afe->reconstruct( $this->stack ); + // We skip the spec's "adjust MathML attributes" and + // "adjust foreign attributes" steps, since the browser will + // do this later when it parses the output and it doesn't affect + // balancing. + $this->stack->insertForeignElement( + BalanceSets::MATHML_NAMESPACE, $value, $attribs + ); + if ( $selfClose ) { + // emit explicit </math> tag. + $this->stack->pop(); + } + return true; - case 'li': - if ( !$this->stack->inListItemScope( $value ) ) { - return true; // ignore - } - $this->stack->generateImpliedEndTags( $value ); - $this->stack->popTag( $value ); - return true; + case 'svg': + $this->afe->reconstruct( $this->stack ); + // We skip the spec's "adjust SVG attributes" and + // "adjust foreign attributes" steps, since the browser will + // do this later when it parses the output and it doesn't affect + // balancing. + $this->stack->insertForeignElement( + BalanceSets::SVG_NAMESPACE, $value, $attribs + ); + if ( $selfClose ) { + // emit explicit </svg> tag. + $this->stack->pop(); + } + return true; - case 'dd': - case 'dt': - if ( !$this->stack->inScope( $value ) ) { - return true; // ignore - } - $this->stack->generateImpliedEndTags( $value ); - $this->stack->popTag( $value ); - return true; + case 'caption': + case 'col': + case 'colgroup': + // OMITTED: <frame> + case 'head': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + // Ignore table tags if we're not inTableMode + return true; + } - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - if ( !$this->stack->inScope( BalanceSets::$headingSet ) ) { - return true; // ignore - } - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( BalanceSets::$headingSet ); - return true; + // Handle any other start tag here + $this->afe->reconstruct( $this->stack ); + $this->stack->insertHTMLElement( $value, $attribs ); + return true; + } elseif ( $token === 'endtag' ) { + switch ( $value ) { + // </body>,</html> are unsupported. - case 'sarcasm': - // Take a deep breath, then: - break; + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + + case 'address': + case 'article': + case 'aside': + case 'blockquote': + case 'button': + case 'center': + case 'details': + case 'dialog': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'figcaption': + case 'figure': + case 'footer': + case 'header': + case 'hgroup': + case 'listing': + case 'main': + case 'menu': + case 'nav': + case 'ol': + case 'pre': + case 'section': + case 'summary': + case 'ul': + // Ignore if there is not a matching open tag + if ( !$this->stack->inScope( $value ) ) { + return true; + } + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( $value ); + return true; - case 'a': - case 'b': - case 'big': - case 'code': - case 'em': - case 'font': - case 'i': - case 'nobr': - case 's': - case 'small': - case 'strike': - case 'strong': - case 'tt': - case 'u': - if ( $this->stack->adoptionAgency( $value, $this->afe ) ) { - return true; // If we did something, we're done. - } - break; // Go to the "any other end tag" case. + case 'form': + if ( $this->stack->indexOf( 'template' ) < 0 ) { + $openform = $this->formElementPointer; + $this->formElementPointer = null; + if ( !$openform || !$this->stack->inScope( $openform ) ) { + return true; + } + $this->stack->generateImpliedEndTags(); + // Don't flatten yet if we're removing a <form> element + // out-of-order. (eg. `<form><div></form>`) + $flatten = ( $this->stack->currentNode === $openform ); + $this->stack->removeElement( $openform, $flatten ); + } else { + if ( !$this->stack->inScope( 'form' ) ) { + return true; + } + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( 'form' ); + } + return true; - case 'applet': - case 'marquee': - case 'object': - if ( !$this->stack->inScope( $value ) ) { - return true; // ignore - } - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( $value ); - $this->afe->clearToMarker(); - return true; + case 'p': + if ( !$this->stack->inButtonScope( 'p' ) ) { + $this->inBodyMode( 'tag', 'p', [] ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); + } + $this->stack->generateImpliedEndTags( $value ); + $this->stack->popTag( $value ); + return true; - case 'br': - // Turn </br> into <br> - return $this->inBodyMode( 'tag', $value, [] ); + case 'li': + if ( !$this->stack->inListItemScope( $value ) ) { + return true; // ignore + } + $this->stack->generateImpliedEndTags( $value ); + $this->stack->popTag( $value ); + return true; + + case 'dd': + case 'dt': + if ( !$this->stack->inScope( $value ) ) { + return true; // ignore + } + $this->stack->generateImpliedEndTags( $value ); + $this->stack->popTag( $value ); + return true; + + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + if ( !$this->stack->inScope( BalanceSets::$headingSet ) ) { + return true; // ignore + } + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( BalanceSets::$headingSet ); + return true; + + case 'sarcasm': + // Take a deep breath, then: + break; + + case 'a': + case 'b': + case 'big': + case 'code': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + if ( $this->stack->adoptionAgency( $value, $this->afe ) ) { + return true; // If we did something, we're done. + } + break; // Go to the "any other end tag" case. + + case 'applet': + case 'marquee': + case 'object': + if ( !$this->stack->inScope( $value ) ) { + return true; // ignore + } + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( $value ); + $this->afe->clearToMarker(); + return true; + + case 'br': + // Turn </br> into <br> + return $this->inBodyMode( 'tag', $value, [] ); } // Any other end tag goes here @@ -2985,87 +2985,87 @@ class Balancer { return true; } elseif ( $token === 'tag' ) { switch ( $value ) { - case 'caption': - $this->afe->insertMarker(); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->switchMode( 'inCaptionMode' ); - return true; - case 'colgroup': - $this->stack->clearToContext( BalanceSets::$tableContextSet ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->switchMode( 'inColumnGroupMode' ); - return true; - case 'col': - $this->inTableMode( 'tag', 'colgroup', [] ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - case 'tbody': - case 'tfoot': - case 'thead': - $this->stack->clearToContext( BalanceSets::$tableContextSet ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->switchMode( 'inTableBodyMode' ); - return true; - case 'td': - case 'th': - case 'tr': - $this->inTableMode( 'tag', 'tbody', [] ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - case 'table': - if ( !$this->stack->inTableScope( $value ) ) { - return true; // Ignore this tag. - } - $this->inTableMode( 'endtag', $value ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - - case 'style': - // OMITTED: <script> - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'caption': + $this->afe->insertMarker(); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->switchMode( 'inCaptionMode' ); + return true; + case 'colgroup': + $this->stack->clearToContext( BalanceSets::$tableContextSet ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->switchMode( 'inColumnGroupMode' ); + return true; + case 'col': + $this->inTableMode( 'tag', 'colgroup', [] ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); + case 'tbody': + case 'tfoot': + case 'thead': + $this->stack->clearToContext( BalanceSets::$tableContextSet ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->switchMode( 'inTableBodyMode' ); + return true; + case 'td': + case 'th': + case 'tr': + $this->inTableMode( 'tag', 'tbody', [] ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); + case 'table': + if ( !$this->stack->inTableScope( $value ) ) { + return true; // Ignore this tag. + } + $this->inTableMode( 'endtag', $value ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); - case 'input': - if ( !isset( $attribs['type'] ) || strcasecmp( $attribs['type'], 'hidden' ) !== 0 ) { - break; // Handle this as "everything else" - } - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - return true; + case 'style': + // OMITTED: <script> + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); - case 'form': - if ( - $this->formElementPointer || - $this->stack->indexOf( 'template' ) >= 0 - ) { - return true; // ignore this token - } - $this->formElementPointer = + case 'input': + if ( !isset( $attribs['type'] ) || strcasecmp( $attribs['type'], 'hidden' ) !== 0 ) { + break; // Handle this as "everything else" + } $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->popTag( $this->formElementPointer ); - return true; + $this->stack->pop(); + return true; + + case 'form': + if ( + $this->formElementPointer || + $this->stack->indexOf( 'template' ) >= 0 + ) { + return true; // ignore this token + } + $this->formElementPointer = + $this->stack->insertHTMLElement( $value, $attribs ); + $this->stack->popTag( $this->formElementPointer ); + return true; } // Fall through for "anything else" clause. } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'table': - if ( !$this->stack->inTableScope( $value ) ) { - return true; // Ignore. - } - $this->stack->popTag( $value ); - $this->resetInsertionMode(); - return true; - // OMITTED: <body> - case 'caption': - case 'col': - case 'colgroup': - // OMITTED: <html> - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - return true; // Ignore the token. - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'table': + if ( !$this->stack->inTableScope( $value ) ) { + return true; // Ignore. + } + $this->stack->popTag( $value ); + $this->resetInsertionMode(); + return true; + // OMITTED: <body> + case 'caption': + case 'col': + case 'colgroup': + // OMITTED: <html> + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + return true; // Ignore the token. + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } // Fall through for "anything else" clause. } elseif ( $token === 'comment' ) { @@ -3116,43 +3116,43 @@ class Balancer { private function inCaptionMode( $token, $value, $attribs = null, $selfClose = false ) { if ( $token === 'tag' ) { switch ( $value ) { - case 'caption': - case 'col': - case 'colgroup': - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - if ( $this->endCaption() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + if ( $this->endCaption() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } // Fall through to "anything else" case. } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'caption': - $this->endCaption(); - return true; - case 'table': - if ( $this->endCaption() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; - case 'body': - case 'col': - case 'colgroup': - // OMITTED: <html> - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - // Ignore the token - return true; + case 'caption': + $this->endCaption(); + return true; + case 'table': + if ( $this->endCaption() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; + case 'body': + case 'col': + case 'colgroup': + // OMITTED: <html> + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + // Ignore the token + return true; } // Fall through to "anything else" case. } @@ -3172,28 +3172,28 @@ class Balancer { // Fall through to handle non-whitespace below. } elseif ( $token === 'tag' ) { switch ( $value ) { - // OMITTED: <html> - case 'col': - $this->stack->insertHTMLElement( $value, $attribs ); - $this->stack->pop(); - return true; - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + // OMITTED: <html> + case 'col': + $this->stack->insertHTMLElement( $value, $attribs ); + $this->stack->pop(); + return true; + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } // Fall through for "anything else". } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'colgroup': - if ( !$this->stack->currentNode->isHtmlNamed( 'colgroup' ) ) { + case 'colgroup': + if ( !$this->stack->currentNode->isHtmlNamed( 'colgroup' ) ) { + return true; // Ignore the token. + } + $this->stack->pop(); + $this->switchMode( 'inTableMode' ); + return true; + case 'col': return true; // Ignore the token. - } - $this->stack->pop(); - $this->switchMode( 'inTableMode' ); - return true; - case 'col': - return true; // Ignore the token. - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } // Fall through for "anything else". } elseif ( $token === 'eof' ) { @@ -3228,50 +3228,50 @@ class Balancer { private function inTableBodyMode( $token, $value, $attribs = null, $selfClose = false ) { if ( $token === 'tag' ) { switch ( $value ) { - case 'tr': - $this->stack->clearToContext( BalanceSets::$tableBodyContextSet ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->switchMode( 'inRowMode' ); - return true; - case 'th': - case 'td': - $this->inTableBodyMode( 'tag', 'tr', [] ); - $this->insertToken( $token, $value, $attribs, $selfClose ); - return true; - case 'caption': - case 'col': - case 'colgroup': - case 'tbody': - case 'tfoot': - case 'thead': - if ( $this->endSection() ) { + case 'tr': + $this->stack->clearToContext( BalanceSets::$tableBodyContextSet ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->switchMode( 'inRowMode' ); + return true; + case 'th': + case 'td': + $this->inTableBodyMode( 'tag', 'tr', [] ); $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; + return true; + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'tfoot': + case 'thead': + if ( $this->endSection() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'table': - if ( $this->endSection() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; - case 'tbody': - case 'tfoot': - case 'thead': - if ( $this->stack->inTableScope( $value ) ) { - $this->endSection(); - } - return true; - // OMITTED: <body> - case 'caption': - case 'col': - case 'colgroup': - // OMITTED: <html> - case 'td': - case 'th': - case 'tr': - return true; // Ignore the token. + case 'table': + if ( $this->endSection() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; + case 'tbody': + case 'tfoot': + case 'thead': + if ( $this->stack->inTableScope( $value ) ) { + $this->endSection(); + } + return true; + // OMITTED: <body> + case 'caption': + case 'col': + case 'colgroup': + // OMITTED: <html> + case 'td': + case 'th': + case 'tr': + return true; // Ignore the token. } } // Anything else: @@ -3291,53 +3291,53 @@ class Balancer { private function inRowMode( $token, $value, $attribs = null, $selfClose = false ) { if ( $token === 'tag' ) { switch ( $value ) { - case 'th': - case 'td': - $this->stack->clearToContext( BalanceSets::$tableRowContextSet ); - $this->stack->insertHTMLElement( $value, $attribs ); - $this->switchMode( 'inCellMode' ); - $this->afe->insertMarker(); - return true; - case 'caption': - case 'col': - case 'colgroup': - case 'tbody': - case 'tfoot': - case 'thead': - case 'tr': - if ( $this->endRow() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; + case 'th': + case 'td': + $this->stack->clearToContext( BalanceSets::$tableRowContextSet ); + $this->stack->insertHTMLElement( $value, $attribs ); + $this->switchMode( 'inCellMode' ); + $this->afe->insertMarker(); + return true; + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'tfoot': + case 'thead': + case 'tr': + if ( $this->endRow() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'tr': - $this->endRow(); - return true; - case 'table': - if ( $this->endRow() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; - case 'tbody': - case 'tfoot': - case 'thead': - if ( - $this->stack->inTableScope( $value ) && - $this->endRow() - ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; - // OMITTED: <body> - case 'caption': - case 'col': - case 'colgroup': - // OMITTED: <html> - case 'td': - case 'th': - return true; // Ignore the token. + case 'tr': + $this->endRow(); + return true; + case 'table': + if ( $this->endRow() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; + case 'tbody': + case 'tfoot': + case 'thead': + if ( + $this->stack->inTableScope( $value ) && + $this->endRow() + ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; + // OMITTED: <body> + case 'caption': + case 'col': + case 'colgroup': + // OMITTED: <html> + case 'td': + case 'th': + return true; // Ignore the token. } } // Anything else: @@ -3359,51 +3359,51 @@ class Balancer { private function inCellMode( $token, $value, $attribs = null, $selfClose = false ) { if ( $token === 'tag' ) { switch ( $value ) { - case 'caption': - case 'col': - case 'colgroup': - case 'tbody': - case 'td': - case 'tfoot': - case 'th': - case 'thead': - case 'tr': - if ( $this->endCell() ) { - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + if ( $this->endCell() ) { + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'td': - case 'th': - if ( $this->stack->inTableScope( $value ) ) { - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( $value ); - $this->afe->clearToMarker(); - $this->switchMode( 'inRowMode' ); - } - return true; - // OMITTED: <body> - case 'caption': - case 'col': - case 'colgroup': - // OMITTED: <html> - return true; + case 'td': + case 'th': + if ( $this->stack->inTableScope( $value ) ) { + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( $value ); + $this->afe->clearToMarker(); + $this->switchMode( 'inRowMode' ); + } + return true; + // OMITTED: <body> + case 'caption': + case 'col': + case 'colgroup': + // OMITTED: <html> + return true; - case 'table': - case 'tbody': - case 'tfoot': - case 'thead': - case 'tr': - if ( $this->stack->inTableScope( $value ) ) { - $this->stack->generateImpliedEndTags(); - $this->stack->popTag( BalanceSets::$tableCellSet ); - $this->afe->clearToMarker(); - $this->switchMode( 'inRowMode' ); - $this->insertToken( $token, $value, $attribs, $selfClose ); - } - return true; + case 'table': + case 'tbody': + case 'tfoot': + case 'thead': + case 'tr': + if ( $this->stack->inTableScope( $value ) ) { + $this->stack->generateImpliedEndTags(); + $this->stack->popTag( BalanceSets::$tableCellSet ); + $this->afe->clearToMarker(); + $this->switchMode( 'inRowMode' ); + $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } } // Anything else: @@ -3418,65 +3418,65 @@ class Balancer { return $this->inBodyMode( $token, $value, $attribs, $selfClose ); } elseif ( $token === 'tag' ) { switch ( $value ) { - // OMITTED: <html> - case 'option': - if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { - $this->stack->pop(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; - case 'optgroup': - if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { - $this->stack->pop(); - } - if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) { - $this->stack->pop(); - } - $this->stack->insertHTMLElement( $value, $attribs ); - return true; - case 'select': - $this->inSelectMode( 'endtag', $value ); // treat it like endtag - return true; - case 'input': - case 'keygen': - case 'textarea': - if ( !$this->stack->inSelectScope( 'select' ) ) { - return true; // ignore token (fragment case) - } - $this->inSelectMode( 'endtag', 'select' ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - case 'script': - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + // OMITTED: <html> + case 'option': + if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { + $this->stack->pop(); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; + case 'optgroup': + if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { + $this->stack->pop(); + } + if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) { + $this->stack->pop(); + } + $this->stack->insertHTMLElement( $value, $attribs ); + return true; + case 'select': + $this->inSelectMode( 'endtag', $value ); // treat it like endtag + return true; + case 'input': + case 'keygen': + case 'textarea': + if ( !$this->stack->inSelectScope( 'select' ) ) { + return true; // ignore token (fragment case) + } + $this->inSelectMode( 'endtag', 'select' ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); + case 'script': + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'optgroup': - if ( - $this->stack->currentNode->isHtmlNamed( 'option' ) && - $this->stack->length() >= 2 && - $this->stack->node( $this->stack->length() - 2 )->isHtmlNamed( 'optgroup' ) - ) { - $this->stack->pop(); - } - if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) { - $this->stack->pop(); - } - return true; - case 'option': - if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { - $this->stack->pop(); - } - return true; - case 'select': - if ( !$this->stack->inSelectScope( $value ) ) { - return true; // fragment case - } - $this->stack->popTag( $value ); - $this->resetInsertionMode(); - return true; - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'optgroup': + if ( + $this->stack->currentNode->isHtmlNamed( 'option' ) && + $this->stack->length() >= 2 && + $this->stack->node( $this->stack->length() - 2 )->isHtmlNamed( 'optgroup' ) + ) { + $this->stack->pop(); + } + if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) { + $this->stack->pop(); + } + return true; + case 'option': + if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) { + $this->stack->pop(); + } + return true; + case 'select': + if ( !$this->stack->inSelectScope( $value ) ) { + return true; // fragment case + } + $this->stack->popTag( $value ); + $this->resetInsertionMode(); + return true; + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } } elseif ( $token === 'comment' ) { $this->stack->insertComment( $value ); @@ -3488,24 +3488,24 @@ class Balancer { private function inSelectInTableMode( $token, $value, $attribs = null, $selfClose = false ) { switch ( $value ) { - case 'caption': - case 'table': - case 'tbody': - case 'tfoot': - case 'thead': - case 'tr': - case 'td': - case 'th': - if ( $token === 'tag' ) { - $this->inSelectInTableMode( 'endtag', 'select' ); - return $this->insertToken( $token, $value, $attribs, $selfClose ); - } elseif ( $token === 'endtag' ) { - if ( $this->stack->inTableScope( $value ) ) { + case 'caption': + case 'table': + case 'tbody': + case 'tfoot': + case 'thead': + case 'tr': + case 'td': + case 'th': + if ( $token === 'tag' ) { $this->inSelectInTableMode( 'endtag', 'select' ); return $this->insertToken( $token, $value, $attribs, $selfClose ); + } elseif ( $token === 'endtag' ) { + if ( $this->stack->inTableScope( $value ) ) { + $this->inSelectInTableMode( 'endtag', 'select' ); + return $this->insertToken( $token, $value, $attribs, $selfClose ); + } + return true; } - return true; - } } // anything else return $this->inSelectMode( $token, $value, $attribs, $selfClose ); @@ -3527,50 +3527,50 @@ class Balancer { return true; } elseif ( $token === 'tag' ) { switch ( $value ) { - case 'base': - case 'basefont': - case 'bgsound': - case 'link': - case 'meta': - case 'noframes': - // OMITTED: <script> - case 'style': - case 'template': - // OMITTED: <title> - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'base': + case 'basefont': + case 'bgsound': + case 'link': + case 'meta': + case 'noframes': + // OMITTED: <script> + case 'style': + case 'template': + // OMITTED: <title> + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); - case 'caption': - case 'colgroup': - case 'tbody': - case 'tfoot': - case 'thead': - return $this->switchModeAndReprocess( - 'inTableMode', $token, $value, $attribs, $selfClose - ); + case 'caption': + case 'colgroup': + case 'tbody': + case 'tfoot': + case 'thead': + return $this->switchModeAndReprocess( + 'inTableMode', $token, $value, $attribs, $selfClose + ); - case 'col': - return $this->switchModeAndReprocess( - 'inColumnGroupMode', $token, $value, $attribs, $selfClose - ); + case 'col': + return $this->switchModeAndReprocess( + 'inColumnGroupMode', $token, $value, $attribs, $selfClose + ); - case 'tr': - return $this->switchModeAndReprocess( - 'inTableBodyMode', $token, $value, $attribs, $selfClose - ); + case 'tr': + return $this->switchModeAndReprocess( + 'inTableBodyMode', $token, $value, $attribs, $selfClose + ); - case 'td': - case 'th': - return $this->switchModeAndReprocess( - 'inRowMode', $token, $value, $attribs, $selfClose - ); + case 'td': + case 'th': + return $this->switchModeAndReprocess( + 'inRowMode', $token, $value, $attribs, $selfClose + ); } return $this->switchModeAndReprocess( 'inBodyMode', $token, $value, $attribs, $selfClose ); } elseif ( $token === 'endtag' ) { switch ( $value ) { - case 'template': - return $this->inHeadMode( $token, $value, $attribs, $selfClose ); + case 'template': + return $this->inHeadMode( $token, $value, $attribs, $selfClose ); } return true; } else { diff --git a/includes/user/User.php b/includes/user/User.php index 37a80f22bc..0d8ef89290 100644 --- a/includes/user/User.php +++ b/includes/user/User.php @@ -179,6 +179,7 @@ class User implements IDBAccessObject, UserIdentity { 'reupload-shared', 'rollback', 'sendemail', + 'sendemail-new-users', 'siteadmin', 'suppressionlog', 'suppressredirect', @@ -233,7 +234,7 @@ class User implements IDBAccessObject, UserIdentity { * @deprecated since 1.29 */ private $mGroups; - /** @var array Associative array of (group name => UserGroupMembership object) */ + /** @var UserGroupMembership[] Associative array of (group name => UserGroupMembership object) */ protected $mGroupMemberships; /** @var array */ protected $mOptionOverrides; @@ -3316,7 +3317,7 @@ class User implements IDBAccessObject, UserIdentity { * Get the list of explicit group memberships this user has, stored as * UserGroupMembership objects. Implicit groups are not included. * - * @return array Associative array of (group name as string => UserGroupMembership object) + * @return UserGroupMembership[] Associative array of (group name => UserGroupMembership object) * @since 1.29 */ public function getGroupMemberships() { diff --git a/includes/user/UserGroupMembership.php b/includes/user/UserGroupMembership.php index a06be834c4..f771f4285e 100644 --- a/includes/user/UserGroupMembership.php +++ b/includes/user/UserGroupMembership.php @@ -276,7 +276,7 @@ class UserGroupMembership { * * @param int $userId ID of the user to search for * @param IDatabase|null $db Optional database connection - * @return array Associative array of (group name => UserGroupMembership object) + * @return UserGroupMembership[] Associative array of (group name => UserGroupMembership object) */ public static function getMembershipsForUser( $userId, IDatabase $db = null ) { if ( !$db ) { diff --git a/includes/user/UserIdentityValue.php b/includes/user/UserIdentityValue.php new file mode 100644 index 0000000000..e728264a3f --- /dev/null +++ b/includes/user/UserIdentityValue.php @@ -0,0 +1,70 @@ +<?php +/** + * Value object representing a user's identity. + * + * 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\User; + +use Wikimedia\Assert\Assert; + +/** + * Value object representing a user's identity. + * + * @since 1.31 + */ +class UserIdentityValue implements UserIdentity { + + /** + * @var int + */ + private $id; + + /** + * @var string + */ + private $name; + + /** + * @param int $id + * @param string $name + */ + public function __construct( $id, $name ) { + Assert::parameterType( 'integer', $id, '$id' ); + Assert::parameterType( 'string', $name, '$name' ); + + $this->id = $id; + $this->name = $name; + } + + /** + * @return int The user ID. May be 0 for anonymous users or for users with no local account. + */ + public function getId() { + return $this->id; + } + + /** + * @return string The user's logical name. May be an IPv4 or IPv6 address for anonymous users. + */ + public function getName() { + return $this->name; + } + +} diff --git a/includes/utils/AutoloadGenerator.php b/includes/utils/AutoloadGenerator.php index 421a89067f..d12531b304 100644 --- a/includes/utils/AutoloadGenerator.php +++ b/includes/utils/AutoloadGenerator.php @@ -42,6 +42,13 @@ class AutoloadGenerator { */ protected $overrides = []; + /** + * Directories that should be excluded + * + * @var string[] + */ + protected $excludePaths = []; + /** * @param string $basepath Root path of the project being scanned for classes * @param array|string $flags @@ -60,6 +67,34 @@ class AutoloadGenerator { } } + /** + * Directories that should be excluded + * + * @since 1.31 + * @param string[] $paths + */ + public function setExcludePaths( array $paths ) { + foreach ( $paths as $path ) { + $this->excludePaths[] = self::normalizePathSeparator( $path ); + } + } + + /** + * Whether the file should be excluded + * + * @param string $path File path + * @return bool + */ + private function shouldExclude( $path ) { + foreach ( $this->excludePaths as $dir ) { + if ( strpos( $path, $dir ) === 0 ) { + return true; + } + } + + return false; + } + /** * Force a class to be autoloaded from a specific path, regardless of where * or if it was detected. @@ -94,6 +129,9 @@ class AutoloadGenerator { if ( substr( $inputPath, 0, $len ) !== $this->basepath ) { throw new \Exception( "Path is not within basepath: $inputPath" ); } + if ( $this->shouldExclude( $inputPath ) ) { + return; + } $result = $this->collector->getClasses( file_get_contents( $inputPath ) ); @@ -354,18 +392,18 @@ class ClassCollector { // Note: When changing class name discovery logic, // AutoLoaderTest.php may also need to be updated. switch ( $token[0] ) { - case T_NAMESPACE: - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - case T_DOUBLE_COLON: - $this->startToken = $token; - break; - case T_STRING: - if ( $token[1] === 'class_alias' ) { + case T_NAMESPACE: + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + case T_DOUBLE_COLON: $this->startToken = $token; - $this->alias = []; - } + break; + case T_STRING: + if ( $token[1] === 'class_alias' ) { + $this->startToken = $token; + $this->alias = []; + } } } @@ -376,78 +414,78 @@ class ClassCollector { */ protected function tryEndExpect( $token ) { switch ( $this->startToken[0] ) { - case T_DOUBLE_COLON: - // Skip over T_CLASS after T_DOUBLE_COLON because this is something like - // "self::static" which accesses the class name. It doens't define a new class. - $this->startToken = null; - break; - case T_NAMESPACE: - if ( $token === ';' || $token === '{' ) { - $this->namespace = $this->implodeTokens() . '\\'; - } else { - $this->tokens[] = $token; - } - break; - - case T_STRING: - if ( $this->alias !== null ) { - // Flow 1 - Two string literals: - // - T_STRING class_alias - // - '(' - // - T_CONSTANT_ENCAPSED_STRING 'TargetClass' - // - ',' - // - T_WHITESPACE - // - T_CONSTANT_ENCAPSED_STRING 'AliasName' - // - ')' - // Flow 2 - Use of ::class syntax for first parameter - // - T_STRING class_alias - // - '(' - // - T_STRING TargetClass - // - T_DOUBLE_COLON :: - // - T_CLASS class - // - ',' - // - T_WHITESPACE - // - T_CONSTANT_ENCAPSED_STRING 'AliasName' - // - ')' - if ( $token === '(' ) { - // Start of a function call to class_alias() - $this->alias = [ 'target' => false, 'name' => false ]; - } elseif ( $token === ',' ) { - // Record that we're past the first parameter - if ( $this->alias['target'] === false ) { - $this->alias['target'] = true; - } - } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) { - if ( $this->alias['target'] === true ) { - // We already saw a first argument, this must be the second. - // Strip quotes from the string literal. - $this->alias['name'] = substr( $token[1], 1, -1 ); + case T_DOUBLE_COLON: + // Skip over T_CLASS after T_DOUBLE_COLON because this is something like + // "self::static" which accesses the class name. It doens't define a new class. + $this->startToken = null; + break; + case T_NAMESPACE: + if ( $token === ';' || $token === '{' ) { + $this->namespace = $this->implodeTokens() . '\\'; + } else { + $this->tokens[] = $token; + } + break; + + case T_STRING: + if ( $this->alias !== null ) { + // Flow 1 - Two string literals: + // - T_STRING class_alias + // - '(' + // - T_CONSTANT_ENCAPSED_STRING 'TargetClass' + // - ',' + // - T_WHITESPACE + // - T_CONSTANT_ENCAPSED_STRING 'AliasName' + // - ')' + // Flow 2 - Use of ::class syntax for first parameter + // - T_STRING class_alias + // - '(' + // - T_STRING TargetClass + // - T_DOUBLE_COLON :: + // - T_CLASS class + // - ',' + // - T_WHITESPACE + // - T_CONSTANT_ENCAPSED_STRING 'AliasName' + // - ')' + if ( $token === '(' ) { + // Start of a function call to class_alias() + $this->alias = [ 'target' => false, 'name' => false ]; + } elseif ( $token === ',' ) { + // Record that we're past the first parameter + if ( $this->alias['target'] === false ) { + $this->alias['target'] = true; + } + } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) { + if ( $this->alias['target'] === true ) { + // We already saw a first argument, this must be the second. + // Strip quotes from the string literal. + $this->alias['name'] = substr( $token[1], 1, -1 ); + } + } elseif ( $token === ')' ) { + // End of function call + $this->classes[] = $this->alias['name']; + $this->alias = null; + $this->startToken = null; + } elseif ( !is_array( $token ) || ( + $token[0] !== T_STRING && + $token[0] !== T_DOUBLE_COLON && + $token[0] !== T_CLASS && + $token[0] !== T_WHITESPACE + ) ) { + // Ignore this call to class_alias() - compat/Timestamp.php + $this->alias = null; + $this->startToken = null; } - } elseif ( $token === ')' ) { - // End of function call - $this->classes[] = $this->alias['name']; - $this->alias = null; - $this->startToken = null; - } elseif ( !is_array( $token ) || ( - $token[0] !== T_STRING && - $token[0] !== T_DOUBLE_COLON && - $token[0] !== T_CLASS && - $token[0] !== T_WHITESPACE - ) ) { - // Ignore this call to class_alias() - compat/Timestamp.php - $this->alias = null; - $this->startToken = null; } - } - break; - - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - $this->tokens[] = $token; - if ( is_array( $token ) && $token[0] === T_STRING ) { - $this->classes[] = $this->namespace . $this->implodeTokens(); - } + break; + + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + $this->tokens[] = $token; + if ( is_array( $token ) && $token[0] === T_STRING ) { + $this->classes[] = $this->namespace . $this->implodeTokens(); + } } } diff --git a/includes/utils/AvroValidator.php b/includes/utils/AvroValidator.php index 554dda9d6d..153b313599 100644 --- a/includes/utils/AvroValidator.php +++ b/includes/utils/AvroValidator.php @@ -37,133 +37,133 @@ class AvroValidator { */ public static function getErrors( AvroSchema $schema, $datum ) { switch ( $schema->type ) { - case AvroSchema::NULL_TYPE: - if ( !is_null( $datum ) ) { - return self::wrongType( 'null', $datum ); - } - return []; - case AvroSchema::BOOLEAN_TYPE: - if ( !is_bool( $datum ) ) { - return self::wrongType( 'boolean', $datum ); - } - return []; - case AvroSchema::STRING_TYPE: - case AvroSchema::BYTES_TYPE: - if ( !is_string( $datum ) ) { - return self::wrongType( 'string', $datum ); - } - return []; - case AvroSchema::INT_TYPE: - if ( !is_int( $datum ) ) { - return self::wrongType( 'integer', $datum ); - } - if ( AvroSchema::INT_MIN_VALUE > $datum - || $datum > AvroSchema::INT_MAX_VALUE - ) { - return self::outOfRange( - AvroSchema::INT_MIN_VALUE, - AvroSchema::INT_MAX_VALUE, - $datum - ); - } - return []; - case AvroSchema::LONG_TYPE: - if ( !is_int( $datum ) ) { - return self::wrongType( 'integer', $datum ); - } - if ( AvroSchema::LONG_MIN_VALUE > $datum - || $datum > AvroSchema::LONG_MAX_VALUE - ) { - return self::outOfRange( - AvroSchema::LONG_MIN_VALUE, - AvroSchema::LONG_MAX_VALUE, - $datum - ); - } - return []; - case AvroSchema::FLOAT_TYPE: - case AvroSchema::DOUBLE_TYPE: - if ( !is_float( $datum ) && !is_int( $datum ) ) { - return self::wrongType( 'float or integer', $datum ); - } - return []; - case AvroSchema::ARRAY_SCHEMA: - if ( !is_array( $datum ) ) { - return self::wrongType( 'array', $datum ); - } - $errors = []; - foreach ( $datum as $d ) { - $result = self::getErrors( $schema->items(), $d ); - if ( $result ) { + case AvroSchema::NULL_TYPE: + if ( !is_null( $datum ) ) { + return self::wrongType( 'null', $datum ); + } + return []; + case AvroSchema::BOOLEAN_TYPE: + if ( !is_bool( $datum ) ) { + return self::wrongType( 'boolean', $datum ); + } + return []; + case AvroSchema::STRING_TYPE: + case AvroSchema::BYTES_TYPE: + if ( !is_string( $datum ) ) { + return self::wrongType( 'string', $datum ); + } + return []; + case AvroSchema::INT_TYPE: + if ( !is_int( $datum ) ) { + return self::wrongType( 'integer', $datum ); + } + if ( AvroSchema::INT_MIN_VALUE > $datum + || $datum > AvroSchema::INT_MAX_VALUE + ) { + return self::outOfRange( + AvroSchema::INT_MIN_VALUE, + AvroSchema::INT_MAX_VALUE, + $datum + ); + } + return []; + case AvroSchema::LONG_TYPE: + if ( !is_int( $datum ) ) { + return self::wrongType( 'integer', $datum ); + } + if ( AvroSchema::LONG_MIN_VALUE > $datum + || $datum > AvroSchema::LONG_MAX_VALUE + ) { + return self::outOfRange( + AvroSchema::LONG_MIN_VALUE, + AvroSchema::LONG_MAX_VALUE, + $datum + ); + } + return []; + case AvroSchema::FLOAT_TYPE: + case AvroSchema::DOUBLE_TYPE: + if ( !is_float( $datum ) && !is_int( $datum ) ) { + return self::wrongType( 'float or integer', $datum ); + } + return []; + case AvroSchema::ARRAY_SCHEMA: + if ( !is_array( $datum ) ) { + return self::wrongType( 'array', $datum ); + } + $errors = []; + foreach ( $datum as $d ) { + $result = self::getErrors( $schema->items(), $d ); + if ( $result ) { + $errors[] = $result; + } + } + return $errors; + case AvroSchema::MAP_SCHEMA: + if ( !is_array( $datum ) ) { + return self::wrongType( 'array', $datum ); + } + $errors = []; + foreach ( $datum as $k => $v ) { + if ( !is_string( $k ) ) { + $errors[] = self::wrongType( 'string key', $k ); + } + $result = self::getErrors( $schema->values(), $v ); + if ( $result ) { + $errors[$k] = $result; + } + } + return $errors; + case AvroSchema::UNION_SCHEMA: + $errors = []; + foreach ( $schema->schemas() as $schema ) { + $result = self::getErrors( $schema, $datum ); + if ( !$result ) { + return []; + } $errors[] = $result; } - } - return $errors; - case AvroSchema::MAP_SCHEMA: - if ( !is_array( $datum ) ) { - return self::wrongType( 'array', $datum ); - } - $errors = []; - foreach ( $datum as $k => $v ) { - if ( !is_string( $k ) ) { - $errors[] = self::wrongType( 'string key', $k ); - } - $result = self::getErrors( $schema->values(), $v ); - if ( $result ) { - $errors[$k] = $result; - } - } - return $errors; - case AvroSchema::UNION_SCHEMA: - $errors = []; - foreach ( $schema->schemas() as $schema ) { - $result = self::getErrors( $schema, $datum ); - if ( !$result ) { - return []; - } - $errors[] = $result; - } - if ( $errors ) { - return [ "Expected any one of these to be true", $errors ]; - } - return "No schemas provided to union"; - case AvroSchema::ENUM_SCHEMA: - if ( !in_array( $datum, $schema->symbols() ) ) { - $symbols = implode( ', ', $schema->symbols ); - return "Expected one of $symbols but recieved $datum"; - } - return []; - case AvroSchema::FIXED_SCHEMA: - if ( !is_string( $datum ) ) { - return self::wrongType( 'string', $datum ); - } - $len = strlen( $datum ); - if ( $len !== $schema->size() ) { - return "Expected string of length {$schema->size()}, " - . "but recieved one of length $len"; - } - return []; - case AvroSchema::RECORD_SCHEMA: - case AvroSchema::ERROR_SCHEMA: - case AvroSchema::REQUEST_SCHEMA: - if ( !is_array( $datum ) ) { - return self::wrongType( 'array', $datum ); - } - $errors = []; - foreach ( $schema->fields() as $field ) { - $name = $field->name(); - if ( !array_key_exists( $name, $datum ) ) { - $errors[$name] = 'Missing expected field'; - continue; - } - $result = self::getErrors( $field->type(), $datum[$name] ); - if ( $result ) { - $errors[$name] = $result; - } - } - return $errors; - default: - return "Unknown avro schema type: {$schema->type}"; + if ( $errors ) { + return [ "Expected any one of these to be true", $errors ]; + } + return "No schemas provided to union"; + case AvroSchema::ENUM_SCHEMA: + if ( !in_array( $datum, $schema->symbols() ) ) { + $symbols = implode( ', ', $schema->symbols ); + return "Expected one of $symbols but recieved $datum"; + } + return []; + case AvroSchema::FIXED_SCHEMA: + if ( !is_string( $datum ) ) { + return self::wrongType( 'string', $datum ); + } + $len = strlen( $datum ); + if ( $len !== $schema->size() ) { + return "Expected string of length {$schema->size()}, " + . "but recieved one of length $len"; + } + return []; + case AvroSchema::RECORD_SCHEMA: + case AvroSchema::ERROR_SCHEMA: + case AvroSchema::REQUEST_SCHEMA: + if ( !is_array( $datum ) ) { + return self::wrongType( 'array', $datum ); + } + $errors = []; + foreach ( $schema->fields() as $field ) { + $name = $field->name(); + if ( !array_key_exists( $name, $datum ) ) { + $errors[$name] = 'Missing expected field'; + continue; + } + $result = self::getErrors( $field->type(), $datum[$name] ); + if ( $result ) { + $errors[$name] = $result; + } + } + return $errors; + default: + return "Unknown avro schema type: {$schema->type}"; } } diff --git a/includes/watcheditem/WatchedItem.php b/includes/watcheditem/WatchedItem.php index bfd1d6136b..43a9c4e536 100644 --- a/includes/watcheditem/WatchedItem.php +++ b/includes/watcheditem/WatchedItem.php @@ -18,7 +18,7 @@ * @file * @ingroup Watchlist */ -use MediaWiki\MediaWikiServices; + use MediaWiki\Linker\LinkTarget; /** @@ -30,34 +30,6 @@ use MediaWiki\Linker\LinkTarget; * @ingroup Watchlist */ class WatchedItem { - - /** - * @deprecated since 1.27, see User::IGNORE_USER_RIGHTS - */ - const IGNORE_USER_RIGHTS = User::IGNORE_USER_RIGHTS; - - /** - * @deprecated since 1.27, see User::CHECK_USER_RIGHTS - */ - const CHECK_USER_RIGHTS = User::CHECK_USER_RIGHTS; - - /** - * @deprecated Internal class use only - */ - const DEPRECATED_USAGE_TIMESTAMP = -100; - - /** - * @var bool - * @deprecated Internal class use only - */ - public $checkRights = User::CHECK_USER_RIGHTS; - - /** - * @var Title - * @deprecated Internal class use only - */ - private $title; - /** * @var LinkTarget */ @@ -77,20 +49,15 @@ class WatchedItem { * @param User $user * @param LinkTarget $linkTarget * @param null|string $notificationTimestamp the value of the wl_notificationtimestamp field - * @param bool|null $checkRights DO NOT USE - used internally for backward compatibility */ public function __construct( User $user, LinkTarget $linkTarget, - $notificationTimestamp, - $checkRights = null + $notificationTimestamp ) { $this->user = $user; $this->linkTarget = $linkTarget; $this->notificationTimestamp = $notificationTimestamp; - if ( $checkRights !== null ) { - $this->checkRights = $checkRights; - } } /** @@ -113,88 +80,6 @@ class WatchedItem { * @return bool|null|string */ public function getNotificationTimestamp() { - // Back compat for objects constructed using self::fromUserTitle - if ( $this->notificationTimestamp === self::DEPRECATED_USAGE_TIMESTAMP ) { - // wfDeprecated( __METHOD__, '1.27' ); - if ( $this->checkRights && !$this->user->isAllowed( 'viewmywatchlist' ) ) { - return false; - } - $item = MediaWikiServices::getInstance()->getWatchedItemStore() - ->loadWatchedItem( $this->user, $this->linkTarget ); - if ( $item ) { - $this->notificationTimestamp = $item->getNotificationTimestamp(); - } else { - $this->notificationTimestamp = false; - } - } return $this->notificationTimestamp; } - - /** - * Back compat pre 1.27 with the WatchedItemStore introduction - * @todo remove in 1.28/9 - * ------------------------------------------------- - */ - - /** - * @return Title - * @deprecated Internal class use only - */ - public function getTitle() { - if ( !$this->title ) { - $this->title = Title::newFromLinkTarget( $this->linkTarget ); - } - return $this->title; - } - - /** - * @deprecated since 1.27 Use the constructor, WatchedItemStore::getWatchedItem() - * or WatchedItemStore::loadWatchedItem() - */ - public static function fromUserTitle( $user, $title, $checkRights = User::CHECK_USER_RIGHTS ) { - wfDeprecated( __METHOD__, '1.27' ); - return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights ); - } - - /** - * @deprecated since 1.27 Use User::addWatch() - * @return bool - */ - public function addWatch() { - wfDeprecated( __METHOD__, '1.27' ); - $this->user->addWatch( $this->getTitle(), $this->checkRights ); - return true; - } - - /** - * @deprecated since 1.27 Use User::removeWatch() - * @return bool - */ - public function removeWatch() { - wfDeprecated( __METHOD__, '1.27' ); - if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) { - return false; - } - $this->user->removeWatch( $this->getTitle(), $this->checkRights ); - return true; - } - - /** - * @deprecated since 1.27 Use User::isWatched() - * @return bool - */ - public function isWatched() { - wfDeprecated( __METHOD__, '1.27' ); - return $this->user->isWatched( $this->getTitle(), $this->checkRights ); - } - - /** - * @deprecated since 1.27 Use WatchedItemStore::duplicateAllAssociatedEntries() - */ - public static function duplicateEntries( Title $oldTitle, Title $newTitle ) { - wfDeprecated( __METHOD__, '1.27' ); - $store = MediaWikiServices::getInstance()->getWatchedItemStore(); - $store->duplicateAllAssociatedEntries( $oldTitle, $newTitle ); - } - } diff --git a/languages/Language.php b/languages/Language.php index 8d3984dec0..467dc78971 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -3117,18 +3117,18 @@ class Language { */ function getArrow( $direction = 'forwards' ) { switch ( $direction ) { - case 'forwards': - return $this->isRTL() ? '←' : '→'; - case 'backwards': - return $this->isRTL() ? '→' : '←'; - case 'left': - return '←'; - case 'right': - return '→'; - case 'up': - return '↑'; - case 'down': - return '↓'; + case 'forwards': + return $this->isRTL() ? '←' : '→'; + case 'backwards': + return $this->isRTL() ? '→' : '←'; + case 'left': + return '←'; + case 'right': + return '→'; + case 'up': + return '↑'; + case 'down': + return '↓'; } } diff --git a/languages/classes/LanguageGa.php b/languages/classes/LanguageGa.php index 38c50d2eb2..a94343cbba 100644 --- a/languages/classes/LanguageGa.php +++ b/languages/classes/LanguageGa.php @@ -43,30 +43,30 @@ class LanguageGa extends Language { } switch ( $case ) { - case 'ainmlae': - switch ( $word ) { - case 'an Domhnach': - $word = 'Dé Domhnaigh'; - break; - case 'an Luan': - $word = 'Dé Luain'; - break; - case 'an Mháirt': - $word = 'Dé Mháirt'; - break; - case 'an Chéadaoin': - $word = 'Dé Chéadaoin'; - break; - case 'an Déardaoin': - $word = 'Déardaoin'; - break; - case 'an Aoine': - $word = 'Dé hAoine'; - break; - case 'an Satharn': - $word = 'Dé Sathairn'; - break; - } + case 'ainmlae': + switch ( $word ) { + case 'an Domhnach': + $word = 'Dé Domhnaigh'; + break; + case 'an Luan': + $word = 'Dé Luain'; + break; + case 'an Mháirt': + $word = 'Dé Mháirt'; + break; + case 'an Chéadaoin': + $word = 'Dé Chéadaoin'; + break; + case 'an Déardaoin': + $word = 'Déardaoin'; + break; + case 'an Aoine': + $word = 'Dé hAoine'; + break; + case 'an Satharn': + $word = 'Dé Sathairn'; + break; + } } return $word; } diff --git a/languages/classes/LanguageLa.php b/languages/classes/LanguageLa.php index f4082af4ae..8a3a9d2af9 100644 --- a/languages/classes/LanguageLa.php +++ b/languages/classes/LanguageLa.php @@ -47,65 +47,65 @@ class LanguageLa extends Language { } switch ( $case ) { - case 'genitive': - // only a few declensions, and even for those mostly the singular only - $in = [ - '/u[ms]$/', # 2nd declension singular - '/ommunia$/', # 3rd declension neuter plural (partly) - '/a$/', # 1st declension singular - '/libri$/', '/nuntii$/', '/datae$/', # 2nd declension plural (partly) - '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) - '/es$/' # 5th declension singular - ]; - $out = [ - 'i', - 'ommunium', - 'ae', - 'librorum', 'nuntiorum', 'datorum', - 'tionis', 'ntis', 'atis', - 'ei' - ]; - return preg_replace( $in, $out, $word ); - case 'accusative': - // only a few declensions, and even for those mostly the singular only - $in = [ - '/u[ms]$/', # 2nd declension singular - '/a$/', # 1st declension singular - '/ommuniam$/', # 3rd declension neuter plural (partly) - '/libri$/', '/nuntii$/', '/datam$/', # 2nd declension plural (partly) - '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) - '/es$/' # 5th declension singular - ]; - $out = [ - 'um', - 'am', - 'ommunia', - 'libros', 'nuntios', 'data', - 'tionem', 'ntem', 'atem', - 'em' - ]; - return preg_replace( $in, $out, $word ); - case 'ablative': - // only a few declensions, and even for those mostly the singular only - $in = [ - '/u[ms]$/', # 2nd declension singular - '/ommunia$/', # 3rd declension neuter plural (partly) - '/a$/', # 1st declension singular - '/libri$/', '/nuntii$/', '/data$/', # 2nd declension plural (partly) - '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) - '/es$/' # 5th declension singular - ]; - $out = [ - 'o', - 'ommunibus', - 'a', - 'libris', 'nuntiis', 'datis', - 'tione', 'nte', 'ate', - 'e' - ]; - return preg_replace( $in, $out, $word ); - default: - return $word; + case 'genitive': + // only a few declensions, and even for those mostly the singular only + $in = [ + '/u[ms]$/', # 2nd declension singular + '/ommunia$/', # 3rd declension neuter plural (partly) + '/a$/', # 1st declension singular + '/libri$/', '/nuntii$/', '/datae$/', # 2nd declension plural (partly) + '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) + '/es$/' # 5th declension singular + ]; + $out = [ + 'i', + 'ommunium', + 'ae', + 'librorum', 'nuntiorum', 'datorum', + 'tionis', 'ntis', 'atis', + 'ei' + ]; + return preg_replace( $in, $out, $word ); + case 'accusative': + // only a few declensions, and even for those mostly the singular only + $in = [ + '/u[ms]$/', # 2nd declension singular + '/a$/', # 1st declension singular + '/ommuniam$/', # 3rd declension neuter plural (partly) + '/libri$/', '/nuntii$/', '/datam$/', # 2nd declension plural (partly) + '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) + '/es$/' # 5th declension singular + ]; + $out = [ + 'um', + 'am', + 'ommunia', + 'libros', 'nuntios', 'data', + 'tionem', 'ntem', 'atem', + 'em' + ]; + return preg_replace( $in, $out, $word ); + case 'ablative': + // only a few declensions, and even for those mostly the singular only + $in = [ + '/u[ms]$/', # 2nd declension singular + '/ommunia$/', # 3rd declension neuter plural (partly) + '/a$/', # 1st declension singular + '/libri$/', '/nuntii$/', '/data$/', # 2nd declension plural (partly) + '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly) + '/es$/' # 5th declension singular + ]; + $out = [ + 'o', + 'ommunibus', + 'a', + 'libris', 'nuntiis', 'datis', + 'tione', 'nte', 'ate', + 'e' + ]; + return preg_replace( $in, $out, $word ); + default: + return $word; } } } diff --git a/languages/data/ZhConversion.php b/languages/data/ZhConversion.php index 64cce789a5..41bdba43fc 100644 --- a/languages/data/ZhConversion.php +++ b/languages/data/ZhConversion.php @@ -4663,6 +4663,7 @@ public static $zh2Hant = [ '大历' => '大曆', '大本钟' => '大本鐘', '大历史' => '大歷史', +'大历险' => '大歷險', '大病初愈' => '大病初癒', '大目干连' => '大目乾連', '大笨钟' => '大笨鐘', @@ -6805,6 +6806,8 @@ public static $zh2Hant = [ '碧河里' => '碧河里', '碰钟' => '碰鐘', '确系' => '確係', +'码码表' => '碼碼表', +'码表示' => '碼表示', '码表' => '碼錶', '磁制' => '磁製', '磨蝎' => '磨蝎', @@ -8909,7 +8912,6 @@ public static $zh2Hant = [ '发型' => '髮型', '发夹' => '髮夾', '发妻' => '髮妻', -'发姐' => '髮姐', '发屋' => '髮屋', '发已霜白' => '髮已霜白', '发带' => '髮帶', @@ -14051,6 +14053,8 @@ public static $zh2TW = [ '數碼訊號' => '數位訊號', '数字电视' => '數位電視', '數碼電視' => '數位電視', +'数字音乐' => '數位音樂', +'數碼音樂' => '數位音樂', '調制解調器' => '數據機', '调制解调器' => '數據機', '斯堪的納維亞' => '斯堪地那維亞', @@ -14184,6 +14188,7 @@ public static $zh2TW = [ '百慕大' => '百慕達', '卢旺达' => '盧安達', '盧旺達' => '盧安達', +'真人騷' => '真人秀', '睾' => '睪', '知识产权局' => '知識產權局', '知識產權局' => '知識產權署', @@ -16416,6 +16421,7 @@ public static $zh2HK = [ '看著者' => '看著者', '看著述' => '看著述', '看著錄' => '看著錄', +'真人秀' => '真人騷', '眼眶里' => '眼眶裏', '眼睛里' => '眼睛裏', '眼里' => '眼裏', @@ -19273,6 +19279,7 @@ public static $zh2CN = [ '看著稱' => '看著称', '看著者' => '看著者', '看著述' => '看著述', +'真人騷' => '真人秀', '著業' => '着业', '著絲' => '着丝', '著麼' => '着么', diff --git a/languages/i18n/af.json b/languages/i18n/af.json index b28deeb2af..7751439169 100644 --- a/languages/i18n/af.json +++ b/languages/i18n/af.json @@ -570,7 +570,7 @@ "newarticle": "(Nuut)", "newarticletext": "Hierdie bladsy bestaan nie.\nTik iets in die invoerboks hier onder om 'n nuwe bladsy te skep. Meer inligting is op die [$1 hulpbladsy] beskikbaar.\nAs u per ongeluk hier uitgekom het, gebruik u blaaier se '''terug'''-knoppie.", "anontalkpagetext": "----\n<em>Hierdie is die besprekingsblad vir 'n anonieme gebruiker wat nog nie 'n rekening geskep het nie, of wat dit nie gebruik nie.</em>\nDaarom moet ons sy/haar numeriese IP-adres vir identifikasie gebruik.\nSó 'n adres kan deur verskeie gebruikers gedeel word.\nIndien u 'n anonieme gebruiker is wat voel dat ontoepaslike kommentaar teen u gerig is, [[Special:CreateAccount|skep gerus 'n rekening]] of [[Special:UserLogin|meld aan]] om verwarring met ander anonieme gebruikers te voorkom.", - "noarticletext": "Hierdie bladsy bevat geen teks nie.\nU kan [[Special:Search/{{PAGENAME}}|vir die bladsytitel in ander bladsye soek]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} die verwante logboeke deursoek]\nof [{{fullurl:{{FULLPAGENAME}}|action=edit}} hierdie bladsy wysig]</span>.", + "noarticletext": "Hierdie bladsy bevat geen teks nie.\nU kan [[Special:Search/{{PAGENAME}}|vir die bladsytitel in ander bladsye soek]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} die verwante logboeke deursoek]\nof [{{fullurl:{{FULLPAGENAME}}|action=edit}} hierdie bladsy skep]</span>.", "noarticletext-nopermission": "Hierdie bladsy bevat geen teks nie.\nU kan vir die term [[Special:Search/{{PAGENAME}}|in ander bladsye soek]] of\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} die verwante logboeke deursoek]</span>, maar u kan nie die bladsy skep nie.", "missing-revision": "Die weergawe #$1 van die bladsy \"{{FULLPAGENAME}} bestaan nie.\n\nDit word meestal veroorsaak deur die volg van 'n verouderde verwysing na 'n bladsy wat verwyder is.\nMeer gegewens kan moontlik in die [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} skraplogboek] gevind word.", "userpage-userdoesnotexist": "U is besig om 'n gebruikersblad wat nie bestaan nie te wysig (gebruiker \"<nowiki>$1</nowiki>\"). Maak asseblief seker of u die bladsy wil skep/ wysig.", @@ -1116,13 +1116,15 @@ "rcfilters-activefilters": "Aktiewe filters", "rcfilters-advancedfilters": "Gevorderde filters", "rcfilters-limit-title": "Wysigings om te wys", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|wysiging|$1 wysigings}}, $2", + "rcfilters-date-popup-title": "Tydperk om te deursoek", "rcfilters-days-title": "Afgelope dae", "rcfilters-hours-title": "Afgelope ure", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dae}}", "rcfilters-days-show-hours": "$1 {{PLURAL:$1|uur|ure}}", "rcfilters-highlighted-filters-list": "Bekleurklem: $1", "rcfilters-quickfilters": "Gestoorde filters", - "rcfilters-quickfilters-placeholder-title": "Geen gestoorde skakels", + "rcfilters-quickfilters-placeholder-title": "Geen gestoorde filters", "rcfilters-quickfilters-placeholder-description": "Om instellings te stoor en later weer te gebruik, kliek op die bladwyser-piktogram in die Aktiewe Filter-gebied onder.", "rcfilters-savedqueries-defaultlabel": "Gestoorde filters", "rcfilters-savedqueries-rename": "Hernoem", @@ -1138,7 +1140,7 @@ "rcfilters-restore-default-filters": "Stel filters terug", "rcfilters-clear-all-filters": "Verwyder alle filters", "rcfilters-show-new-changes": "Wys nuutste wysigings", - "rcfilters-search-placeholder": "Filter onlangse wysigings (blaai of begin tik)", + "rcfilters-search-placeholder": "Filter wysigings (blaai of begin tik)", "rcfilters-invalid-filter": "Ongeldig filter", "rcfilters-empty-filter": "Geen aktiewe filters. Alle wysigings word gewys.", "rcfilters-filterlist-title": "Filters", @@ -1188,6 +1190,9 @@ "rcfilters-filter-watchlist-notwatched-label": "Nie in dophoulys", "rcfilters-filter-watchlist-notwatched-description": "Alles behalwe wysigings aan bladsye op u dophoulys.", "rcfilters-filtergroup-watchlistactivity": "Dophoulys-bedrywighede", + "rcfilters-filter-watchlistactivity-unseen-label": "Nie-besigtigde wysigings", + "rcfilters-filter-watchlistactivity-unseen-description": "Wysigings aan blaaie wat u nog nie sedert die wysiging besoek het nie.", + "rcfilters-filter-watchlistactivity-seen-description": "Wysigings aan blaaie wat u reeds sedert die wysiging besoek het.", "rcfilters-filtergroup-changetype": "Soort wysiging", "rcfilters-filter-pageedits-label": "Bladsywysigings", "rcfilters-filter-pageedits-description": "Wysigings aan wiki-inhoud, besprekings en kategoriebeskrywings…", @@ -1208,6 +1213,8 @@ "rcfilters-view-tags": "Geëtiketteerde wysigings", "rcfilters-view-namespaces-tooltip": "Filtreer resultate volgens naamruimte", "rcfilters-view-tags-tooltip": "Filter resultate volgens wysigingsetikette", + "rcfilters-liveupdates-button": "Monitor bywerkings", + "rcfilters-liveupdates-button-title-off": "Wys nuwe wysigings soos hulle inrol.", "rcfilters-preference-label": "Versteek die verbeter weergawe van 'Onlangse wysigings'", "rcnotefrom": "{{PLURAL:$5|Wysiging|Wysigings}} sedert <strong>$3 om $4</strong> (maksimum van <strong>$1</strong> word gewys).", "rclistfrom": "Vertoon wysigings vanaf $3 $2", diff --git a/languages/i18n/ar.json b/languages/i18n/ar.json index 76558ca9cc..3ec6f9524e 100644 --- a/languages/i18n/ar.json +++ b/languages/i18n/ar.json @@ -583,11 +583,11 @@ "botpasswords-insert-failed": "فشل في إضافة اسم البوت \"$1\".هل أضيف بالفعل؟", "botpasswords-update-failed": "فشل في تحديث اسم بوت \"$1\". هل تم حذفه؟", "botpasswords-created-title": "تم إنشاء كلمة سر بوت", - "botpasswords-created-body": "تم إنشاء كلمة سر بوت \"$1\" للمستخدم \"$2\".", + "botpasswords-created-body": "كلمة سر البوت للبوت \"$1\" الخاص {{GENDER:$2|بالمستخدم|بالمستخدمة}} \"$2\" تم إنشاؤها.", "botpasswords-updated-title": "تم تحديث كلمة سر البوت", - "botpasswords-updated-body": "كلمة سر البوت \"$1\" للمستخدم \"$2\" تم تحديثها.", + "botpasswords-updated-body": "كلمة سر البوت \"$1\" {{GENDER:$2|للمستخدم|للمستخدمة}} \"$2\" تم تحديثها.", "botpasswords-deleted-title": "كلمة سر البوت حذفت", - "botpasswords-deleted-body": "كلمة سر البوت \"$1\" لمستخدم \"$2\" قد حذفت.", + "botpasswords-deleted-body": "كلمة سر البوت \"$1\" {{GENDER:$2|للمستخدم|للمستخدمة}} \"$2\" قد حذفت.", "botpasswords-newpassword": "كلمة السر الجديدة لتسجيل الدخول ب <strong>$1</strong> هي <strong>$2</strong>. <em>من فضلك سجل هذه كمرجع في المستقبل .</em><br> (للبوتات القديمة التي تتطلب أن يكون اسم تسجيل الدخول مثل اسم المستخدم النهائي، يمكنك أيضا استخدام <strong>$3</strong> كاسم مستخدم و <strong>$4</strong> ككلمة سر.)", "botpasswords-no-provider": "BotPasswordsSessionProvider غير متاح.", "botpasswords-restriction-failed": "قيود كلمة مرور البوت تمنع هذا الولوج.", @@ -1049,7 +1049,7 @@ "recentchangesdays-max": "الحد الأقصى {{PLURAL:$1|أقل من يوم|يوم واحد|يومان|$1 أيام|$1 يوما|$1 يوم}}", "recentchangescount": "عدد التعديلات الظاهرة مبدئيا:", "prefs-help-recentchangescount": "بما في ذلك أحدث التغييرات وتاريخ الصفحات والسجلات.", - "prefs-help-watchlist-token2": "هذا هو المفتاح السري لتغذية الويب لقائمة مراقبتك.\nيمكن لأي شخص يعرفه أن يقرأ قائمة مراقبتك، ولذا لا تتشاركه مع أحد. [[Special:ResetTokens|انقر هنا إذا أردت إعادة ضبطه]].", + "prefs-help-watchlist-token2": "هذا هو المفتاح السري لتغذية الويب لقائمة مراقبتك.\nيمكن لأي شخص يعرفه أن يقرأ قائمة مراقبتك، ولذا لا تتشاركه مع أحد.\nإذا احتجت، [[Special:ResetTokens|يمكنك إعادة ضبطه]].", "savedprefs": "تم حفظ تفضيلاتك.", "savedrights": "حُفظت المجموعات الجديدة {{GENDER:$1|للمستخدم|للمستخدمة}} $1.", "timezonelegend": "المنطقة الزمنية:", @@ -1069,6 +1069,7 @@ "timezoneregion-indian": "المحيط الهندي", "timezoneregion-pacific": "المحيط الهادي", "allowemail": "اسمح للمستخدمين الآخرين بإرسال بريد إلكتروني إلي", + "email-allow-new-users-label": "اسمح بالبريد الإلكتروني من المستخدمين الجدد تمامًا", "email-blacklist-label": "امنع هؤلاء المستخدمين من إرسال بريد إلكتروني لي:", "prefs-searchoptions": "البحث", "prefs-namespaces": "أسماء النطاقات", @@ -1238,6 +1239,7 @@ "right-siteadmin": "غلق ورفع غلق قاعدة البيانات", "right-override-export-depth": "تصدير الصفحات متضمنة الصفحات الموصولة حتى عمق 5", "right-sendemail": "إرسال رسائل بريد إلكتروني إلى مستخدمين آخرين", + "right-sendemail-new-users": "إرسال رسالة بريد إلكتروني للمستخدمين الذين ليس لديهم أفعال في السجلات", "right-managechangetags": "إنشاء وتعطيل [[Special:Tags|الوسوم]]", "right-applychangetags": "تطبيق [[Special:Tags|الوسوم]] مع التغييرات التي أجريتها.", "right-changetags": "إضافة وإزالة [[Special:Tags|وسوم]] في مراجعات ومدخلات سجل فردية", @@ -1339,6 +1341,7 @@ "recentchanges-noresult": "لا توجد تغييرات خلال الفترة المحددة تطابق هذه المعايير.", "recentchanges-timeout": "البحث انتهى بدون نتائج. ربما تحب تجربة مدخلات بحث مختلفة.", "recentchanges-network": "نتيجة لخطأ تقني، لم يمكن تحميل أي نتائج. من فضلك حاول إعادة تحديث الصفحة.", + "recentchanges-notargetpage": "أدخل اسم صفحة بالأعلى لرؤية التغييرات المرتبطة بهذه الصفحة.", "recentchanges-feed-description": "تابع أحدث التغييرات للويكي عبر هذه التلقيمة.", "recentchanges-label-newpage": "أنشأ هذا التعديل صفحة جديدة", "recentchanges-label-minor": "هذا تعديل طفيف", @@ -1470,8 +1473,11 @@ "rcfilters-watchlist-showupdated": "التغييرات للصفحات التي لم تزرها منذ حدوث التغييرات تكون <strong>بالخط العريض</strong>، مع علامات صلبة.", "rcfilters-preference-label": "أخف النسخة المحسنة من أحدث التغييرات", "rcfilters-preference-help": "يسترجع عملية إعادة تصميم الواجهة لعام 2017 وكل الأدوات التي أضيفت منذ ذلك الوقت.", - "rcfilters-filter-showlinkedto-label": "أظهر التغييرات في الصفحات الموصولة بصفحة:", - "rcfilters-target-page-placeholder": "اختر صفحة", + "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": "بالأسفل {{PLURAL:$5|التغيير|التغييرات}} منذ <strong>$2</strong> (إلى <strong>$1</strong> معروضة).", "rclistfromreset": "إعادة ضبط خيار التاريخ", "rclistfrom": "أظهر التغييرات بدء من $3 $2", @@ -1516,7 +1522,7 @@ "recentchangeslinked-feed": "تغييرات ذات علاقة", "recentchangeslinked-toolbox": "تغييرات ذات علاقة", "recentchangeslinked-title": "التغييرات المرتبطة بصفحة «$1»", - "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثاً للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''", + "recentchangeslinked-summary": "أدخل اسم صفحة لرؤية التغييرات في الصفحات الموصولة من أو إلى تلك الصفحة. (لرؤية أعضاء تصنيف، أدخل تصنيف:الاسم الخاص بالتصنيف). التغييرات في الصفحات في [[Special:Watchlist|قائمة مراقبتك]] <strong>عريضة</strong>.", "recentchangeslinked-page": "اسم الصفحة:", "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك", "recentchanges-page-added-to-category": "[[:$1]] أضيفت إلى التصنيف", @@ -3619,6 +3625,8 @@ "tag-mw-replace-description": "التعديلات التي أزالت أكثر من 90% من محتوى صفحة", "tag-mw-rollback": "استرجاع", "tag-mw-rollback-description": "التعديلات التي استرجعت التعديلات السابقة باستخدام وصلة الاسترجاع", + "tag-mw-undo": "رجوع", + "tag-mw-undo-description": "التعديلات التي ترجع عن التعديلات السابقة باستخدام وصلة رجوع", "tags-title": "وسوم", "tags-intro": "هذه الصفحة تعرض الوسوم التي ربما يعلم البرنامج تعديلا بها، ومعانيها.", "tags-tag": "اسم الوسم", diff --git a/languages/i18n/ast.json b/languages/i18n/ast.json index 64af5da272..6d2ed73c15 100644 --- a/languages/i18n/ast.json +++ b/languages/i18n/ast.json @@ -525,11 +525,11 @@ "botpasswords-insert-failed": "Nun pudo amestase'l nome de bot «$1». ¿Taba añadíu yá?", "botpasswords-update-failed": "Nun pudo anovase'l nome de bot «$1». ¿Desaniciaríase?", "botpasswords-created-title": "Creóse la contraseña de bot", - "botpasswords-created-body": "Creóse la contraseña del bot llamáu «$1» del usuariu «$2».", + "botpasswords-created-body": "Creóse la contraseña del bot llamáu «$1» {{GENDER:$2|del usuariu|de la usuaria}} «$2».", "botpasswords-updated-title": "Anovóse la contraseña de bot", - "botpasswords-updated-body": "Anovóse la contraseña del bot llamáu «$1» del usuariu «$2».", + "botpasswords-updated-body": "Anovóse la contraseña del bot llamáu «$1» {{GENDER:$2|del usuariu|de la usuaria}} «$2».", "botpasswords-deleted-title": "Desanicióse la contraseña de bot", - "botpasswords-deleted-body": "Desanicióse la contraseña del bot llamáu «$1» del usuariu «$2».", + "botpasswords-deleted-body": "Desanicióse la contraseña del bot llamáu «$1» {{GENDER:$2|del usuariu|de la usuaria}} «$2».", "botpasswords-newpassword": "La nueva contraseña p'aniciar sesión con <strong>$1</strong> ye <strong>$2</strong>. <em>Por favor, rexistra esto pa referencies futures.</em> <br> (Pa los bots antiguos que necesiten que'l nome d'aniciu de sesión sía'l mesmu que'l nome d'usuariu, tamién pue usase <strong>$3</strong> como nome d'usuariu y <strong>$4</strong> como contraseña.)", "botpasswords-no-provider": "BotPasswordsSessionProvider nun ta disponible.", "botpasswords-restriction-failed": "Hai torgues de contraseña de bot que torgaron esti aniciu de sesión.", @@ -993,7 +993,7 @@ "recentchangesdays-max": "Máximo $1 {{PLURAL:$1|día|díes}}", "recentchangescount": "Númberu d'ediciones p'amosar de mou predetermináu:", "prefs-help-recentchangescount": "Incluye los cambios recientes, los historiales de páxines y los rexistros.", - "prefs-help-watchlist-token2": "Esta ye la clave secreta pa la canal de noticies web de la so llista de vixilancia.\nCualquiera que la sepa podrá lleer la so llista de vixilancia; nun la comparta.\n[[Special:ResetTokens|Calque equí si necesita reaniciala]].", + "prefs-help-watchlist-token2": "Esta ye la clave secreta pa la canal de noticies web de la to llista de vixilancia.\nCualquiera que la sepa podrá lleer la to llista de vixilancia; nun la compartas.\nSi lo necesites [[Special:ResetTokens|puedes reaniciala]].", "savedprefs": "Guardáronse les preferencies.", "savedrights": "Guardáronse los grupos {{GENDER:$1|del usuariu|de la usuaria}} $1.", "timezonelegend": "Estaya horaria:", @@ -1013,6 +1013,7 @@ "timezoneregion-indian": "Océanu Índicu", "timezoneregion-pacific": "Océanu Pacíficu", "allowemail": "Permitir qu'otros usuarios m'unvien correos", + "email-allow-new-users-label": "Permitir los correos de los usuarios nuevos", "email-blacklist-label": "Torgar qu'estos usuarios m'unvien correos electrónicos:", "prefs-searchoptions": "Buscar", "prefs-namespaces": "Espacios de nome", @@ -1182,6 +1183,7 @@ "right-siteadmin": "Candar y descandar la base de datos", "right-override-export-depth": "Esportar páxines, incluyendo páxines enllazaes fasta una fondura de 5", "right-sendemail": "Unviar corréu a otros usuarios", + "right-sendemail-new-users": "Unviar corréu electrónicu a usuarios ensin aiciones rexistraes", "right-managechangetags": "Crear y (des)activar [[Special:Tags|etiquetes]]", "right-applychangetags": "Aplicar [[Special:Tags|etiquetes]] xunto colos cambios propios", "right-changetags": "Amestar y desaniciar [[Special:Tags|etiquetes]] arbitraries en revisiones individuales y entraes del rexistru", @@ -1283,6 +1285,7 @@ "recentchanges-noresult": "Nengún cambiu nel periodu conseñáu coincide con esos criterios.", "recentchanges-timeout": "Esta gueta escosó'l tiempu. Escurque quieras tentar con parámetros de gueta distintos.", "recentchanges-network": "Nun se cargó nenguna resultancia por cuenta d'un problema técnicu. Tenta volver a cargar la páxina.", + "recentchanges-notargetpage": "Escribe'l nome d'una páxina más arriba pa ver los cambeos rellacionaos con esa páxina.", "recentchanges-feed-description": "Sigui nesta canal los últimos cambios de la wiki.", "recentchanges-label-newpage": "Esta edición creó una páxina nueva", "recentchanges-label-minor": "Esta ye una edición menor", @@ -1415,6 +1418,11 @@ "rcfilters-watchlist-showupdated": "Los cambeos fechos en páxines que nun visitasti desque se ficieron apaecen en <strong>negrina</strong>, con marcadores sólidos.", "rcfilters-preference-label": "Tapecer la versión meyorada de Cambios recién", "rcfilters-preference-help": "Revierte'l rediseñu de la interfaz de 2017 y toles ferramientes añadíes d'entós aquí.", + "rcfilters-filter-showlinkedfrom-label": "Amosar los cambios nes páxines enllazaes dende", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páxines enllazaes dende</strong> la páxina seleicionada", + "rcfilters-filter-showlinkedto-label": "Amosar los cambios nes páxines qu'enllacen a", + "rcfilters-filter-showlinkedto-option-label": "<strong>Páxines qu'enllacen a</strong> la páxina seleicionada", + "rcfilters-target-page-placeholder": "Escribe'l nome de la páxina", "rcnotefrom": "Abaxo {{PLURAL:$5|tá'l cambiu|tan los cambios}} dende'l <strong>$3</strong>, a les <strong>$4</strong> (s'amuesen un máximu de <strong>$1</strong>).", "rclistfromreset": "Reaniciar la seleición de data", "rclistfrom": "Amosar los nuevos cambios dende'l $3 a les $2", @@ -1459,7 +1467,7 @@ "recentchangeslinked-feed": "Cambios rellacionaos", "recentchangeslinked-toolbox": "Cambios rellacionaos", "recentchangeslinked-title": "Cambios rellacionaos con \"$1\"", - "recentchangeslinked-summary": "Esta ye una llista de los caberos cambios fechos nes páxines enllaciaes dende una páxina determinada (o nos miembros d'una categoría determinada).\nLes páxines de [[Special:Watchlist|la to llista de siguimientu]] tán en <strong>negrina</strong>.", + "recentchangeslinked-summary": "Esscribe'l nome d'una páxina pa ver los cambios nes páxines enllazaes a o dende esa páxina. (Pa ver los miembros d'una categoría, escribe Categoría:Nome de la categoría). Los cambios nes páxines de [[Special:Watchlist|la to llista de siguimientu]] tán en <strong>negrina</strong>.", "recentchangeslinked-page": "Nome de la páxina:", "recentchangeslinked-to": "Amosar los cambios de les páxines qu'enllacen en cuenta de los de la páxina dada", "recentchanges-page-added-to-category": "[[:$1]] amestóse a la categoría", diff --git a/languages/i18n/atj.json b/languages/i18n/atj.json index b915fafec9..578221ca68 100644 --- a/languages/i18n/atj.json +++ b/languages/i18n/atj.json @@ -264,7 +264,7 @@ "noarticletext": "Mekwatc nama takon atisokesinihikan nta paskickwemakanik.\nKe ki totaman [[Special:Search/{{PAGENAME}}|nantowapaha ohwe icinikatamowin]] kotakahikw paskickwemakanik, \n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|paskickwemakan={{FULLPAGENAMEE}}}} nantowapata ka ici mikowapihikemikak]\nkekotc [{{fullurl:{{FULLPAGENAME}}|tota=orocowata}} ocita paskickwemakan]</span>.", "noarticletext-nopermission": "Mekwatc nama takon atisokesinihikan nta paskickwemakanik.\nKe ki totaman[[Special:Search/{{PAGENAME}}|nantowapaha ohwe icinikatamowin ]] kotakahikw paskickwemakanik,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|paskickwemakan={{FULLPAGENAMEE}}}} nantowapaha kotakihi wapatcikaniwoca ohwe ]</span>,nama aric ki mirikwin ohwe kata ocitain paskickwemakan.", "editing": "Meckotcita $1", - "creating": "$1 Wir tipirowe ka ki ocitatc", + "creating": "\"$1\" mekwatc ka orisinahak", "editingsection": "Ka ki meckotcitatc $1 (tipanitakiniwok)", "templatesused": "{{PLURAL:$1|tapapitcikan ka apatak |tapapitcikan ka apatak}} nta paskickwemakanik:", "template-protected": "(nakataweritcikatew)", diff --git a/languages/i18n/az.json b/languages/i18n/az.json index 379c774a8d..1a46a3fd1d 100644 --- a/languages/i18n/az.json +++ b/languages/i18n/az.json @@ -1016,6 +1016,8 @@ "rcfilters-activefilters": "Aktiv filtrlər", "rcfilters-advancedfilters": "Geniş filtr", "rcfilters-limit-title": "Göstərilməli dəyişikliklər", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|redaktə|redaktə}}, $2", + "rcfilters-date-popup-title": "Axtarış üçün vaxt aralığı", "rcfilters-days-title": "Son günlər", "rcfilters-hours-title": "Son saatlar", "rcfilters-days-show-days": "$1 {{PLURAL:$1|gün|gün}}", diff --git a/languages/i18n/be-tarask.json b/languages/i18n/be-tarask.json index 05560b45b5..2a89558a42 100644 --- a/languages/i18n/be-tarask.json +++ b/languages/i18n/be-tarask.json @@ -1013,6 +1013,7 @@ "timezoneregion-indian": "Індыйскі акіян", "timezoneregion-pacific": "Ціхі акіян", "allowemail": "Дазволіць іншым удзельнікам і ўдзельніцам дасылаць мне лісты электроннай поштай", + "email-allow-new-users-label": "Дазволіць лісты электроннай пошты ад зусім новых удзельнікаў", "email-blacklist-label": "Забараніць гэтым удзельнікам дасылаць мне лісты электроннай поштай:", "prefs-searchoptions": "Пошук", "prefs-namespaces": "Прасторы назваў", @@ -1031,7 +1032,7 @@ "yourrealname": "Сапраўднае імя:", "yourlanguage": "Мова інтэрфэйсу:", "yourvariant": "Варыянт мовы зьместу:", - "prefs-help-variant": "Выбраны Вамі альфабэт ці артаграфія для паказу старонак зьместу {{GRAMMAR:родны|{{SITENAME}}}}.", + "prefs-help-variant": "Пераважны вамі варыянт ці артаграфія для паказу старонак зьместу гэтай вікі.", "yournick": "Новы подпіс:", "prefs-help-signature": "Камэнтары на старонках абмеркаваньняў павінны быць падпісаныя з дапамогай сымбаляў «<nowiki>~~~~</nowiki>», якія будуць ператвораныя ў ваш подпіс і актуальны час.", "badsig": "Няслушны неапрацаваны подпіс.\nПраверце HTML-тэгі.", @@ -1040,11 +1041,11 @@ "gender-unknown": "Калі вы будзеце згадвацца, праграмнае забесьпячэньне будзе кожны раз пры магчымасьці ўжываць гендэрна нэўтральныя словы", "gender-male": "Ён рэдагуе вікістаронкі", "gender-female": "Яна рэдагуе вікістаронкі", - "prefs-help-gender": "Вызначаць гэта неабавязкова.\nАпраграмаваньне выкарыстоўвае гэтае значэньне толькі для граматычна карэктнага звароту да вас.\nГэтая інфармацыя будзе агульнадаступнай.", + "prefs-help-gender": "Вызначаць гэта неабавязкова.\nПраграмнае забесьпячэньне выкарыстоўвае гэтае значэньне толькі для граматычна карэктнага звароту да вас.\nГэтая інфармацыя будзе агульнадаступнай.", "email": "Электронная пошта", "prefs-help-realname": "Сапраўднае імя паведамляць неабавязковае.\nКалі Вы яго пазначыце, яно можа быць выкарыстанае для пазначэньня Вашай працы.", - "prefs-help-email": "Адрас электроннай пошты неабавязковы, але ён дае магчымасьць даслаць Вам пароль, калі Вы забылі яго.", - "prefs-help-email-others": "Вы можаце таксама дазволіць іншым удзельнікам кантактаваць з Вамі праз Вашую асабістую старонку гутарак безь неабходнасьці раскрыцьця адрасу электроннай пошты.", + "prefs-help-email": "Адрас электроннай пошты неабавязковы, але ён неабходны для скіданьня паролю, калі вы забудзеце яго.", + "prefs-help-email-others": "Вы можаце таксама дазволіць іншым удзельнікам кантактаваць з вамі праз электронную пошту па спасылцы на вашай старонцы ці старонцы гутарак.\nВаш адрас электроннай пошты ня будзе паказаны іншым удзельнікам пры кантактаваньні.", "prefs-help-email-required": "Патрабуецца адрас электроннай пошты.", "prefs-info": "Асноўныя зьвесткі", "prefs-i18n": "Інтэрнацыяналізацыя", @@ -1182,6 +1183,7 @@ "right-siteadmin": "блякаваньне і разблякаваньне базы зьвестак", "right-override-export-depth": "экспартаваньне старонак, уключаючы зьвязаныя старонкі з глыбінёй да 5", "right-sendemail": "адпраўка электронных лістоў іншым удзельнікам", + "right-sendemail-new-users": "Дасылаць лісты электроннай пошты ўдзельнікам без зафіксаваных дзеяньняў", "right-managechangetags": "стварэньне і (дэ)актывацыя [[Special:Tags|метак]]", "right-applychangetags": "дадаваць [[Special:Tags|меткі]] пры рэдагаваньні", "right-changetags": "дадаваць і выдаляць адвольныя [[Special:Tags|меткі]] да асобных вэрсіяў і запісаў у журнале падзеяў", @@ -1283,6 +1285,7 @@ "recentchanges-noresult": "Зьмены, што пасуюць дадзенаму пэрыяду і крытэрам, ня знойдзеныя.", "recentchanges-timeout": "Адведзены на гэты пошук час скончыўся. Вы можаце паспрабаваць іншыя парамэтры пошуку.", "recentchanges-network": "Праз тэхнічную памылку вынікі ня могуць быць загружаныя. Калі ласка, паспрабуйце абнавіць старонку.", + "recentchanges-notargetpage": "Увядзіце вышэй назву старонкі, каб пабачыць зьмены, зьвязаныя з гэтай старонкай.", "recentchanges-feed-description": "Сачыце за апошнімі зьменамі ў вікі праз гэтую стужку.", "recentchanges-label-newpage": "Гэтым рэдагаваньнем была створаная новая старонка", "recentchanges-label-minor": "Гэта дробнае рэдагаваньне", @@ -1414,7 +1417,11 @@ "rcfilters-watchlist-showupdated": "Зьмены старонак, якія вы не наведвалі пасьля гэтых зьменаў, пазначаныя <strong>тоўстым</strong> з адпаведнымі пазнакамі.", "rcfilters-preference-label": "Схаваць палепшаную вэрсію апошніх зьменаў", "rcfilters-preference-help": "Адкатвае рэдызайн інтэрфэйсу 2017 году і ўсе інструмэнты, дададзеныя з таго часу.", - "rcfilters-filter-showlinkedfrom-label": "Паказаць зьмены на старонках, на якія спасылаецца:", + "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": "Ніжэй {{PLURAL:$5|знаходзіцца зьмена|знаходзяцца зьмены}} з <strong>$4 $3</strong> (да <strong>$1</strong> на старонку).", "rclistfromreset": "Скінуць выбар даты", "rclistfrom": "Паказаць зьмены з $2 $3", @@ -1459,7 +1466,7 @@ "recentchangeslinked-feed": "Зьвязаныя праўкі", "recentchangeslinked-toolbox": "Зьвязаныя праўкі", "recentchangeslinked-title": "Зьвязаныя праўкі для «$1»", - "recentchangeslinked-summary": "Гэта сьпіс апошніх зьменаў старонак, на якія спасылаецца азначаная старонка (ці ўсіх старонак, якія належаць азначанай катэгорыі).\nСтаронкі з [[Special:Watchlist|Вашага сьпісу назіраньня]] пазначаныя <strong>тоўстым шрыфтам</strong>.", + "recentchangeslinked-summary": "Увядзіце назву старонкі, каб пабачыць зьмены старонак, на якія яна спасылаецца або якія спасылаюцца на яе (каб пабачыць старонкі з катэгорыі, увядзіце Катэгорыя:Назва). Зьмены старонак з [[Special:Watchlist|вашага сьпісу назіраньня]] пазначаныя <strong>тоўстым шрыфтам</strong>.", "recentchangeslinked-page": "Назва старонкі:", "recentchangeslinked-to": "Замест гэтага паказваць зьмены на старонках, што спасылаюцца на гэтую старонку", "recentchanges-page-added-to-category": "[[:$1]] дададзеная да катэгорыі", @@ -1667,6 +1674,9 @@ "uploadstash-file-too-large": "Немагчыма апрацаваць файл памерам большым за $1 байтаў.", "uploadstash-not-logged-in": "Удзельнік не ўвайшоў у сыстэму, файлы мусяць належаць удзельнікам.", "uploadstash-wrong-owner": "Гэты файл ($1) не належыць цяперашняму ўдзельніку.", + "uploadstash-no-such-key": "Няма такога ключа ($1), немагчыма выдаліць.", + "uploadstash-no-extension": "Пустое пашырэньне.", + "uploadstash-zero-length": "Файл мае нулявую даўжыню.", "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.", @@ -2615,6 +2625,8 @@ "import-mapping-namespace": "Імпарт у прастору назваў:", "import-mapping-subpage": "Імпарт у якасьці падстаронак наступнай старонкі:", "import-upload-filename": "Назва файла:", + "import-upload-username-prefix": "Прэфікс інтэрвікі:", + "import-assign-known-users": "Прызначаць праўкі лякальным удзельнікам, калі ўдзельнік з такім імем існуе лякальна", "import-comment": "Камэнтар:", "importtext": "Калі ласка, экспартуйце файл з крынічнай вікі з дапамогай [[Special:Export|прылады экспарту]].\nЗахавайце яго на свой кампутар, а потым загрузіце сюды.", "importstart": "Імпартаваньне старонак…", @@ -2623,6 +2635,7 @@ "imported-log-entries": "{{PLURAL:$1|Імпартаваны $1 запіс журнала|Імпартаваныя $1 запісы журнала|Імпартаваныя $1 запісаў журнала}}.", "importfailed": "Немагчыма імпартаваць: $1", "importunknownsource": "Невядомы тып крыніцы імпарту", + "importnoprefix": "Не пададзены прэфікс інтэрвікі", "importcantopen": "Немагчыма адкрыць файл імпарту", "importbadinterwiki": "Няслушная спасылка на іншую моўную вэрсію", "importsuccess": "Імпартаваньне скончанае!", @@ -3296,6 +3309,8 @@ "autosumm-blank": "Выдалены ўвесь зьмест старонкі", "autosumm-replace": "Старонка замененая на '$1'", "autoredircomment": "Перанакіроўвае на [[$1]]", + "autosumm-removed-redirect": "Выдаленае перанакіраваньне на [[$1]]", + "autosumm-changed-redirect-target": "Перанакіраваньне зьмененае з [[$1]] на [[$2]]", "autosumm-new": "Створана старонка са зьместам '$1'", "autosumm-newblank": "Створаная пустая старонка", "size-bytes": "$1 б", @@ -3433,6 +3448,10 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Метка|Меткі}}]]: $2)", "tag-mw-contentmodelchange": "зьмена мадэлі зьместу", "tag-mw-contentmodelchange-description": "Рэдагаваньні, якія [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel зьмяняюць мадэль зьместу] старонкі", + "tag-mw-new-redirect": "Новае перанакіраваньне", + "tag-mw-new-redirect-description": "Рэдагаваньні, якія ствараюць новае перанакіраваньне ці зьмяняюць старонку на перанакіраваньне", + "tag-mw-removed-redirect": "Выдаленае перанакіраваньне", + "tag-mw-removed-redirect-description": "Праўкі, якія зьмяняюць існае перанакіраваньне на не перанакіраваньне", "tags-title": "Меткі", "tags-intro": "На гэтай старонцы знаходзіцца сьпіс метак, якімі праграмнае забесьпячэньне можа пазначыць рэдагаваньне, і іх значэньне.", "tags-tag": "Назва меткі", diff --git a/languages/i18n/be.json b/languages/i18n/be.json index 7e4665b84e..6dac67ef6b 100644 --- a/languages/i18n/be.json +++ b/languages/i18n/be.json @@ -451,7 +451,7 @@ "createacct-benefit-heading": "{{SITENAME}} зроблены такімі ж людзьмі, як вы.", "createacct-benefit-body1": "{{PLURAL:$1|праўка|праўкі|правак}}", "createacct-benefit-body2": "{{PLURAL:$1|старонка|старонкі|старонак}}", - "createacct-benefit-body3": "{{PLURAL:$1|апошні \nўкладальнік|апошнія \nўкладальнікі|апошніх \nукладальнікаў}}", + "createacct-benefit-body3": "{{PLURAL:$1|апошні ўкладальнік|апошнія ўкладальнікі|апошніх укладальнікаў}}", "badretype": "Уведзеныя паролі не аднолькавыя.", "usernameinprogress": "Стварэнне рахунку для гэтага імені ўдзельніка ўжо ў працэсе.\nКалі ласка, пачакайце.", "userexists": "Такое імя ўдзельніка ўжо занятае.\nКалі ласка, выбярыце іншае імя.", diff --git a/languages/i18n/bg.json b/languages/i18n/bg.json index fad0987bf0..db39ac0991 100644 --- a/languages/i18n/bg.json +++ b/languages/i18n/bg.json @@ -71,7 +71,7 @@ "tog-shownumberswatching": "Показване на броя на потребителите, наблюдаващи дадена страница", "tog-oldsig": "Вашият текущ подпис:", "tog-fancysig": "Без превръщане на подписа в препратка към потребителската страница", - "tog-uselivepreview": "Използване на бърз предварителен преглед", + "tog-uselivepreview": "Показване на предварителен преглед без презареждане на страницата", "tog-forceeditsummary": "Предупреждаване при празно поле за резюме на редакцията", "tog-watchlisthideown": "Скриване на моите редакции в списъка ми за наблюдение", "tog-watchlisthidebots": "Скриване на редакциите на ботове в списъка ми за наблюдение", @@ -471,7 +471,7 @@ "nosuchusershort": "Не съществува потребител с името „$1“.\nПроверете изписването.", "nouserspecified": "Необходимо е да се посочи потребителско име.", "login-userblocked": "Този потребител е блокиран. Влизането в системата не е позволено.", - "wrongpassword": "Въведената парола е невалидна.\nОпитайте отново.", + "wrongpassword": "Навалидно потребителско име и/или парола.\nОпитайте отново.", "wrongpasswordempty": "Не е въведена парола.\nОпитайте отново.", "passwordtooshort": "Необходимо е паролата да съдържа поне {{PLURAL:$1|1 знак|$1 знака}}.", "passwordtoolong": "Паролата не може да бъде по-дългa от {{PLURAL:$1|1 знак|$1 знака}}.", @@ -537,15 +537,16 @@ "botpasswords-label-delete": "Изтриване", "botpasswords-label-resetpassword": "Възстановяване на парола", "botpasswords-label-grants": "Приложими разрешения:", + "botpasswords-label-grants-column": "Дадено", "botpasswords-bad-appid": "Името на бота „$1“ не е валидно.", "botpasswords-insert-failed": "Неуспешно добавяне на име на бота „$1“. Дали не е добавяно вече?", "botpasswords-update-failed": "Неуспешно подновяване на името на бота „$1“. Дали не е изтрито?", "botpasswords-created-title": "Паролата на бота е създадена", - "botpasswords-created-body": "Паролата на бот „$1“ на потребител „$2“ е създадена.", + "botpasswords-created-body": "Паролата на бот „$1“ на {{GENDER:$2|потребител}} „$2“ е създадена.", "botpasswords-updated-title": "Паролата на бота е обновена", - "botpasswords-updated-body": "Паролата на бот „$1“ на потребител „$2“ е обновена.", + "botpasswords-updated-body": "Паролата на бот „$1“ на потребител {{GENDER:$2|„$2“}} е обновена.", "botpasswords-deleted-title": "Паролата на бота е изтрита", - "botpasswords-deleted-body": "Паролата на бот „$1“ на потребител „$2“ е премахната.", + "botpasswords-deleted-body": "Паролата на бот „$1“ на {{GENDER:$2|потребител}} „$2“ е премахната.", "resetpass_forbidden": "Не е разрешена промяна на паролата", "resetpass_forbidden-reason": "Паролите не могат да се променят: $1", "resetpass-no-info": "За да имате директен достъп до тази страница е необходимо да влезете в системата.", @@ -573,6 +574,7 @@ "passwordreset-emailelement": "Потребителско име: \n$1\n\nВременна парола: \n$2", "passwordreset-emailsentemail": "Ако електронната Ви поща е свързана със сметката Ви, на нея е изпратено писмо за възстановяване на паролата.", "passwordreset-emailsentusername": "Ако това потребителско име е свързано с електронна поща, е изпратено писмо за възстановяване на паролата.", + "passwordreset-nosuchcaller": "Източникът на извикването не съществува: $1", "passwordreset-invalidemail": "Неправилен email адрес", "passwordreset-nodata": "Не сте указали нито потребителско име, нито адрес на ел. поща", "changeemail": "Промяна или премахване на адреса за е-поща", @@ -626,7 +628,7 @@ "anoneditwarning": "<strong>Внимание:</strong> Не сте влезли в системата. Ако направите редакция IP-адресът Ви ще бъде публично видим. Ако <strong>[$1 влезете]</strong> или си <strong>[$2 създадете акаунт]</strong>, редакциите Ви ще бъдат свързани с потребителското Ви име, заедно с други преимущества.", "anonpreviewwarning": "<em>Не сте влезли в системата. Ако съхраните редакцията си, тя ще бъде записана в историята на страницата с вашия IP-адрес.</em>", "missingsummary": "<strong>Напомняне:</strong> Не е въведено кратко описание на промените.\nПри повторно натискане на бутона „$1“, редакцията ще бъде съхранена без резюме.", - "missingcommenttext": "По-долу въведете вашето съобщение.", + "missingcommenttext": "Моля, въведете коментар.", "missingcommentheader": "<strong>Напомняне:</strong> Не е въведено заглавие на коментара.\nПри повторно натискане на „$1“, редакцията ще бъде записана без коментар.", "summary-preview": "Предварителен преглед на резюмето:", "subject-preview": "Предварителен преглед на заглавието:", @@ -962,7 +964,7 @@ "prefs-editwatchlist-clear": "Изчистване на списъка за наблюдение", "prefs-watchlist-days": "Брой дни, които да се показват в списъка за наблюдение:", "prefs-watchlist-days-max": "Най-много $1 {{PLURAL:$1|ден|дни}}", - "prefs-watchlist-edits": "Брой редакции, които се показват в разширения списък за наблюдение:", + "prefs-watchlist-edits": "Максимален брой редакции в списъка за наблюдение:", "prefs-watchlist-edits-max": "Максимален брой: 1000", "prefs-watchlist-token": "Уникален идентификатор на списъка за наблюдение:", "prefs-misc": "Други", @@ -982,7 +984,7 @@ "recentchangesdays-max": "(най-много $1 {{PLURAL:$1|ден|дни}})", "recentchangescount": "Брой показвани редакции по подразбиране:", "prefs-help-recentchangescount": "Това включва последните промени, историите на страниците и дневниците.", - "prefs-help-watchlist-token2": "Това е секретният ключ към уеб хранилката на вашия списък за наблюдение. Всеки, който го знае, би могъл да прегледа списъка ви за наблюдение, така че не го споделяйте. При нужда можете да го [[Специални:ResetTokens|изчистите]].", + "prefs-help-watchlist-token2": "Това е секретният ключ към уеб хранилката на вашия списък за наблюдение.\nВсеки, който го знае, би могъл да прегледа списъка ви за наблюдение, така че не го споделяйте.\nПри нужда можете да го [[Special:ResetTokens|изчистите]].", "savedprefs": "Настройките ви бяха съхранени.", "savedrights": "Потребителските групи на {{GENDER:$1|$1}} са запазени.", "timezonelegend": "Часова зона:", @@ -1164,7 +1166,7 @@ "right-siteadmin": "Заключване и отключване на базата от данни", "right-override-export-depth": "Изнасяне на страници, включително свързаните с тях в дълбочина до пето ниво", "right-sendemail": "Изпращане на е-писма до другите потребители", - "right-managechangetags": "Създаване и (де)активиране на [[Специални:Етикети|етикети]]", + "right-managechangetags": "Създаване и (де)активиране на [[Special:Tags|етикети]]", "grant-group-page-interaction": "Взаимодействие със страници", "grant-group-file-interaction": "Взаимодействие с медийни файлове", "grant-group-watchlist-interaction": "Взаимодействие с вашия списък за наблюдение", @@ -1273,7 +1275,9 @@ "rcfilters-group-results-by-page": "Групиране на резултатите по страница", "rcfilters-activefilters": "Активни филтри", "rcfilters-advancedfilters": "Разширени филтри", - "rcfilters-limit-title": "Промени за показване", + "rcfilters-limit-title": "Резултати за показване", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|промяна|$1 промени}}, $2", + "rcfilters-date-popup-title": "Период за търсене", "rcfilters-days-title": "Последните дни", "rcfilters-hours-title": "Последните часове", "rcfilters-days-show-days": "$1 {{PLURAL:$1|ден|дни}}", @@ -1296,12 +1300,12 @@ "rcfilters-restore-default-filters": "Възстановяване на филтрите по подразбиране", "rcfilters-clear-all-filters": "Изчистване на всички филтри", "rcfilters-show-new-changes": "Преглед на най-новите промени", - "rcfilters-search-placeholder": "Филтриране на последните промени (изберете или започнете да въвеждате)", + "rcfilters-search-placeholder": "Филтриране на промените (използвайте менюто или търсете по име на филтър)", "rcfilters-invalid-filter": "Невалиден филтър", "rcfilters-empty-filter": "Няма активни филтри. Показани са всички редакции.", "rcfilters-filterlist-title": "Филтри", "rcfilters-filterlist-whatsthis": "Как работи това?", - "rcfilters-filterlist-feedbacklink": "Оставете коментар за новите (бета) филтри", + "rcfilters-filterlist-feedbacklink": "Споделете какво мислите за тези (нови) инструменти за филтриране", "rcfilters-highlightbutton-title": "Отбелязване на резултатите", "rcfilters-highlightmenu-title": "Изберете цвят", "rcfilters-highlightmenu-help": "Изберете цвят за отбелязване на свойството", @@ -1311,17 +1315,17 @@ "rcfilters-filter-editsbyself-description": "Ваши редакции.", "rcfilters-filter-editsbyother-label": "Чужди редакции", "rcfilters-filter-editsbyother-description": "Всички редакции с изключение на вашите собствени.", - "rcfilters-filtergroup-userExpLevel": "Ниво на опита (само за регистрирани потребители)", + "rcfilters-filtergroup-userExpLevel": "Регистрация на потребителя и опит", "rcfilters-filter-user-experience-level-registered-label": "Регистрирани", "rcfilters-filter-user-experience-level-registered-description": "Влезли в системата редактори.", "rcfilters-filter-user-experience-level-unregistered-label": "Нерегистрирани", "rcfilters-filter-user-experience-level-unregistered-description": "Редактори, които не са влезли в системата.", "rcfilters-filter-user-experience-level-newcomer-label": "Новодошли", - "rcfilters-filter-user-experience-level-newcomer-description": "По-малко от 10 редакции и 5 дни активност.", + "rcfilters-filter-user-experience-level-newcomer-description": "Регистрирани редактори с по-малко от 10 редакции или 4 дни активност.", "rcfilters-filter-user-experience-level-learner-label": "Учещи се", - "rcfilters-filter-user-experience-level-learner-description": "Повече опит от „Новодошли“, но по-малко от „Опитни потребители“.", + "rcfilters-filter-user-experience-level-learner-description": "Регистрирани потребители с опит от „Новодошли“ до „Опитни потребители“.", "rcfilters-filter-user-experience-level-experienced-label": "Опитни потребители", - "rcfilters-filter-user-experience-level-experienced-description": "Повече от 30 дни активност и 500 редакции.", + "rcfilters-filter-user-experience-level-experienced-description": "Регистрирани редактори с повече от 500 редакции и 30 дни активност.", "rcfilters-filtergroup-automated": "Автоматизирани редакции", "rcfilters-filter-bots-label": "Бот", "rcfilters-filter-bots-description": "Редакции, направени с помощта на автоматизирани инструменти.", @@ -1358,9 +1362,9 @@ "rcfilters-filter-logactions-description": "Административни действия, създавания на сметки, изтривания на страници, качвания...", "rcfilters-filtergroup-lastRevision": "Текущи версии", "rcfilters-filter-lastrevision-label": "Текуща версия", - "rcfilters-filter-lastrevision-description": "Последната промяна на страница.", - "rcfilters-filter-previousrevision-label": "По-ранни версии", - "rcfilters-filter-previousrevision-description": "Всички редакции, които не са последните на страница.", + "rcfilters-filter-lastrevision-description": "Само последната промяна на страница.", + "rcfilters-filter-previousrevision-label": "Не последната версия", + "rcfilters-filter-previousrevision-description": "Всички редакции, които не са „последната версия“.", "rcfilters-filter-excluded": "Изключени", "rcfilters-tag-prefix-namespace-inverted": "<strong>:не</strong> $1", "rcfilters-exclude-button-off": "Изключване на избраните", @@ -1420,7 +1424,7 @@ "recentchangeslinked-feed": "Свързани промени", "recentchangeslinked-toolbox": "Свързани промени", "recentchangeslinked-title": "Промени, свързани с „$1“", - "recentchangeslinked-summary": "Тук се показват последните промени на страниците, към които се препраща от дадена страница. При избиране на категория, се показват промените по страниците, влизащи в нея. ''Пример:'' Ако изберете страницата '''А''', която съдържа препратки към '''Б''' и '''В''', тогава ще можете да прегледате промените по '''Б''' и '''В'''.\n\nАко пък сложите отметка пред '''Обръщане на релацията''', ще можете да прегледате промените в обратна посока: ще се включат тези страници, които съдържат препратки към посочената страница.\n\nСтраниците от списъка ви за наблюдение се показват в '''получер'''.", + "recentchangeslinked-summary": "Тук се показват последните промени на страниците, към които се препраща от дадена страница. При избиране на категория, се показват промените по страниците, влизащи в нея. ''Пример:'' Ако изберете страницата '''А''', която съдържа препратки към '''Б''' и '''В''', тогава ще можете да прегледате промените по '''Б''' и '''В'''.\n\nАко пък сложите отметка пред '''Обръщане на релацията''', ще можете да прегледате промените в обратна посока: ще се включат тези страници, които съдържат препратки към посочената страница.\n\nСтраниците от [[Special:Watchlist|списъка ви за наблюдение]] се показват в <strong>получер</strong>.", "recentchangeslinked-page": "Име на страницата:", "recentchangeslinked-to": "Обръщане на релацията, така че да се показват промените на страниците, сочещи към избраната страница", "recentchanges-page-added-to-category": "[[:$1]] е добавена към категория", @@ -1516,7 +1520,7 @@ "upload-file-error": "Вътрешна грешка", "upload-file-error-text": "Вътрешна грешка при опит за създаване на временен файл на сървъра.\nОбърнете се към [[Special:ListUsers/sysop|администратор]].", "upload-misc-error": "Неизвестна грешка при качване", - "upload-misc-error-text": "Неизвестна грешка при качване. Убедете се, че адресът е верен и опитайте отново. Ако отново имате проблем, обърнете се към [[Special:ListUsers/sysop|администратор]].", + "upload-misc-error-text": "Неизвестна грешка при качване.\nУбедете се, че адресът е верен и опитайте отново.\nАко отново имате проблем, обърнете се към [[Special:ListUsers/sysop|администратор]].", "upload-too-many-redirects": "Адресът съдържа твърде много пренасочвания", "upload-http-error": "Възникна HTTP грешка: $1", "upload-dialog-title": "Качване на файл", @@ -1551,8 +1555,8 @@ "backend-fail-read": "Файлът „$1“ не може да бъде прочетен.", "backend-fail-create": "Файлът „$1“ не може да бъде съхранен.", "backend-fail-maxsize": "Файлът „$1“ не може да бъде съхранен, тъй като размерът му надвишава {{PLURAL:$2|един байт|$2 байт}}.", - "backend-fail-connect": "Не е възможно свързването към бекенда за съхранение „1“.", - "backend-fail-internal": "Възникна неизвестна грешка в бекенда за съхранение „1“.", + "backend-fail-connect": "Не е възможно свързването към бекенда за съхранение „$1“.", + "backend-fail-internal": "Възникна неизвестна грешка в бекенда за съхранение „$1“.", "zip-file-open-error": "Възникна грешка при отваряне на файла за проверка на ZIP.", "zip-wrong-format": "Указаният файл не е ZIP файл.", "zip-bad": "Файлът е повреден или е нечетим ZIP файл.\nСигурността му не може да бъде проверена.", @@ -1606,7 +1610,7 @@ "listfiles_size": "Размер", "listfiles_description": "Описание", "listfiles_count": "Версии", - "listfiles-show-all": "Включване на старите версии на изображенията", + "listfiles-show-all": "Включване на стари версии на файлове", "listfiles-latestversion": "Текущата версия", "listfiles-latestversion-yes": "Да", "listfiles-latestversion-no": "Не", @@ -1650,7 +1654,7 @@ "filerevert-comment": "Причина:", "filerevert-defaultcomment": "Възвръщане към версия от $2, $1 ($3)", "filerevert-submit": "Възвръщане", - "filerevert-success": "Файлът '''[[Media:$1|$1]]''' беше възвърнат към [$4 версия от $3, $2].", + "filerevert-success": "Файлът <strong>[[Media:$1|$1]]</strong> беше възвърнат към [$4 версия от $3, $2].", "filerevert-badversion": "Не съществува предишна локална версия на файла със зададения времеви отпечатък.", "filedelete": "Изтриване на $1", "filedelete-legend": "Изтриване на файл", @@ -1659,8 +1663,8 @@ "filedelete-comment": "Причина:", "filedelete-submit": "Изтриване", "filedelete-success": "Файлът '''$1''' беше изтрит.", - "filedelete-success-old": "Версията на '''[[Media:$1|$1]]''' към $3, $2 е била изтрита.", - "filedelete-nofile": "Файлът '''$1''' не съществува.", + "filedelete-success-old": "Версията на <strong>[[Media:$1|$1]]</strong> към $3, $2 е била изтрита.", + "filedelete-nofile": "Файлът <strong>$1</strong> не съществува.", "filedelete-nofile-old": "Не съществува архивна версия на '''$1''' с указаните параметри.", "filedelete-otherreason": "Друга/допълнителна причина:", "filedelete-reason-otherlist": "Друга причина", @@ -1981,7 +1985,7 @@ "notvisiblerev": "Версията беше изтрита", "watchlist-details": "{{PLURAL:$1|Една наблюдавана страница|$1 наблюдавани страници}} от списъка Ви за наблюдение (без беседи).", "wlheader-enotif": "Известяването по е-поща е включено.", - "wlheader-showupdated": "Страниците, които са били променени след последния път, когато сте ги посетили, са показани в '''получер'''.", + "wlheader-showupdated": "Страниците, които са били променени след последния път, когато сте ги посетили, са показани в <strong>получер</strong>.", "wlnote": "{{PLURAL:$1|Показана е последната промяна|Показани са последните <strong>$1</strong> промени}} през {{PLURAL:$2|последния час|последните <strong>$2</strong> часа}}, започвайки от $3, $4.", "wlshowlast": "Показване на последните $1 часа $2 дни", "watchlist-hide": "Скриване", @@ -2184,8 +2188,8 @@ "contributions-userdoesnotexist": "Няма регистрирана потребителска сметка за „$1“.", "nocontribs": "Не са намерени промени, отговарящи на критерия.", "uctop": "(текуща)", - "month": "Месец:", - "year": "Година:", + "month": "От месец (и по-рано):", + "year": "От година (и по-рано):", "sp-contributions-newbies": "Показване само на приносите на нови потребители", "sp-contributions-newbies-sub": "на нови потребители", "sp-contributions-newbies-title": "Потребителски приноси за нови сметки", @@ -2206,8 +2210,8 @@ "whatlinkshere": "Какво сочи насам", "whatlinkshere-title": "Страници, които сочат към „$1“", "whatlinkshere-page": "Страница:", - "linkshere": "Следните страници сочат към '''[[:$1]]''':", - "nolinkshere": "Няма страници, сочещи към '''[[:$1]]'''.", + "linkshere": "Следните страници сочат към <strong>[[:$1]]</strong>:", + "nolinkshere": "Няма страници, сочещи към <strong>[[:$1]]</strong>.", "nolinkshere-ns": "Няма страници, сочещи към [[:$1]] в избраното именно пространство.", "isredirect": "пренасочваща страница", "istemplate": "включване", @@ -2389,9 +2393,9 @@ "movereason": "Причина:", "revertmove": "връщане", "delete_and_move_text": "Целевата страница „[[:$1]]“ вече съществува.\nИскате ли да я изтриете, за да освободите място за преместването?", - "delete_and_move_confirm": "Да, искам да изтрия тази страница.", + "delete_and_move_confirm": "Да, искам да изтрия тази страница", "delete_and_move_reason": "Изтрита, за да се освободи място за преместване от „[[$1]]“", - "selfmove": "Страницата не може да бъде преместена, тъй като целевото име съвпада с първоначалното ѝ заглавие.", + "selfmove": "Заглавието съвпада;\nстраницата не може да бъде преместена върху самата нея.", "immobile-source-namespace": "Не могат да се местят страници в именно пространство „$1“", "immobile-target-namespace": "Не могат да се местят страници в именно пространство „$1“.", "immobile-target-namespace-iw": "Страницата не може да бъде преместена под заглавие, оформено като междууики препратка.", @@ -2481,7 +2485,7 @@ "import-nonewrevisions": "Не са импортирани версии (всички вече съществуват или са пропуснати поради грешки).", "xml-error-string": "$1 на ред $2, колона $3 (байт $4): $5", "import-upload": "Качване на XML данни", - "import-token-mismatch": "Загуба на данните за текущата сесия.\n\nМоже би сте излезли от системата. <strong>Моля, уверете се, че сте влезли в профила си и опитайте отново</strong>.\nАко все още не работи, опитайте да [[Special:UserLogout|излезете]] и да влезете отново, също така проверете дали браузърът ви позволява бисквитки от този сайт.", + "import-token-mismatch": "Загуба на данните за текущата сесия.\n\nМоже би сте излезли от системата. '''Моля, уверете се, че сте влезли в профила си и опитайте отново'''.\nАко все още не работи, опитайте да [[Special:UserLogout|излезете]] и да влезете отново, също така проверете дали браузърът ви позволява бисквитки от този сайт.", "import-invalid-interwiki": "Не може да бъде извършено внасяне от посоченото уики.", "import-error-edit": "Страницата „$1“ не беше внесена, тъй като нямате права да я редактирате.", "import-error-create": "Страницата „$1“ не беше внесена, тъй като нямате права да я създадете.", @@ -2519,15 +2523,15 @@ "tooltip-ca-delete": "Изтриване на страницата", "tooltip-ca-undelete": "Възстановяване на изтрити редакции на страницата", "tooltip-ca-move": "Преместване на страницата", - "tooltip-ca-watch": "Добавяне на страницата към списъка ви за наблюдение", - "tooltip-ca-unwatch": "Премахване на страницата от списъка ви за наблюдение", + "tooltip-ca-watch": "Добавяне на страницата към списъка Ви за наблюдение", + "tooltip-ca-unwatch": "Премахване на страницата от списъка Ви за наблюдение", "tooltip-search": "Претърсване на {{SITENAME}}", "tooltip-search-go": "Отиване на страницата, ако тя съществува с точно това име", "tooltip-search-fulltext": "Търсене в страниците за този текст", "tooltip-p-logo": "Посещаване на началната страница", "tooltip-n-mainpage": "Началната страница", "tooltip-n-mainpage-description": "Посещаване на началната страница", - "tooltip-n-portal": "Информация за проекта — какво, къде, как", + "tooltip-n-portal": "Информация за проекта – какво, къде, как", "tooltip-n-currentevents": "Информация за текущи събития", "tooltip-n-recentchanges": "Списък на последните промени в уикито", "tooltip-n-randompage": "Зареждане на случайна страница", @@ -2753,7 +2757,7 @@ "exif-software": "Използван софтуер", "exif-artist": "Автор", "exif-copyright": "Притежател на авторското право", - "exif-exifversion": "Exif версия", + "exif-exifversion": "Версия на Exif", "exif-flashpixversion": "Поддържана версия на Flashpix", "exif-colorspace": "Цветово пространство", "exif-componentsconfiguration": "Значение на всеки компонент", @@ -3101,6 +3105,7 @@ "autosumm-blank": "Премахване на цялото съдържание на страницата", "autosumm-replace": "Заместване на съдържанието на страницата с „$1“", "autoredircomment": "Пренасочване към [[$1]]", + "autosumm-changed-redirect-target": "Промяна на целта на пренасочване от [[$1]] на [[$2]]", "autosumm-new": "Нова страница: „$1“", "autosumm-newblank": "Създаване на празна страница", "lag-warn-normal": "Промените от {{PLURAL:$1|последната $1 секунда|последните $1 секунди}} вероятно не са показани в списъка.", @@ -3180,7 +3185,7 @@ "redirect-submit": "Отваряне", "redirect-lookup": "Параметър:", "redirect-value": "Стойност:", - "redirect-user": "Потребителски номер", + "redirect-user": "Потребителски идентификатор", "redirect-page": "Номер на страницата", "redirect-revision": "Версия на страницата", "redirect-file": "Име на файл", @@ -3218,6 +3223,7 @@ "tag-filter-submit": "Филтриране", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Етикет|Етикети}}]]: $2)", "tag-mw-contentmodelchange": "промяна на модела на съдържание", + "tag-mw-undo": "Отмяна", "tags-title": "Етикети", "tags-intro": "Тук са изброени всички етикети, които могат да се ползват за отбелязване на редакциите, както и тяхното значение.", "tags-tag": "Име на етикета", diff --git a/languages/i18n/bho.json b/languages/i18n/bho.json index f0cbb13eda..9ce3d2ccec 100644 --- a/languages/i18n/bho.json +++ b/languages/i18n/bho.json @@ -1213,7 +1213,7 @@ "recentchangeslinked-feed": "संबंधित बदलाव", "recentchangeslinked-toolbox": "संबंधित बदलाव", "recentchangeslinked-title": "\"$1\" संबंधी बदलाव", - "recentchangeslinked-summary": "ई एगो पन्ना बिसेस से (या एगो श्रेणी बिसेस की सदस्य से) जुड़ल पन्नवन में हाल में भइल बदलाव के एगो लिस्ट हवे। \n[[Special:Watchlist|आपकी धियानसूची]] के पन्ना सब <strong>मोट अच्छर में</strong> लउकत बाने।", + "recentchangeslinked-summary": "कौनों पन्ना पर या फिर ओकरा से जुड़े वाला पन्ना सभ में भइल बदलाव देखे खाती पन्ना के नाँव दीं। (कौनों श्रेणी में आवे वाला पन्ना सभ खाती, श्रेणी:श्रेणी के नाँव डालीं)। [[Special:Watchlist|राउर धियानसूची]] में शामिल पन्ना सभ के बदलाव <strong>मोट अच्छर में</strong> लउकत बाने।", "recentchangeslinked-page": "पन्ना नाँव:", "recentchangeslinked-to": "एकरे बजाय ए पन्ना से जुड़ल पन्नवन मे भइल बदलाव देखावल जाय", "recentchanges-page-added-to-category": "[[:$1]] श्रेणी में जोड़ल गइल", @@ -1427,6 +1427,7 @@ "booksources-search-legend": "किताबी स्रोत के खोज", "booksources-search": "खोज", "log": "सगरी लॉग", + "all-logs-page": "सगरी पब्लिक लॉग", "allpages": "सगरी पन्ना", "nextpage": "अगिला पन्ना ($1)", "prevpage": "पछिला पन्ना ($1)", @@ -1448,6 +1449,7 @@ "trackingcategories-name": "संदेस नाँव", "emailuser": "ई प्रयोगकर्ता के ईमेल करीं", "emailusername": "प्रयोगकर्तानाँव:", + "usermessage-editor": "सिस्टम मसेंजर", "watchlist": "धियानसूची", "mywatchlist": "धियानसूची", "watch": "धियानसूची में डालीं", @@ -1464,6 +1466,8 @@ "protectlogpage": "सुरक्षा लॉग", "protectlogtext": "नीचे पन्ना सुरक्षा में भइल बदलावकुल के सूची बा।\nहाल में सुरक्षित पन्नन के सूची खातिर [[Special:ProtectedPages|सुरक्षित पन्नन के सूची]] देखीं।", "protectedarticle": "\"[[$1]]\" सुरक्षित कइल गइल", + "protect-default": "सगरी प्रयोगकर्ता लोग के एलाऊ करीं", + "restriction-edit": "संपादन करीं", "restriction-move": "स्थानांतरण", "restriction-create": "बनावे पर", "restriction-upload": "अपलोड", @@ -1500,6 +1504,7 @@ "sp-contributions-blocked-notice": "ई प्रयोगकर्ता के ई समय निष्क्रीय करल गईल बा।\nनविनतम नष्ट लौग प्रविष्टी उद्धरण खातिर निचे दिहल बा:", "sp-contributions-search": "योगदान खातिर खोज करीं", "sp-contributions-username": "आइपी पता भा प्रयोगकर्तानाँव:", + "sp-contributions-toponly": "खाली उहे संपादन देखीं जवन सभसे नया होखें", "sp-contributions-newonly": "खाली उहे संपादन देखीं जेकरा से नया पन्ना बनल होखे", "sp-contributions-submit": "खोजीं", "whatlinkshere": "इहाँ का जुड़ल बा", @@ -1528,6 +1533,7 @@ "change-blocklink": "ब्लॉक बदलीं", "contribslink": "योगदान", "blocklogpage": "निष्क्रिय खाता", + "blocklogentry": "[[$1]] के ब्लॉक कइल गइल, समाप्ती के अवधि $2 $3", "movepagebtn": "पन्ना स्थांतरण करीं", "movelogpage": "स्थानांतरण लॉग", "revertmove": "पिछलका स्थिति", @@ -1605,12 +1611,21 @@ "tooltip-save": "जवन बदलाव कइलीं ओकरा के सहेजीं", "tooltip-preview": "जवन बदलाव कइलीं ओकर झलक देखीं। सहेजे से पहिले एकर इस्तेमाल करे के आगरह बा।", "tooltip-diff": "देखीं कि पाठ में आप का बदलाव कइले बानी", + "tooltip-compareselectedversions": "एह पन्ना के चुनल गइल दू गो रिवीजन सभ में अंतर देखीं", "tooltip-rollback": "\"रोलबैक\" एह पन्ना पर सभसे अंतिम संपादन करे वाला संपादक के कइल बदलाव(सभ) एकही क्लिक में वापस लवटा देला", "tooltip-undo": "\"वापस लीं\" ए संपादन के पलट देला आ संपादन फार्म के झलक देखावे वाला मोड में खोलेला। ई छोट सारांश में कारण जोड़े के मोका देला।", "tooltip-summary": "संछेप में एगो सारांश लिखीं", "simpleantispam-label": "स्पैम-बिरोधी रोक (Anti-spam check)\nएके <strong>मत</strong> भरीं!", + "pageinfo-header-edits": "संपादन इतिहास", "pageinfo-length": "पन्ना लंबाई (बाइट में)", + "pageinfo-article-id": "पन्ना आइडी", "pageinfo-subpages-name": "एह पन्ना के उपपन्ना संख्या", + "pageinfo-firstuser": "पन्ना बनावेवाला", + "pageinfo-firsttime": "पन्ना बनावे के तारीख", + "pageinfo-lastuser": "अंतिम संपादक", + "pageinfo-lasttime": "सभसे नया संपादन के तारीख", + "pageinfo-edits": "कुल संपादन गिनती", + "pageinfo-authors": "कुल अलग-अलग संपादकन के गिनती", "pageinfo-magic-words": "जादुई शब्द {{{{PLURAL:$1||शब्द|शब्द}}}} ($1)", "pageinfo-toolboxlink": "पन्ना से जुड़ल जानकारी", "previousdiff": "← पुरान संपादन", @@ -1681,6 +1696,7 @@ "logentry-newusers-create": "खाता $1 {{GENDER:$2|बनावल गइल}}", "logentry-upload-upload": "$1 {{GENDER:$2|अपलोड कइलें}} $3", "searchsuggest-search": "{{SITENAME}} में खोजीं", + "duration-days": "$1 दिन", "expandtemplates": "टेम्पलेट बिस्तार", "mediastatistics": "मीडिया सांख्यिकी" } diff --git a/languages/i18n/bn.json b/languages/i18n/bn.json index d17556bc6c..9037116939 100644 --- a/languages/i18n/bn.json +++ b/languages/i18n/bn.json @@ -725,7 +725,7 @@ "contentmodelediterror": "আপনি এই পুনর্বিবেচনা সম্পাদনা করতে পারবেন না কারণ এর বিষয়বস্তু মডেল <code>$1</code>, যা বর্তমান বিষয়বস্তু মডেল <code>$2</code>-এর থেকে ভিন্ন।", "recreate-moveddeleted-warn": "'''সতর্কীকরণ: আপনি এমন একটি পাতা পুনরায় তৈরি করছেন যা পূর্বে অপসারণ করা হয়েছিল।'''\n\nআপনি পাতাটি সম্পাদনা চালিয়ে যাওয়া ঠিক হবে কিনা, তা বিবেচনা করুন।\nআপনার সুবিধার্থে পাতাটির অপলুপ্তি লগ এখানে দেয়া হলো:", "moveddeleted-notice": "এই পাতাটি অপসারণ করা হয়েছে।\nসূত্র হিসেবে নিচে এই পাতার অপসারণ, সুরক্ষা ও স্থানান্তর লগ দেওয়া হলো।", - "moveddeleted-notice-recent": "দুঃখিত, এই পাতাটি সাম্প্রতি অপসারিত হয়েছে (সর্বশেষ ২৪ ঘণ্টায়)।\nসূত্র হিসেবে নিচে এই পাতা অপসারণ, সুরক্ষা ও স্থানান্তর লগ দেয়া হয়েছে।", + "moveddeleted-notice-recent": "দুঃখিত, এই পাতাটি সম্প্রতি অপসারিত হয়েছে (সর্বশেষ ২৪ ঘণ্টায়)।\nসূত্র হিসেবে নিচে এই পাতার অপসারণ, সুরক্ষা ও স্থানান্তর লগ দেয়া হয়েছে।", "log-fulllog": "সম্পূর্ণ লগ দেখুন", "edit-hook-aborted": "হূক দ্বারা সম্পাদনা পরিত্যক্ত হয়েছে।\nএর কোন ব্যাখ্যা নাই।", "edit-gone-missing": "পাতাটি হালনাগাদ হয়নি।\nসম্ভবতঃ পাতাটি মুছে ফেলা হয়েছে।", @@ -1036,6 +1036,7 @@ "timezoneregion-indian": "ভারত মহাসাগর", "timezoneregion-pacific": "প্রশান্ত মহাসাগর", "allowemail": "অন্য ব্যবহারকারীদেরকে আমাকে ইমেল করতে অনুমতি দিন", + "email-allow-new-users-label": "নতুন ব্যবহারকারীদেরকে ইমেলের অনুমতি দিন", "email-blacklist-label": "আমাকে ইমেইল পাঠানো থেকে এই ব্যবহারকারীদের বিরত রাখুন:", "prefs-searchoptions": "অনুসন্ধান", "prefs-namespaces": "নামস্থানসমূহ", @@ -1306,6 +1307,7 @@ "recentchanges-noresult": "নির্ধারিত সময়ের মধ্যে কোনো পরিবর্তন পাওয়া যায়নি।", "recentchanges-timeout": "এই অনুসন্ধানের সময় শেষ হয়েছে। আপনি ভিন্ন অনুসন্ধানের পরামিতি দিয়ে চেষ্টা করতে পারেন।", "recentchanges-network": "কারিগরি ত্রুটির কারনে কোন ফলাফল পাওয়া যায়নি । অনুগ্রহকরে এই পাতাটি রিফ্রেশ করে আমার চেষ্টা করুন ।", + "recentchanges-notargetpage": "যে পাতার সম্পর্কিত পরিবর্তনগুলি দেখতে চান সেই পাতার নাম উপরে লিখুন।", "recentchanges-feed-description": "এই ফিডে উইকিটির সবচেয়ে সাম্প্রতিক পরিবর্তনগুলি অনুসরণ করুন।", "recentchanges-label-newpage": "এই সম্পাদনায় একটি নতুন পাতা তৈরি হয়েছে", "recentchanges-label-minor": "এটি একটি অনুল্লেখিত সম্পাদনা", @@ -1436,6 +1438,7 @@ "rcfilters-watchlist-showupdated": "পরিবর্তন যখন ঘটেছে তখন থেকে যে পাতাগুলি আপনি এখনো দেখেননি তা মোটা চিহ্নসহ <strong>গাঢ়</strong> করে দেখানো হয়েছে।", "rcfilters-preference-label": "সাম্প্রতিক পরিবর্তনের উন্নত সংস্করণটি লুকান", "rcfilters-preference-help": "২০১৭-এর পুনঃনকশাকৃত ইন্টারফেস এবং তখন পর্যন্ত ও তখন থেকে যোগ করা সকল সরঞ্জামে ফেরত নিবে।", + "rcfilters-target-page-placeholder": "একটি পাতার নাম লিখুন", "rcnotefrom": "<strong>$2</strong>টা থেকে সংঘটিত পরিবর্তনগুলি (সর্বোচ্চ <strong>$1টি</strong> দেখানো হয়েছে)।", "rclistfromreset": "তারিখ নির্বাচন পুনঃস্থাপন করুন", "rclistfrom": "$2, $3 তারিখের পর সংঘটিত নতুন পরিবর্তনগুলো দেখাও", @@ -1480,7 +1483,7 @@ "recentchangeslinked-feed": "সম্পর্কিত পরিবর্তন", "recentchangeslinked-toolbox": "সম্পর্কিত পরিবর্তন", "recentchangeslinked-title": "\"$1\"-এর সাথে সম্পর্কিত পরিবর্তনসমূহ", - "recentchangeslinked-summary": "একটি নির্দিষ্ট পাতা (অথবা নির্দিষ্ট বিষয়শ্রেণীতে) থেকে সংযুক্ত এ পাতার সাম্প্রতিক পরিবর্তনের তালিকা দেওয়া হয়েছে। আপনার [[Special:Watchlist|আপনার নজরতালিকায়]] রাখা পাতাগুলি '''গাঢ়''' করে দেখানো হয়েছে।", + "recentchangeslinked-summary": "একটি পাতায় বা পাতা থেকে সংযুক্ত পাতাগুলির পরিবর্তন দেখতে একটি পাতার নাম লিখুন। (একটি বিষয়শ্রেণীর সদস্যদের দেখতে, বিষয়শ্রেণী:বিষয়শ্রেণীর নাম লিখুন)। আপনার [[Special:Watchlist|আপনার নজরতালিকায়]] রাখা পাতাগুলি <strong>গাঢ়</strong> করে দেখানো হয়েছে।", "recentchangeslinked-page": "পাতার নাম:", "recentchangeslinked-to": "প্রদত্ত পাতায় সংযুক্ত আছে এমন পাতাগুলোর পরিবর্তন দেখাও", "recentchanges-page-added-to-category": "বিষয়শ্রেণীতে [[:$1]] যোগ করা হয়েছে", @@ -3337,6 +3340,8 @@ "autosumm-blank": "পাতা খালি করা হয়েছে", "autosumm-replace": "পাতাকে '$1' দিয়ে প্রতিস্থাপিত করা হল", "autoredircomment": "[[$1]]-এ পুনর্নির্দেশ করা হল", + "autosumm-removed-redirect": "[[$1]]-এ করা পুনর্নির্দেশ সরানো হয়েছে", + "autosumm-changed-redirect-target": "পুনর্নির্দেশের লক্ষ্য [[$1]] থেকে [[$2]]-এ পরিবর্তিত হয়েছে", "autosumm-new": "\"$1\" দিয়ে পাতা তৈরি", "autosumm-newblank": "খালি পাতা তৈরি হয়েছে", "size-bytes": "$1 {{PLURAL:$1|বাইট}}", @@ -3528,10 +3533,18 @@ "tag-mw-contentmodelchange": "বিষয়বস্তুর রূপ পরিবর্তন", "tag-mw-contentmodelchange-description": "সম্পাদনা যা একটি পাতার [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel বিষয়বস্তুর রূপ পরিবর্তন] করে", "tag-mw-new-redirect": "নতুন পুনর্নির্দেশনা", + "tag-mw-new-redirect-description": "সম্পাদনাগুলি যা একটি নতুন পুনর্নির্দেশ তৈরি করে বা একটি পাতাকে একটি পুনর্নির্দেশে পরিবর্তিত করে", + "tag-mw-removed-redirect": "পুনর্নির্দেশ সরানো হয়েছে", + "tag-mw-removed-redirect-description": "সম্পাদনাগুলি যা ইতিমধ্যে থাকা পুনর্নির্দেশনাকে অ-পুনর্নির্দেশনায় পরিবর্তিত করে", + "tag-mw-changed-redirect-target": "পুনর্নির্দেশের লক্ষ্য পরিবর্তিত হয়েছে", + "tag-mw-changed-redirect-target-description": "সম্পাদনাগুলি যা একটি পুনর্নির্দেশের লক্ষ্য পরিবর্তন করে", "tag-mw-blank": "খালি করা", "tag-mw-blank-description": "সেই সম্পাদনাগুলি যা একটি পাতাকে খালি করেছে", "tag-mw-replace": "প্রতিস্থাপিত", + "tag-mw-replace-description": "সম্পাদনাগুলি যা একটি পাতার বিষয়বস্তুর ৯০ ভাগের বেশী সরিয়ে ফেলে", "tag-mw-rollback": "পুনর্বহাল", + "tag-mw-rollback-description": "সম্পাদনাগুলি যা রোলব্যাক সংযোগ ব্যবহার করে পূর্বের সম্পাদনায় ফেরত নেয়", + "tag-mw-undo": "পূর্বাবস্থায় ফেরত", "tags-title": "ট্যাগসমূহ", "tags-intro": "এই পাতায় সফটওয়্যারটি একটি সম্পাদনা চিহ্নিত করার জন্য যে সকল ট্যাগ ব্যবহার করে তার তালিকা ও বর্ণনা রয়েছে।", "tags-tag": "ট্যাগ নাম", @@ -3630,6 +3643,7 @@ "compare-revision-not-exists": "আপনার কাঙ্ক্ষিত সংস্করণটি নেই।", "diff-form": "পার্থক্য", "diff-form-oldid": "পুরানো সংস্করণ আইডি (ঐচ্ছিক)", + "diff-form-revid": "পার্থক্যের সংস্করণের আইডি", "diff-form-submit": "পার্থক্য দেখান", "permanentlink": "স্থায়ী সংযোগ", "permanentlink-revid": "সংশোধন আইডি", diff --git a/languages/i18n/bs.json b/languages/i18n/bs.json index f8eb1cc73a..312b20c2e6 100644 --- a/languages/i18n/bs.json +++ b/languages/i18n/bs.json @@ -630,7 +630,7 @@ "anonpreviewwarning": "''Niste prijavljeni. Nakon spremanja izmjena vaÅ¡a IP adresa će biti zapisana u historiji uređivanja ove stranice.''", "missingsummary": "<strong>Napomena:</strong> Niste unijeli sažetak izmjene.\nAko ponovo kliknete na \"$1\", VaÅ¡a izmjena će biti sačuvana bez sažetka.", "selfredirect": "<strong>Upozorenje:</strong> Preusmjerili ste stranicu na samu sebe.\nMožda ste naveli pogreÅ¡an cilj preusmjeravanja ili ste uređivali pogreÅ¡nu stranicu.\nAko ponovno kliknete \"$1\", ipak će nastati preusmjerenje.", - "missingcommenttext": "Unesite komentar ispod.", + "missingcommenttext": "Unesite komentar.", "missingcommentheader": "<strong>Napomena:</strong> Niste napisali naslov ovog komentara.\nAko ponovo kliknete na \"$1\", VaÅ¡a izmjena će biti sačuvana bez naslova.", "summary-preview": "Pregled sažetka:", "subject-preview": "Pregled teme:", @@ -712,7 +712,7 @@ "contentmodelediterror": "Ne možete urediti ovu izmjenu jer je njen model sadržaja <code>$1</code>, Å¡to se razlikuje od trenutnog modela sadržaja stranice <code>$2</code>.", "recreate-moveddeleted-warn": "<strong>Upozorenje: Ponovo pravite stranicu koja je prethodno obrisana.</strong>\n\nRazmotrite je li prikladno nastaviti s uređivanjem ove stranice.\nOvdje je naveden zapisnik brisanja i premjeÅ¡tanja:", "moveddeleted-notice": "Ova stranica je obrisana.\nZapisnik brisanja, zaÅ¡tite i premjeÅ¡tanja stranice prikazan je ispod.", - "moveddeleted-notice-recent": "Žao nam je, ova stranica je nedavno obrisana (u prethodna 24 sata).\nNiže su navedeni zapisnici brisanja i premjeÅ¡tanja.", + "moveddeleted-notice-recent": "Žao nam je, ova stranica je nedavno obrisana (u prethodna 24 sata).\nNiže su navedeni zapisnici brisanja, zaÅ¡tite i premjeÅ¡tanja.", "log-fulllog": "Prikaži cijeli zapisnik", "edit-hook-aborted": "Izmjena je poniÅ¡tena putem interfejsa.\nNije ponuđeno nikakvo objaÅ¡njenje.", "edit-gone-missing": "Stranica se nije mogla osvježiti.\nIzgleda da je obrisana.", @@ -905,6 +905,8 @@ "diff-multi-sameuser": "({{PLURAL:$1|Nije prikazana jedna međuizmjena|Nisu prikazane $1 međuizmjene|Nije prikazano $1 međuizmjena}} istog korisnika)", "diff-multi-otherusers": "(Nije prikazana {{PLURAL:$1|jedna međuverzija|$1 međuverzija}} {{PLURAL:$2|drugog korisnika|$2 korisnika}})", "diff-multi-manyusers": "({{PLURAL:$1|Jedna međurevizija|$1 međurevizije|$1 međurevizija}} od viÅ¡e od $2 {{PLURAL:$2|korisnika|korisnika}} {{PLURAL:$1|nije prikazana|nisu prikazane}})", + "diff-paragraph-moved-tonew": "Pasus je premjeÅ¡ten. Kliknite da pređete na njegovo novo mjesto.", + "diff-paragraph-moved-toold": "Pasus je premjeÅ¡ten. Kliknite da pređete na njegovo staro mjesto.", "difference-missing-revision": "{{PLURAL:$2|Jedna izmjena|$2 izmjene}} od ove razlike ($1) ne {{PLURAL:$2|postoji|postoje}}.\n\nOvo se obično deÅ¡ava kada pratite zastarjelu vezu na stranice koja je obrisana.\nViÅ¡e informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokol brisanja].", "searchresults": "Rezultati pretrage", "searchresults-title": "Rezultati pretrage za \"$1\"", @@ -1642,6 +1644,22 @@ "uploadstash-refresh": "Osvježi spisak datoteka", "uploadstash-thumbnail": "pogledaj minijaturu", "uploadstash-exception": "Ne mogu sačuvati datoteku u skladiÅ¡te ($1): \"$2\".", + "uploadstash-bad-path": "Putanja ne postoji.", + "uploadstash-bad-path-invalid": "Putanja nije ispravna.", + "uploadstash-bad-path-unknown-type": "Nepoznata vrsta \"$1\".", + "uploadstash-bad-path-bad-format": "Ključ \"$1\" nije u odgovarajućem formatu.", + "uploadstash-file-not-found-no-thumb": "Ne mogu dobiti minijaturu.", + "uploadstash-file-not-found-no-local-path": "Nema lokalne putanje za umanjenu stavku.", + "uploadstash-file-not-found-no-object": "Ne mogu napraviti lokalni podatkovni objekt za minijaturu.", + "uploadstash-file-not-found-no-remote-thumb": "Dobavljanje minijature nije uspjelo: $1\nURL = $2", + "uploadstash-file-not-found-missing-content-type": "Nedostaje zaglavlje za vrstu sadržaja.", + "uploadstash-file-not-found-not-exists": "Ne mogu naći putanju ili ovo nije obična datoteka.", + "uploadstash-file-too-large": "Ne mogu poslužiti datoteku veću od $1 {{PLURAL:$1|bajta|bajtova}}", + "uploadstash-not-logged-in": "Niko nije prijavljen. Datoteke moraju pripadati korisnicima.", + "uploadstash-wrong-owner": "Ova datoteka ($1) ne pripada trenutnom korisniku.", + "uploadstash-no-such-key": "Nema takvog ključa ($1). Ne mogu ukloniti.", + "uploadstash-no-extension": "ProÅ¡irenje je prazno.", + "uploadstash-zero-length": "Datoteka ima nultu dužinu.", "invalid-chunk-offset": "Neispravna polazna tačka", "img-auth-accessdenied": "Pristup onemogućen", "img-auth-nopathinfo": "Nedostaje PATH_INFO.\nVaÅ¡ server nije postavljen da daje ovu informaciju.\nMožda je zasnovan na CGI koji ne podržava img_auth.\nPogledajte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.", @@ -2103,6 +2121,7 @@ "enotif_lastdiff": "Da vidite ovu izmjenu, pogledajte $1", "enotif_anon_editor": "anonimni korisnik $1", "enotif_body": "PoÅ¡tovani $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nSažetak urednika: $PAGESUMMARY $PAGEMINOREDIT\n\nKontaktirajte urednika:\ne-poÅ¡ta: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nNeće biti drugih obavjeÅ¡tenja u slučaju daljnjih izmjena osim ako prijavljeni ponovno posjetite stranicu. Također možete poniÅ¡titi oznake obavijesti za sve praćene stranice koje imate na vaÅ¡em spisku praćenja.\n\nVaÅ¡ prijateljski {{SITENAME}} sistem obavjeÅ¡tavanja\n\n--\nZa promjenu vaÅ¡ih postavki email obavijesti, posjetite\n{{canonicalurl:{{#special:Preferences}}}}\n\nZa promjenu postavki vaÅ¡eg praćenja, posjetite\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nDa obriÅ¡ete stranicu sa vaÅ¡eg spiska praćenja, posjetite\n$UNWATCHURL\n\nPovratne informacije i daljnja pomoć:\n$HELPPAGE", + "enotif_minoredit": "Ovo je manja izmjena", "created": "napravljena", "changed": "izmijenjena", "deletepage": "ObriÅ¡i stranicu", @@ -2250,6 +2269,7 @@ "undelete-search-title": "Pretraga obrisanih stranica", "undelete-search-box": "Pretraga obrisanih stranica", "undelete-search-prefix": "Prikaži stranice koje počinju sa:", + "undelete-search-full": "Prikaži naslove koji sadrže:", "undelete-search-submit": "Traži", "undelete-no-results": "Nije pronađena odgovarajuća stranica u arhivi brisanja.", "undelete-filename-mismatch": "Ne može se vratiti revizija datoteke od $1: pogreÅ¡no ime datoteke", @@ -2419,6 +2439,8 @@ "ipb_blocked_as_range": "GreÅ¡ka: IP adresa $1 nije direktno blokirana i ne može se deblokirati.\nMeđutim, možda je blokirana kao dio bloka $2, koji se ne može deblokirati.", "ip_range_invalid": "Netačan opseg IP-adresa.", "ip_range_toolarge": "Nisu dopuÅ¡tene blokade veće od /$1.", + "ip_range_exceeded": "IP-opseg prekoračuje gornju granicu. Dozvoljeni opseg: /$1.", + "ip_range_toolow": "IP-opsezi nisu dozvoljeni.", "proxyblocker": "ZaÅ¡tita od proxya", "proxyblockreason": "VaÅ¡a IP adresa je blokirana jer je ona otvoreni proxy.\nMolimo vas da kontaktirate vaÅ¡eg davatelja internetskih usluga ili tehničku podrÅ¡ku i obavijestite ih o ovom ozbiljnom sigurnosnom problemu.", "sorbsreason": "VaÅ¡a IP adresa je prikazana kao otvoreni proxy u DNSBL koji koristi {{SITENAME}}.", @@ -2488,7 +2510,7 @@ "delete_and_move_text": "OdrediÅ¡na stranica \"[[:$1]]\" već postoji.\nŽelite li je obrisati da biste oslobodili mjesto za premjeÅ¡tanje?", "delete_and_move_confirm": "Da, obriÅ¡i stranicu", "delete_and_move_reason": "Obrisano da se oslobodi mjesto za premjeÅ¡tanje iz \"[[$1]]\"", - "selfmove": "Izvorni i ciljani naziv su isti; strana ne može da se premjesti preko same sebe.", + "selfmove": "Naziv je isti;\nne mogu premjestiti stranicu preko same sebe.", "immobile-source-namespace": "Ne mogu premjestiti stranice u imenski prostor \"$1\"", "immobile-target-namespace": "Ne mogu se premjestiti stranice u imenski prostor \"$1\"", "immobile-target-namespace-iw": "Međuwiki link nije ispravno odrediÅ¡te premjeÅ¡tanja stranice.", @@ -2562,6 +2584,7 @@ "import-mapping-namespace": "Uvezi u imenski prostor:", "import-mapping-subpage": "Uvezi kao podstranice sljedeće stranice:", "import-upload-filename": "Naziv datoteke:", + "import-upload-username-prefix": "Međuwiki-prefiks:", "import-comment": "Komentar:", "importtext": "Izvezite datoteku iz izvornog wikija koristeći [[Special:Export|alat za izvoz]].\nSačuvajte je na svoj računar i postavite je ovdje.", "importstart": "Uvozim stranice...", @@ -2570,6 +2593,7 @@ "imported-log-entries": "{{PLURAL:$1|Uvezena $1 stavka zapisnika|Uvezene $1 stavke zapisnika|Uvezeno $1 stavki zapisnika}}.", "importfailed": "Uvoz nije uspio: $1", "importunknownsource": "Nepoznat izvorni tip uvoza", + "importnoprefix": "Nije naveden međuwiki-prefiks", "importcantopen": "Ne mogu otvoriti datoteku za uvoz", "importbadinterwiki": "LoÅ¡ interwiki link", "importsuccess": "UspjeÅ¡no ste uvezli stranicu!", @@ -3242,6 +3266,8 @@ "autosumm-blank": "Uklonjen cjelokupan sadržaj stranice", "autosumm-replace": "Zamijenjen sadržaj stranice sa \"$1\"", "autoredircomment": "Preusmjereno na [[$1]]", + "autosumm-removed-redirect": "Uklonjeno preusmjerenje na [[$1]]", + "autosumm-changed-redirect-target": "Izmijenjeno odrediÅ¡te preusmjerenja sa [[$1]] na [[$2]]", "autosumm-new": "Nova stranica: $1", "autosumm-newblank": "Napravljena prazna stranica", "size-bytes": "$1 {{PLURAL:$1|bajt|bajta|bajtova}}", @@ -3422,6 +3448,12 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|oznaka|oznake}}]]: $2)", "tag-mw-contentmodelchange": "promjena modela sadržaja", "tag-mw-contentmodelchange-description": "Izmjena kojom se [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel mijenja model sadržaja] stranice", + "tag-mw-new-redirect": "novo preusmjerenje", + "tag-mw-removed-redirect": "uklonjeno preusmjerenje", + "tag-mw-changed-redirect-target": "izmijenjena odrediÅ¡na stranica preusmjerenja", + "tag-mw-blank": "pražnjenje", + "tag-mw-replace": "zamijenjeno", + "tag-mw-rollback": "vraćanje", "tags-title": "Oznake", "tags-intro": "Ova stranica prikazuje spisak oznaka koje softver može staviti na svaku izmjenu i njihovo značenje.", "tags-tag": "Naziv oznake", @@ -3924,5 +3956,6 @@ "undelete-cantedit": "Ne možete vratiti ovu stranicu jer Vam nije dozvoljeno da je uređujete.", "undelete-cantcreate": "Ne možete vratiti stranicu jer ne postoji stranica s tim nazivom i nije Vam dozvoljeno da je napravite.", "pagedata-title": "Podaci o stranici", + "pagedata-not-acceptable": "Nije pronađen odgovarajući format. Podržane MIME-vrste: $1", "pagedata-bad-title": "Neispravan naslov: $1." } diff --git a/languages/i18n/ca.json b/languages/i18n/ca.json index f54a7e2ee1..7ae92271d6 100644 --- a/languages/i18n/ca.json +++ b/languages/i18n/ca.json @@ -1347,7 +1347,8 @@ "rcfilters-group-results-by-page": "Agrupa els resultats per pàgina", "rcfilters-activefilters": "Filtres actius", "rcfilters-advancedfilters": "Filtres avançats", - "rcfilters-limit-title": "Canvis a mostrar", + "rcfilters-limit-title": "Resultats a mostrar", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|canvi|$1 canvis}}, $2", "rcfilters-date-popup-title": "Període de temps per cercar", "rcfilters-days-title": "Darrers dies", "rcfilters-hours-title": "Hores recents", @@ -1453,7 +1454,8 @@ "rcfilters-liveupdates-button-title-off": "Mostra els nous canvis al moment", "rcfilters-watchlist-markseen-button": "Marca tots els canvis com a vistos", "rcfilters-watchlist-edit-watchlist-button": "Editeu la vostra llista de pàgines seguides", - "rcfilters-target-page-placeholder": "Selecciona una pàgina", + "rcfilters-preference-label": "Amaga la versió millorada de Canvis recents", + "rcfilters-target-page-placeholder": "Escriviu el nom d’una pàgina", "rcnotefrom": "A sota hi ha {{PLURAL:$5|el canvi|els canvis}} a partir de <strong>$3, $4</strong> (fins a <strong>$1</strong>).", "rclistfromreset": "Reinicialitza la selecció de data", "rclistfrom": "Mostra els canvis nous des de $3, $2", @@ -1686,6 +1688,8 @@ "uploadstash-thumbnail": "mostra una miniatura", "uploadstash-bad-path": "El camí no existeix.", "uploadstash-bad-path-invalid": "El camí no és vàlid.", + "uploadstash-bad-path-unknown-type": "El tipus «$1» és desconegut.", + "uploadstash-no-extension": "L’extensió és nul·la.", "invalid-chunk-offset": "El desplaçament del fragment no és vàlid", "img-auth-accessdenied": "Accés denegat", "img-auth-nopathinfo": "Hi manca PATH_INFO.\nEl servidor no està configurat per passar aquesta informació.\nPot estar basat en CGI i no ser compatible amb img_auth.\nConsulteu https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization", @@ -1949,6 +1953,7 @@ "apisandbox-reset": "Neteja", "apisandbox-retry": "Torna a provar", "apisandbox-loading": "S'està carregant la informació del mòdul d'API «$1»...", + "apisandbox-load-error": "S’ha produït un error en carregar la informació del mòdul «$1» de l’API: $2", "apisandbox-no-parameters": "Aquest mòdul API no té paràmetres.", "apisandbox-helpurls": "Enllaços d'ajuda", "apisandbox-examples": "Exemples", @@ -2419,7 +2424,11 @@ "unblocked-id": "S'ha eliminat el blocatge de $1", "unblocked-ip": "[[Special:Contributions/$1|$1]] ha estat desblocat.", "blocklist": "Usuaris blocats", + "autoblocklist": "Blocatges automàtics", "autoblocklist-submit": "Cerca", + "autoblocklist-localblocks": "{{PLURAL:$1|Blocatge automàtic local|Blocatges automàtics locals}}", + "autoblocklist-total-autoblocks": "Nombre total de blocatges automàtics: $1", + "autoblocklist-empty": "La llista de blocatges automàtics és buida.", "ipblocklist": "Usuaris blocats", "ipblocklist-legend": "Cerca un usuari blocat", "blocklist-userblocks": "Amaga blocatges de compte", @@ -3434,6 +3443,7 @@ "fileduplicatesearch-noresults": "No s'ha trobat cap fitxer anomenat «$1».", "specialpages": "Pàgines especials", "specialpages-note-top": "Llegenda", + "specialpages-note-restricted": "* Pàgines especials normals.\n* <span class=\"mw-specialpagerestricted\">Pàgines especials restringides.</span>", "specialpages-group-maintenance": "Informes de manteniment", "specialpages-group-other": "Altres pàgines especials", "specialpages-group-login": "Iniciar sessió / Crear un compte", @@ -3455,6 +3465,9 @@ "tag-filter-submit": "Filtra", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiqueta|Etiquetes}}]]: $2)", "tag-mw-contentmodelchange": "canvi de model de contingut", + "tag-mw-new-redirect": "Redirecció nova", + "tag-mw-blank": "Buidament", + "tag-mw-replace": "Substitució", "tags-title": "Etiquetes", "tags-intro": "Aquesta pàgina llista les etiquetes amb què el programari pot marcar una modificació, i el seu significat.", "tags-tag": "Nom de l'etiqueta", @@ -3591,6 +3604,8 @@ "logentry-delete-delete": "$1 {{GENDER:$2|ha esborrat}} la pàgina $3", "logentry-delete-delete_redir": "$1 {{GENDER:$2|ha esborrat}} la redirecció $3 sobreescrivint-la", "logentry-delete-restore": "$1 {{GENDER:$2|ha restaurat}} la pàgina $3 ($4)", + "restore-count-revisions": "{{PLURAL:$1|Una revisió|$1 revisions}}", + "restore-count-files": "{{PLURAL:$1|Un fitxer|$1 fitxers}}", "logentry-delete-event": "$1 {{GENDER:$2|ha canviat}} la visibilitat {{PLURAL:$5|d'un esdeveniment al registre|de $5 esdeveniments al registre}} de $3: $4", "logentry-delete-revision": "$1 {{GENDER:$2|ha canviat}} la visibilitat {{PLURAL:$5|d'una revisió|de $5 revisions}} a la pàgina $3: $4", "logentry-delete-event-legacy": "$1 {{GENDER:$2|ha canviat}} la visibilitat d'esdeveniments al registre de $3", @@ -3737,6 +3752,7 @@ "mediastatistics": "Estadístiques dels multimèdia", "mediastatistics-summary": "Les estadístiques sobre els tipus de fitxers pujats. Això només inclou la versió més recent d'un fitxer. S'exclouen les versions antigues o eliminades dels fitxers.", "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)", + "mediastatistics-bytespertype": "Mida de fitxer total d’aquesta secció: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3 %).", "mediastatistics-allbytes": "Mida de fitxer total de tots els fitxers {{PLURAL:$1|$1 byte|$1 bytes}} ($2).", "mediastatistics-table-mimetype": "Tipus MIME", "mediastatistics-table-extensions": "Extensions possibles", @@ -3784,6 +3800,7 @@ "special-characters-group-thai": "Tailandès", "special-characters-group-lao": "Laosià", "special-characters-group-khmer": "Khmer", + "special-characters-group-canadianaboriginal": "Sil·labaris canadencs", "special-characters-title-endash": "guió curt", "special-characters-title-emdash": "guió llarg", "special-characters-title-minus": "signe menys", @@ -3909,7 +3926,10 @@ "restrictionsfield-label": "Intervals d'IP permesos:", "revid": "revisió $1", "pageid": "ID de pàgina $1", + "gotointerwiki": "A punt d’abandonar {{SITENAME}}", "gotointerwiki-invalid": "El títol especificat no és vàlid.", + "gotointerwiki-external": "Esteu a punt d’abandonar {{SITENAME}} per a visitar [[$2]], un lloc web diferent.\n\n'''[$1 Continua a $1]'''", + "undelete-cantedit": "Com que no podeu editar aquesta pàgina, no en podeu desfer la supressió.", "pagedata-title": "Dades de la pàgina", "pagedata-bad-title": "Títol no vàlid: $1" } diff --git a/languages/i18n/ce.json b/languages/i18n/ce.json index 22b466fa4a..432fdaf15e 100644 --- a/languages/i18n/ce.json +++ b/languages/i18n/ce.json @@ -1165,6 +1165,8 @@ "rcfilters-activefilters": "Жигара литтарш", "rcfilters-advancedfilters": "Шуьйра литтарш", "rcfilters-limit-title": "Гойту хийцамаш", + "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|хийцам}}, $2", + "rcfilters-date-popup-title": "Лахарна хан", "rcfilters-days-title": "ТӀеххьара денош", "rcfilters-hours-title": "ТӀеххьара сахьташ", "rcfilters-days-show-days": "$1 {{PLURAL:$1|де}}", @@ -1247,6 +1249,7 @@ "rcfilters-liveupdates-button": "Авто-карлаяккха", "rcfilters-liveupdates-button-title-off": "Керла хийцамаш ма-бинехь гайта", "rcfilters-preference-label": "Керла хийцамийн дика кечйина верси къайлаяккха", + "rcfilters-preference-help": "2017 шеран интерфейсан редизайн а, оцу хенахь дуьйна тӀетоьхна гӀирсаш а къайлайоху.", "rcnotefrom": "Лахахь гайтина тӀера <strong>$2</strong> (хийцамаш <strong>$1</strong> кӀезиг).", "rclistfromreset": "Терахь харжар дӀадаккха", "rclistfrom": "Гайта хийцам {{CURRENTYEAR}} шеран {{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTTIME}} бина болу", @@ -2244,7 +2247,7 @@ "tooltip-n-mainpage-description": "Коьрта агӀона дехьа гӀо", "tooltip-n-portal": "Оцу кхолламах, мичахь хlу йу лаьташ а хlудалур ду шуьга", "tooltip-n-currentevents": "ДӀаоьхуш болу хаамашна могӀам", - "tooltip-n-recentchanges": "ТӀаьххьаралера хийцаман могӀам", + "tooltip-n-recentchanges": "ТӀаьххьара хийцамийн могӀам", "tooltip-n-randompage": "Хьажа цахууш нисйеллачу агlоне", "tooltip-n-help": "ГӀоде меттиг", "tooltip-t-whatlinkshere": "ХӀокху агӀонан тӀе хьажийна йолу массо агӀонийн могӀам", @@ -2489,6 +2492,7 @@ "exif-focalplaneresolutionunit": "Магоран фокалан дустар", "exif-sensingmethod": "Сенсоран тайп", "exif-filesource": "Файлан хьост", + "exif-scenetype": "Сценан тайпа", "exif-customrendered": "Кхин тӀе кечдар", "exif-exposuremode": "Сурт доккхуш йолу серлон хьал харжар", "exif-whitebalance": "Къайн баланс", @@ -2823,7 +2827,9 @@ "tag-filter": "[[Special:Tags|Билгалонаш]] луьттург:", "tag-filter-submit": "Литта", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Билгало|Билгалонаш}}]]: $2)", + "tag-mw-new-redirect": "Керла дӀасахьажорг", "tag-mw-rollback": "Юхаяккха", + "tag-mw-undo": "цаоьшу", "tags-title": "Билгалонаш", "tags-intro": "ХӀокху агӀона чохь гойтуш бу билгалонийн могӀам царца программин латторо билгал доху нисдарш, кхин билгалонийн маьӀна а.", "tags-tag": "Билгалона цӀе", diff --git a/languages/i18n/ckb.json b/languages/i18n/ckb.json index ac6f15f624..59e0b3749b 100644 --- a/languages/i18n/ckb.json +++ b/languages/i18n/ckb.json @@ -1702,7 +1702,7 @@ "addwatch": "بیخە سەر لیستی چاودێری", "addedwatchtext": "پەڕەی «[[:$1]]» خرایە ژێر [[Special:Watchlist|پێرستی چاودێری]]یەکەت.\nگۆڕانکارییەکانی داھاتووی ئەم پەڕەیە و پەڕەی وتووێژەکەی، لەوێدا پێرست دەکرێت.", "removewatch": "لەلیستی چاودێری لایبە", - "removedwatchtext": "پەڕەی «[[:$1]]» لە [[Special:Watchlist|لیستی چاودێریەکەت]] لابرا.", + "removedwatchtext": "پەڕەی «[[:$1]]» و لێدوانەکەی لە [[Special:Watchlist|لیستی چاودێریەکەت]] لابران.", "removedwatchtext-short": "پەڕەی «$1» لە پێڕستی چاودێریت لابرا.", "watch": "چاودێری بکە", "watchthispage": "ئەم پەڕەیە بخە ژێر چاودێری", @@ -2794,6 +2794,7 @@ "tags-actions-header": "کردەوەکان", "tags-active-yes": "بەڵێ", "tags-active-no": "نا", + "tags-source-extension": "پێناسی نەرمامێر", "tags-edit": "دەستکاری", "tags-delete": "سڕینەوە", "tags-activate": "کارپێکردن", diff --git a/languages/i18n/cs.json b/languages/i18n/cs.json index ba320509cd..aea4564f82 100644 --- a/languages/i18n/cs.json +++ b/languages/i18n/cs.json @@ -1035,6 +1035,7 @@ "timezoneregion-indian": "Indický oceán", "timezoneregion-pacific": "Tichý oceán", "allowemail": "Dovolit ostatním uživatelům posílat mi e-maily", + "email-allow-new-users-label": "Povolit e-maily od zcela nových uživatelů", "email-blacklist-label": "Znemožnit těmto uživatelům posílat mi e-maily:", "prefs-searchoptions": "Vyhledávání", "prefs-namespaces": "Jmenné prostory", @@ -1204,6 +1205,7 @@ "right-siteadmin": "Zamykání a odemykání databáze", "right-override-export-depth": "Exportovat stránky včetně odkazovaných stránek až do hloubky 5", "right-sendemail": "Odesílání e-mailů ostatním uživatelům", + "right-sendemail-new-users": "Posílání e-mailů uživatelům bez zaznamenaných činností", "right-managechangetags": "Vytváření a (de)aktivace [[Special:Tags|značek]]", "right-applychangetags": "Přidávání [[Special:Tags|značek]] k vlastním změnám", "right-changetags": "Přidávání libovolných [[Special:Tags|značek]] na jednotlivé revize a protokolovací záznamy a jejich odebírání", @@ -1437,6 +1439,11 @@ "rcfilters-watchlist-showupdated": "Změny stránek, které jste od provedení změn nenavÅ¡tívili, jsou zobrazeny <strong>tučně</strong> s vyplněnou značkou.", "rcfilters-preference-label": "Skrýt vylepÅ¡enou verzi posledních změn", "rcfilters-preference-help": "Zruší novou podobu rozhraní zavedenou v roce 2017 a vÅ¡echny nástroje přidané od té doby.", + "rcfilters-filter-showlinkedfrom-label": "Zobrazit změny stránek, na které se odkazuje", + "rcfilters-filter-showlinkedfrom-option-label": "Zobrazit změny stránek odkazovaných <strong>ZE</strong> stránky", + "rcfilters-filter-showlinkedto-label": "Zobrazit změny stránek, které sem odkazují", + "rcfilters-filter-showlinkedto-option-label": "Zobrazit změny stránek odkazujících <strong>NA</strong> stránku", + "rcfilters-target-page-placeholder": "Zadejte název stránky", "rcnotefrom": "Níže {{PLURAL:$5|je změna|jsou změny}} od <strong>$3, $4</strong> ({{PLURAL:$1|zobrazena|zobrazeny|zobrazeno}} nejvýše <strong>$1</strong>).", "rclistfromreset": "Obnovit výběr data", "rclistfrom": "Ukázat nové změny, počínaje od $2, $3", @@ -1481,7 +1488,7 @@ "recentchangeslinked-feed": "Související změny", "recentchangeslinked-toolbox": "Související změny", "recentchangeslinked-title": "Související změny pro stránku „$1“", - "recentchangeslinked-summary": "Níže je seznam nedávných změn stránek odkazovaných ze zadané stránky (nebo patřících do dané kategorie). VaÅ¡e [[Special:Watchlist|sledované stránky]] jsou '''zvýrazněny'''.", + "recentchangeslinked-summary": "Vložením názvu stránky uvidíte změny stránek, které na stránku odkazují nebo na které stránka odkazuje. (Pro stránky zařazené do kategorie vložte Kategorie:Název kategorie.) Vámi [[Special:Watchlist|sledované stránky]] jsou <strong>zvýrazněny</strong>.", "recentchangeslinked-page": "Název stránky:", "recentchangeslinked-to": "Zobrazit změny na stránkách odkazujících na zadanou stránku", "recentchanges-page-added-to-category": "Stránka [[:$1]] zařazena do kategorie", @@ -3322,6 +3329,8 @@ "autosumm-blank": "Stránka vyprázdněna", "autosumm-replace": "Obsah stránky nahrazen textem „$1“", "autoredircomment": "Přesměrování na [[$1]]", + "autosumm-removed-redirect": "ZruÅ¡eno přesměrování na [[$1]]", + "autosumm-changed-redirect-target": "Cíl přesměrování změněn z [[$1]] na [[$2]]", "autosumm-new": "Založena nová stránka s textem „$1“", "autosumm-newblank": "Založena prázdná stránka", "size-bytes": "$1 {{PLURAL:$1|bajt|bajty|bajtů}}", @@ -3499,9 +3508,20 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Značka|Značky}}]]: $2)", "tag-mw-contentmodelchange": "změna modelu obsahu", "tag-mw-contentmodelchange-description": "Editace, které [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel mění obsah modelu] stránky", + "tag-mw-new-redirect": "Nové přesměrování", + "tag-mw-new-redirect-description": "Editace, které zakládají nové přesměrování nebo mění stránku na přesměrování", + "tag-mw-removed-redirect": "ZruÅ¡eno přesměrování", + "tag-mw-removed-redirect-description": "Editace, které mění dosavadní přesměrování na nepřesměrování", + "tag-mw-changed-redirect-target": "Změněn cíl přesměrování", + "tag-mw-changed-redirect-target-description": "Editace, které mění cíl přesměrování", "tag-mw-blank": "Vyprázdnění", "tag-mw-blank-description": "Editace, které vyprázdní stránku", "tag-mw-replace": "Nahrazeno", + "tag-mw-replace-description": "Editace, které odstraňují více než 90 % obsahu stránky", + "tag-mw-rollback": "Rychlý revert", + "tag-mw-rollback-description": "Editace, jimiž byly předchozí editace vráceny zpět pomocí rychlého revertu", + "tag-mw-undo": "Vrácení zpět", + "tag-mw-undo-description": "Editace, jimiž se ruší předchozí editace pomocí odkazu „zruÅ¡it editaci“", "tags-title": "Značky", "tags-intro": "Tato stránka obsahuje seznam značek, kterými může software označovat jednotlivé editace, a jejich významy.", "tags-tag": "Název značky", @@ -3691,8 +3711,8 @@ "logentry-newusers-autocreate": "Automaticky byl {{GENDER:$2|založen}} účet $1", "logentry-protect-move_prot": "$1 {{GENDER:$2|přesunul|přesunula}} nastavení zámků ze stránky $4 na stránku $3", "logentry-protect-unprotect": "$1 {{GENDER:$2|odemknul|odemknula}} stránku $3", - "logentry-protect-protect": "$1 {{GENDER:$2|zamknul|zamknula}} stránku $3 $4", - "logentry-protect-protect-cascade": "$1 {{GENDER:$2|zamknul|zamknula}} stránku $3 $4 [kaskádovým zámkem]", + "logentry-protect-protect": "$1 {{GENDER:$2|zamkl|zamkla|zamkl(a)}} stránku $3 $4", + "logentry-protect-protect-cascade": "$1 {{GENDER:$2|zamkl|zamkla|zamkl(a)}} stránku $3 $4 [kaskádovým zámkem]", "logentry-protect-modify": "$1 {{GENDER:$2|změnil|změnila}} úroveň ochrany stránky $3 $4", "logentry-protect-modify-cascade": "$1 {{GENDER:$2|změnil|změnila}} úroveň ochrany stránky $3 $4 [kaskádový zámek]", "logentry-rights-rights": "$1 {{GENDER:$2|změnil|změnila}} členství {{GENDER:$6|uživatele|uživatelky}} $3 ve skupinách z $4 na $5", diff --git a/languages/i18n/csb.json b/languages/i18n/csb.json index e3240db351..3d71fbe04d 100644 --- a/languages/i18n/csb.json +++ b/languages/i18n/csb.json @@ -431,6 +431,7 @@ "minoredit": "To je drobnô edicjô", "watchthis": "Ùzérôj", "savearticle": "Zapiszë artikel", + "publishchanges": "Òpùblikùj zmianë", "preview": "Pòdzérk", "showpreview": "Wëskrzëni pòdzérk", "showdiff": "Wëskrzëni zjinaczi", @@ -670,6 +671,70 @@ "recentchanges-label-plusminus": "Zjinaczonô wiôlgòsc starnë (lëczba bajtów)", "recentchanges-legend-heading": "<strong>Légenda:</strong>", "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (òbaczë téż [[Special:NewPages|lëstã nowëch strón]])", + "rcfilters-legend-heading": "<strong>Ùżëté skrócënczi:</strong>", + "rcfilters-other-review-tools": "Jiné nôrzãdza przezéru zjinaków", + "rcfilters-group-results-by-page": "Grëpòwanié pòdle strón.", + "rcfilters-activefilters": "Aktiwné filtrë", + "rcfilters-limit-title": "Lëczba wëników do wëskrzënieniô", + "rcfilters-date-popup-title": "Cząd czasu dlô szëkbë.", + "rcfilters-days-title": "Slédnëch dni", + "rcfilters-hours-title": "Slédnëch gòdzënów", + "rcfilters-quickfilters-placeholder-title": "Ni môsz jesz zapisónëch filtrów", + "rcfilters-quickfilters-placeholder-description": "Abë zapisac nastôwë filtrów i ùżëwac jich pózni, klikni ikonkã załóżczi w pòlu aktiwnëch filtrów, jaczé nachôdô sã niżi.", + "rcfilters-savedqueries-defaultlabel": "Zapisóné filtrë", + "rcfilters-savedqueries-add-new-title": "Zapiszë aktualné ùstôwë filtrów.", + "rcfilters-clear-all-filters": "Wëczëszczë filtrë", + "rcfilters-search-placeholder": "Fitruj nowé zjinaczi (ùżij do te menu abò wëszukôj pòdle pòzwë filtra)", + "rcfilters-filterlist-title": "Filtrë", + "rcfilters-filterlist-feedbacklink": "Napiszë, jak cë sã widzą te nowé nôrzãdza filtrowaniô.", + "rcfilters-highlightbutton-title": "Pòdsztrichnąc wëniczi", + "rcfilters-filtergroup-authorship": "Aùtorstwò wkładu", + "rcfilters-filter-editsbyself-label": "Zjinaczi, chtërne zrobił jem jô.", + "rcfilters-filter-editsbyself-description": "Twòje gwôsné dzejania.", + "rcfilters-filter-editsbyother-label": "Zjinaczi, chtërne zrobilë jinszi brëkòwnicë.", + "rcfilters-filter-editsbyother-description": "Wszëtczé zjinaczi, òkróm twòjich.", + "rcfilters-filtergroup-userExpLevel": "Registracjô brëkòwnika i jegò doswiôdczenié", + "rcfilters-filter-user-experience-level-registered-label": "Zaregistrowóni", + "rcfilters-filter-user-experience-level-registered-description": "Zalogòwóni brëkòwnicë", + "rcfilters-filter-user-experience-level-unregistered-label": "Niezaregistrowóni", + "rcfilters-filter-user-experience-level-unregistered-description": "Niezalogòwóni brëkòwnicë", + "rcfilters-filter-user-experience-level-newcomer-label": "Pòczãtniczi", + "rcfilters-filter-user-experience-level-newcomer-description": "Zalogòwóni brëkòwnicë, jaczi mają mni jak 10 edicjów abò mni jak 4 dni aktiwnoscë.", + "rcfilters-filter-user-experience-level-learner-label": "Karno ùczącëch sã", + "rcfilters-filter-user-experience-level-learner-description": "Zaregistrowóni brëkòwnicë z doswiôdczenim wikszim niż „Pòczãtniczi”, ale miészim niż „Doswiôdczony brëkòwnicë”.", + "rcfilters-filter-user-experience-level-experienced-label": "Doswiôdczony brëkòwnicë.", + "rcfilters-filter-user-experience-level-experienced-description": "Zaregistrowóni brëkòwnicë, chtërny mają wicy jak 500 edicjów i 30 dni aktiwnoscë.", + "rcfilters-filter-bots-description": "Zjinaczi wëkònóné przë pòmòcë aùtomaticznëch nôrzãdzów.", + "rcfilters-filter-humans-label": "Człowiek (nie bòt)", + "rcfilters-filter-humans-description": "Zjinaczi dokònany przez lëdzy", + "rcfilters-filter-minor-label": "Drobné zjinaczi", + "rcfilters-filter-minor-description": "Zjinaczi, chtërne aùtor nacéchòwôł jakò „drobné”.", + "rcfilters-filter-major-description": "Zjinaczi nie nacéchòwóné jakò „drobné”.", + "rcfilters-filtergroup-watchlist": "Starnë z lëstë ùzéraniô", + "rcfilters-filter-watchlist-watched-label": "Z lëstë ùzéraniô", + "rcfilters-filter-watchlist-watched-description": "Zjinaczi na starnach, jaczé môsz na lësce ùzéraniô.", + "rcfilters-filter-watchlist-watchednew-label": "Nowé zjinaczi na starnach z lëstë ùzéraniô", + "rcfilters-filter-watchlist-watchednew-description": "Zjinaczi na starnach z lëstë ùzéraniô, jaczich òd czasu nëch edicjów jesz nie òdwiedzył jes.", + "rcfilters-filter-watchlist-notwatched-label": "Leno spòza lëstë ùzéraniô", + "rcfilters-filter-watchlist-notwatched-description": "Wszëtkò òkróm zjinaków na starnach z twòji lëstë ùzéraniô.", + "rcfilters-filtergroup-changetype": "Ôrt zjinaków", + "rcfilters-filter-pageedits-label": "Edicje starnë", + "rcfilters-filter-pageedits-description": "Edicje zamkłoscë, starnów diskùsje, òpisënków kategòrii...", + "rcfilters-filter-newpages-label": "Ùsôdzanié starnów", + "rcfilters-filter-newpages-description": "Zjinaczi, chtërne ùsôdzają nowé starnë.", + "rcfilters-filter-categorization-label": "Zjinaczi kategòriów", + "rcfilters-filter-categorization-description": "Dodanié abò rëmniãcé starnë z kategòrie.", + "rcfilters-filter-logactions-label": "Dzejania notérowóny w registru", + "rcfilters-filter-logactions-description": "Dzejania sprôwników, ùsôdzanié kònt, rëmanié starnów, sélanié lopków...", + "rcfilters-filtergroup-lastRevision": "Òstatné wersje.", + "rcfilters-filter-lastrevision-label": "Nônowszô wersjô", + "rcfilters-filter-lastrevision-description": "Leno nônowszi zjinaczi dlô kòżdi starnë.", + "rcfilters-filter-previousrevision-label": "Wersje jiné niż nônowszô.", + "rcfilters-filter-previousrevision-description": "Wszëtczé edicje, jaczé nie są nônowszą wersją starnë.", + "rcfilters-view-tags": "Edicje ze znakòwnikama przë zjinakach", + "rcfilters-view-tags-tooltip": "Przefiltruj wëniczi pòdle znakòwników przë zjinakach", + "rcfilters-liveupdates-button": "Òdnôwianié na żëwò", + "rcfilters-liveupdates-button-title-off": "Wëskrzëniôj nowé zjinaczi zarôzkù pò tim jak sã pòjawią", "rcnotefrom": "Niżi {{PLURAL:$5|je zjinaka|są zjinaczi}} {{PLURAL:$5|zrobionô|zrobioné}} pò <strong>$3, $4</strong> (nie wicy jak '''$1''' pozycëji).", "rclistfrom": "Pòkażë nowé zmianë òd $3 $2", "rcshowhideminor": "$1 môłé zmianë", @@ -872,7 +937,7 @@ "rollbackfailed": "Nie szło copnąc zmianë", "alreadyrolled": "Ni mòże copnąc slédny edicëji starnë [[:$1]], chtërny ùsôdzcą je [[User:$2|$2]] ([[User talk:$2|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nchtos jiny ju zeditowôł starnã abò copnął zmianë.\n\nSlédnym ùsódzcą starnë bëł [[User:$3|$3]] ([[User talk:$3|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).", "revertpage": "Edicje brëkòwnika [[Special:Contributions/$2|$2]] ([[User talk:$2|diskùsjô]]) òstałë òdrzucóné. Aùtorã przëwrócóny wersji je [[User:$1|$1]].", - "rollback-success": "Copniãto edicëje{{GENDER:$3|brëkòwnmika|brëkòwniczczi}} $1;\ndoprowpdzono nazôd slédną wersëjã ùsôdzcë {{GENDER:$4|$2}}.", + "rollback-success": "Copniãto edicje {{GENDER:$3|brëkòwnika|brëkòwniczczi}} $1;\ndoprowadzóno nazôd slédną wersëjã ùsôdzcë {{GENDER:$4|$2}}.", "rollback-success-notify": "Edicje brëkòwnika $1 òstałë òdrzucóné; \nòsta przëwrócónô òstatnô wersjô, aùtorã chtërny je $2. [$3 Pòkażë zjinaczi]", "protectlogpage": "Zazychrowóné", "protectedarticle": "zazychrowónô [[$1]]", @@ -1175,6 +1240,9 @@ "specialpages": "Specjalné starnë", "tag-filter": "Filtr [[Special:Tags|znakòwników]]:", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Znakòwnik|Znakòwniczi}}]]: $2)", + "tag-mw-blank": "Rëmniãcé całi zamkłoscë starnë", + "tag-mw-rollback": "Copniãcé zjinaków", + "tags-title": "Znakòwniczi", "tags-active-yes": "Jo", "tags-active-no": "Ni", "tags-hitcount": "{{PLURAL:$1|zjinaka|zjinaczi|zjinaków}}", @@ -1188,6 +1256,8 @@ "revdelete-content-hid": "zamkłosc òsta zataconô", "revdelete-restricted": "nastôwi ògrańczenia dlô sprôwników", "revdelete-unrestricted": "rëmôj ògrańczenia dlô sprôwników", + "logentry-block-block": "$1 {{GENDER:$2|zablokòwôł}} {{GENDER:$4|$3}}; cząd blokadë: $5 $6", + "logentry-suppress-block": "$1 {{GENDER:$2|zablokòwôł}} {{GENDER:$4|$3}}; cząd blokadë: $5 $6", "logentry-move-move": "$1 {{GENDER:$2|przeniós|przeniosła}} starnã $3 do $4", "logentry-move-move-noredirect": "$1 {{GENDER:$2|przeniós|przeniosła}} starnã $3 na $4, bez òstôwienia przczerowaniô pòd stôrim titlã", "logentry-move-move_redir": "$1 {{GENDER:$2|przeniós|przeniosła}} starnã $3 na $4 w plac przeczérowaniô", diff --git a/languages/i18n/da.json b/languages/i18n/da.json index ade8fedb24..cce61a4ff9 100644 --- a/languages/i18n/da.json +++ b/languages/i18n/da.json @@ -64,7 +64,8 @@ "SimmeD", "BoBrandt", "R12ntech", - "Joedalton" + "Joedalton", + "EeveeSylveon" ] }, "tog-underline": "Understreg henvisninger:", @@ -1416,10 +1417,10 @@ "rcfilters-filter-categorization-description": "Optegnelser af at sider bliver tilføjet til eller fjernet fra kategorier.", "rcfilters-filter-logactions-label": "Loggede handlinger", "rcfilters-filter-logactions-description": "Administrative handlinger, kontooprettelser, sidesletninger, uploads...", - "rcfilters-filtergroup-lastRevision": "Sidste revision", + "rcfilters-filtergroup-lastRevision": "Seneste revisioner", "rcfilters-filter-lastrevision-label": "Seneste revision", - "rcfilters-filter-lastrevision-description": "Den nyeste ændring af en side.", - "rcfilters-filter-previousrevision-label": "Tidligere revisioner", + "rcfilters-filter-lastrevision-description": "Kun den seneste ændring af en side.", + "rcfilters-filter-previousrevision-label": "Ikke den seneste revision", "rcfilters-filter-previousrevision-description": "Alle ændringer som ikke er »seneste revision«.", "rcfilters-filter-excluded": "Ekskluderet", "rcfilters-view-tags": "Mærkede redigeringer", @@ -1427,6 +1428,7 @@ "rcfilters-watchlist-markseen-button": "Marker alle ændringer som set", "rcfilters-watchlist-edit-watchlist-button": "Rediger din liste med overvÃ¥gede sider", "rcfilters-preference-label": "Skjul den forbedrede verson af Seneste ændringer", + "rcfilters-target-page-placeholder": "Indtast et sidenavn", "rcnotefrom": "Nedenfor er op til '''$1''' {{PLURAL:$5|ændring|ændringer}} siden '''$2''' vist.", "rclistfromreset": "Nulstil datovalg", "rclistfrom": "Vis nye ændringer startende fra den $3 kl. $2", @@ -1545,6 +1547,7 @@ "uploaddisabledtext": "Oplægning af filer er deaktiveret.", "php-uploaddisabledtext": "Oplægning af filer er forhindret i PHP. Tjek indstillingen for file_uploads.", "uploadscripted": "Denne fil indeholder HTML eller script-kode, der i visse tilfælde can fejlfortolkes af en browser.", + "uploaded-href-unsafe-target-svg": "Fandt href til usikre data: URI-mÃ¥l <code><$1 $2=\"$3\"></code> i den overførte SVG-fil.", "uploadscriptednamespace": "Denne SVG-fil indeholder et ulovligt navnerum \"<nowiki>$1</nowiki>\"", "uploadinvalidxml": "XML i den uploadede fil kunne ikke tolkes.", "uploadvirus": "Denne fil indeholder en virus! Virusnavn: $1", @@ -3323,6 +3326,12 @@ "tag-filter": "[[Special:Tags|Tag]]filter:", "tag-filter-submit": "Filtrér", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)", + "tag-mw-new-redirect": "Ny omdirigering", + "tag-mw-removed-redirect": "Fjernede omdirigering", + "tag-mw-changed-redirect-target": "OmdigeringsmÃ¥l ændret", + "tag-mw-blank": "Sidetømning", + "tag-mw-replace": "Erstattet", + "tag-mw-rollback": "Tilbagerulning", "tags-title": "Tags", "tags-intro": "Denne side oplister de tags som programmet kan mærke en redigering med, og deres betydning.", "tags-tag": "Tagnavn", diff --git a/languages/i18n/de.json b/languages/i18n/de.json index e82e4e160c..e9d3e62a61 100644 --- a/languages/i18n/de.json +++ b/languages/i18n/de.json @@ -1089,6 +1089,7 @@ "timezoneregion-indian": "Indischer Ozean", "timezoneregion-pacific": "Pazifischer Ozean", "allowemail": "Anderen Benutzern erlauben, E-Mails an mich zu senden.", + "email-allow-new-users-label": "E-Mails von sehr neuen Benutzern erlauben", "email-blacklist-label": "Diesen Benutzern das Versenden von E-Mails an mich verbieten:", "prefs-searchoptions": "Suche", "prefs-namespaces": "Namensräume", @@ -1261,6 +1262,7 @@ "right-siteadmin": "Datenbank sperren und entsperren", "right-override-export-depth": "Exportiere Seiten einschließlich verlinkter Seiten bis zu einer Tiefe von 5", "right-sendemail": "E-Mails an andere Benutzer senden", + "right-sendemail-new-users": "E-Mails an Benutzer ohne Eintrag im Logbuch senden", "right-managechangetags": "[[Special:Tags|Markierungen]] erstellen und (de)aktivieren", "right-applychangetags": "[[Special:Tags|Markierungen]] zusammen mit den Änderungen anwenden", "right-changetags": "Beliebige [[Special:Tags|Markierungen]] zu einzelnen Versionen und Logbucheinträgen hinzufügen und entfernen", @@ -1496,10 +1498,10 @@ "rcfilters-preference-label": "Die verbesserte Version der Letzten Änderungen ausblenden", "rcfilters-preference-help": "Macht die Neugestaltung der Oberfläche aus dem Jahr 2017 und alle seitdem hinzugefügten Werkzeuge wieder rückgängig.", "rcfilters-filter-showlinkedfrom-label": "Änderungen auf Seiten anzeigen, die verlinkt sind von", - "rcfilters-filter-showlinkedfrom-option-label": "Änderungen auf Seiten anzeigen, die VON einer Seite verlinkt sind.", - "rcfilters-filter-showlinkedto-label": "Änderungen auf Seiten anzeigen, die verlinkt sind auf:", - "rcfilters-filter-showlinkedto-option-label": "Änderungen auf Seiten anzeigen, die AUF eine Seite verlinkt sind.", - "rcfilters-target-page-placeholder": "Eine Seite auswählen", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Seiten</strong>, die <strong>von</strong> der ausgewählten Seite <strong>verlinkt</strong> sind", + "rcfilters-filter-showlinkedto-label": "Änderungen auf Seiten anzeigen, die verlinken auf", + "rcfilters-filter-showlinkedto-option-label": "<strong>Seiten</strong>, die <strong>auf</strong> die ausgewählte Seite <strong>verlinken</strong>", + "rcfilters-target-page-placeholder": "Einen Seitennamen eingeben", "rcnotefrom": "Angezeigt {{PLURAL:$5|wird die Änderung|werden die Änderungen}} seit <strong>$3, $4</strong> (max. <strong>$1</strong> Einträge).", "rclistfromreset": "Datumsauswahl zurücksetzen", "rclistfrom": "Nur Änderungen seit $3, $2 Uhr zeigen.", @@ -1545,7 +1547,7 @@ "recentchangeslinked-feed": "Änderungen an verlinkten Seiten", "recentchangeslinked-toolbox": "Änderungen an verlinkten Seiten", "recentchangeslinked-title": "Änderungen an Seiten, die von „$1“ verlinkt sind", - "recentchangeslinked-summary": "Diese Spezialseite listet die letzten Änderungen an den verlinkten Seiten auf (bzw. an in dieser Kategorie enthaltenen Kategorien). Seiten auf deiner [[Special:Watchlist|Beobachtungsliste]] sind <strong>fett</strong> dargestellt.", + "recentchangeslinked-summary": "Gib einen Seitennamen ein, um Änderungen auf Seiten zu sehen, die auf oder von dieser Seite verlinkt sind. Um Mitglieder einer Kategorie zu sehen, gib „Kategorie:''Name der Kategorie''“ ein. Änderungen an Seiten auf [[Special:Watchlist|deiner Beobachtungsliste]] sind <strong>fett</strong> hervorgehoben.", "recentchangeslinked-page": "Seite:", "recentchangeslinked-to": "Zeige nur Änderungen an Seiten, die auf diese Seite verlinken", "recentchanges-page-added-to-category": "[[:$1]] zur Kategorie hinzugefügt", @@ -3561,6 +3563,8 @@ "tag-mw-replace-description": "Bearbeitungen, die mehr als 90 % des Inhalts einer Seite entfernen.", "tag-mw-rollback": "Zurücksetzung", "tag-mw-rollback-description": "Bearbeitungen, die frühere Bearbeitungen mithilfe des Zurücksetzen-Links rückgängig machen.", + "tag-mw-undo": "Rückgängigmachung", + "tag-mw-undo-description": "Bearbeitungen, die frühere Versionen mit dem Link „Rückgängig machen“ zurücksetzen", "tags-title": "Markierungen", "tags-intro": "Diese Seite zeigt alle Markierungen, die für Bearbeitungen verwendet wurden, sowie deren Bedeutung. \n\nBei entsprechender Einstellung können die Missbrauchfilter beliebige Markierungen in die Versionsgeschichte setzen. Man kann die Versionsgeschichte dann nach den Markierungen filtern.", "tags-tag": "Markierungsname", diff --git a/languages/i18n/diq.json b/languages/i18n/diq.json index e70d1c8e21..bbaa3bce44 100644 --- a/languages/i18n/diq.json +++ b/languages/i18n/diq.json @@ -305,7 +305,7 @@ "nstab-main": "Perre", "nstab-user": "Pera karberi", "nstab-media": "Perra medya", - "nstab-special": "Pera hısusi", + "nstab-special": "Perra xısusiye", "nstab-project": "Perra proji", "nstab-image": "Dosya", "nstab-mediawiki": "Mesac", diff --git a/languages/i18n/dty.json b/languages/i18n/dty.json index 2904a9a3fd..dc25eb0565 100644 --- a/languages/i18n/dty.json +++ b/languages/i18n/dty.json @@ -1032,6 +1032,7 @@ "upload": "फाइल अपलोड गरऽ", "uploadbtn": "फाइल अपलोड गर्न्या", "upload-recreate-warning": "'''चेतावनी: त्यस नाममी रह्याका फाइलहरू सारियाको या हटायाको छ।'''\n\nयै पानाको सारियाको र हटायाको लग तमरो सहजताको लागि दियाको छ।", + "uploadlogpage": "अपलोड लग", "filedesc": "सारांश:", "large-file": "यो सिफारिस गर्याछकि फाइलहरूको आकार $1 भन्दा ठूला हुनु हुँदैन;\nयै फाइलको आकार $2 छ ।", "emptyfile": "तमीले अपलोड गर्याको फाइल रित्तो छ ।\nयो फाइल नाम गलत राख्याका कारणले भयाको हुनसकन्छ\nयो फाइल साँच्चै अपलोड गद्दे कुरडीमी निश्चित होइजाओ ।", @@ -1073,6 +1074,7 @@ "nolinkstoimage": "यो चित्रसित लिंकभयाकि कोइ पाना नाइथी", "morelinkstoimage": "यै फाइलको [[Special:WhatLinksHere/$1|थप लिंकहरू]] हेर ।", "sharedupload-desc-here": "यो फाइल $1 बठे हो र और परियोजनाहरू बठे पन प्रयोग गद्द सकिन्याछ । \nताखाइ यैको [$2 फ़ाइल विवरण पानो]मि रयाका विवरण तल्तिर दियाको छ।", + "filepage-nofile": "येइ नाउँ को कोइ लै फाइल नाइथिन।", "upload-disallowed-here": "तमलाई यो फाइल अधिलेखन गद्द नाइसक्का ।", "filedelete-intro-old": "तमी <strong>[[Media:$1|$1]]</strong> को संस्करणलाई [$4 $3, $2] हुन्या गरि मेट्ट लाग्याछौ ।", "filedelete-maintenance": "रखरखाव चलिरह्याको हुनाले अस्थायी रुपमी फाइलहरू मेट्ट्या र मेट्याकोलाई पुनर्बहाली गर्न निष्क्रिय गरियाकोछ।", @@ -1120,10 +1122,12 @@ "activeusers-count": "विगत {{PLURAL:$3|दिनमी|$3 दिनहरूमी}} $1 {{PLURAL:$1|सम्पादन गरियो|सम्पादनहरू गरिया}}", "activeusers-from": "यहाँबठे सुरु हुन्या प्रयोगकर्ताहरू धेकाओ:", "activeusers-noresult": "प्रयोगकर्ताहरू भेटियानन्", + "listgrouprights-members": "(सदस्यअनैः सूची)", "mailnologintext": "तमीले अरु प्रयोगकर्तानलाई ईमेल पठाउनको लागि आफु पहिली [[Special:UserLogin|प्रवेश(लगइन)गर्याको]] हुनुपडन्छ र [[Special:Preferences|आफ्नो रोजाइहरूमी]] एउटा वैध ईमेल ठेगाना भयाको हुनुपडन्छ ।", "emailuser": "येइ प्रयोगकर्ता लाई इमेल पठाऽ", "emailpagetext": "तल दियाको फार्मले तमी यै {{GENDER:$1|प्रयोगकर्ता}}लाई इमेल पठाउन सक्द्या हौ । तमीले जो ठेगाना [[Special:Preferences|आफ्नो प्रयोगकर्ता रोजाईहरू]]मी दियाका छियौ त्यो यै इमेललाई \"पठाउने\" को रूपमी आउन्याछ, अतः प्राप्तकर्ता तमीलाई सिधै जवाफ दिनसक्द्याछ ।", "usermaildisabledtext": "यै विकिमी तम और प्रयोगकर्तानलाई ई-मेल पठाउन नाइसक्दा", + "watchlist": "अवलोकनसूची", "mywatchlist": "मेरो ध्यान सूची", "nowatchlist": "तमरो ध्यान सूचीमी कोइ लै सामाग्री नाइथिन् ।", "watchlistanontext": "कृपया तमरो ध्यान सूची हेद्द या सम्पादन गद्द कीलाइ लगइन गर ।", @@ -1154,6 +1158,7 @@ "restriction-type": "अनुमति:", "pagesize": "(अक्षरहरू)", "restriction-edit": "सम्पादन", + "restriction-move": "सारऽ", "undeletepage": "मेट्याका पानाहरू हेद्या र पूर्वरुपमी फर्काउन्या", "undeleterevisions": "$1 {{PLURAL:$1|संशोधन|संशोधनहरू}} संग्रहित", "undeletehistory": "यदि कुनै पानालाई पुन: स्थापन गरायौ भण्या सम्पूर्ण संस्करणहरू इतिहासमी पुन:स्थापन हुन्याछन् ।\nयदि यै नामबठे नयाँ पानो निर्माण भैसक्याको छ भण्या पुन: स्थापित संस्करणहरू पूर्व इतिहासको रुपमी स्थापित हुन्याछन् ।", @@ -1169,12 +1174,21 @@ "tooltip-namespace_association": "कुरडिकानी या विषय नेमस्पेसहरुलाई सम्वन्धित नेमस्पेसको रुपमि लिनकि लेखा सन्दुकमि चिनो लगाइदिय ।", "blanknamespace": "(मुख्य)", "contributions": "{{GENDER:$1|प्रयोगकर्ता}}को योगदान", + "contributions-title": "प्रयोगकर्ता $1 का योगदानअन", "mycontris": "मेरो योगदानहरू", "anoncontribs": "योगदान", "uctop": "(अइलोऽ)", "month": "महिना बठे (लै पैल्ली):", "year": "वर्ष बठे( लौ पैल्ली):", + "sp-contributions-newbies": "नौला खाताअनाः योगदानअन लाई मात्तरी धेकाऽ", + "sp-contributions-uploads": "अपलोडअन", + "sp-contributions-logs": "लगअन", + "sp-contributions-talk": "कुरड़िकाआनी", + "sp-contributions-search": "योगदानअन खिलाइ खोजी अरऽ", + "sp-contributions-username": "आइपी(IP) ठेगान या प्रयोगकर्ता नाउँ:", "sp-contributions-toponly": "नवीनतम संशोधनका सम्पादनहरू मात्र धेकाओ", + "sp-contributions-newonly": "तन सम्पादनअन लाई धेकाऽ जो कि पन्ना सिर्जनाअन हुन", + "sp-contributions-submit": "खोजऽ", "whatlinkshere": "याँखाइ कि जोणीन्छ", "whatlinkshere-title": "$1 सित जोडियाऽ पन्नाअन", "whatlinkshere-page": "पानो", @@ -1197,9 +1211,11 @@ "blocklist": "ब्लक गर्याका प्रयोगकर्ताहरू", "ipblocklist": "ब्लक गर्याका प्रयोगकर्ताहरू", "ipblocklist-legend": "ब्लक गर्याका प्रयोगकर्ताहरू खोज", + "infiniteblock": "अनन्त", "blocklink": "रोक्न्या", "contribslink": "योगदानअन", "block-log-flags-anononly": "नाम नभयाका प्रयोकर्ताहरू मात्र", + "proxyblocker": "प्रोक्सी निषेधक", "proxyblockreason": "तमरो IP ठेगानामी रोक लगायाको छ किनकी यो खुला प्रोक्सी हो ।\nकृपया तमरो इन्टरनेट सेवा प्रदायक या प्राविधिक सहायतासँग सम्पर्क गरीबर यै सुरक्षा समस्याका बारेमी जानकारी गराओ ।", "sorbsreason": "तमरो IP ठेगाना खुल्ला प्रोक्सीको रुपमी DNSBL मा सूचीकरण गरिएको छ यैलाई{{SITENAME}}ले प्रयोगमी ल्यायाको छ।", "sorbs_create_account_reason": "तमरो IP ठेगाना खुल्ला प्रोक्सीको रुपमी DNSBL मी सूचीकरण गरियाको छ यैलाई{{SITENAME}}ले प्रयोगमी ल्यायाको छ ।\nतम खाता खोल्न नाइसक्दा ।", @@ -1231,6 +1247,7 @@ "import-noarticle": "आयात गद्दाकी लाई पानाहरू नाइथिन्", "import-error-edit": "तमलाई सम्पादन गद्या अनुमति नभयाको पानो \"$1\" आयात गरिएन ।", "import-error-create": "तमलाई नयाँ बनाउने अनुमति नभयाको पानो \"$1\" आयात गरिएन ।", + "importlogpage": "आयात लग", "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनहरू}} आयात भयो", "tooltip-pt-userpage": "{{GENDER:|तमरो प्रयोगकर्ता}} पान्नो", "tooltip-pt-anonuserpage": "तमी जो IP ठेगानाको रुपमी सम्पादन गद्दै छौ , त्यैको प्रयोगकर्ता पानो निम्न छ :", @@ -1250,6 +1267,7 @@ "tooltip-ca-undelete": "मेट्याको भया पनि यै पानाको सम्पादनहरू पुन:प्राप्त गर", "tooltip-ca-move": "यो पानालाई अर्खिठौर सार", "tooltip-ca-watch": "यै पानालाई तमरा ध्यानसूचीमि थपिदिय", + "tooltip-ca-unwatch": "यै पानालाई तमरि अवलोकनसूची बठेइ हटाऽ", "tooltip-search": "{{SITENAME}}मी खोजऽ", "tooltip-search-go": "यदी ठ्याक्कै येइ नाउँ भया: पन्ना रैछ भँण्या तै मी जा:।", "tooltip-search-fulltext": "यै पाठका लागि पन्नाअनमी खोज", @@ -1274,6 +1292,7 @@ "tooltip-ca-nstab-special": "यो खास पानो हो ,तमी यैलाई आफै सम्पादन गद्द सक्दाइन", "tooltip-ca-nstab-project": "आयोजना पानो हेरिदिय", "tooltip-ca-nstab-image": "चित्र पानो हेर", + "tooltip-ca-nstab-mediawiki": "प्रणाली सन्देश हेरऽ", "tooltip-ca-nstab-template": "टेम्प्लेट(नमूना) हेरिदिय", "tooltip-ca-nstab-category": "श्रेणी पानो हेर", "tooltip-minoredit": "येइ लाई सामान्य सम्पादन भँणिबर चिनो लाऽ", @@ -1287,13 +1306,27 @@ "anonusers": "{{SITENAME}} का नाम नभयाका {{PLURAL:$2| प्रयोगकर्ता|प्रयोगकर्ताहरू}} $1", "simpleantispam-label": "ऐन्टी-स्प्याम जाँच।\nयैलाई <strong>नाइँ</strong> भद्य्या!", "pageinfo-title": "\"$1\" खिलाइ जानकारी", + "pageinfo-header-basic": "नानबड़ि जानकारी", "pageinfo-header-edits": "इतिहास सम्पादन", + "pageinfo-header-restrictions": "पन्ना सुरक्षा", + "pageinfo-display-title": "धेकिन्या शीर्षक", + "pageinfo-default-sort": "पूर्वनिर्धारित अनुक्रमण साँचो", + "pageinfo-length": "पन्ना लम्बाइ (बाइटअन मी)", + "pageinfo-article-id": "पन्ना आइडी", + "pageinfo-language": "पन्ना सामग्री भाषा", + "pageinfo-content-model": "पन्ना सामग्री ढङ्ङ", "pageinfo-robot-policy": "रोबटअन हताँ अनुक्रमण", + "pageinfo-robot-index": "अनुमति भयाऽ", + "pageinfo-robot-noindex": "अनुमति नभयाः", "pageinfo-watchers": "पन्ना निगरानी अद्द्याऽ सङ्ख्या", + "pageinfo-subpages-name": "येइ पन्नाः उपपन्नाअनोः सङ्ख्या", "pageinfo-firstuser": "पन्ना सर्जक", "pageinfo-firsttime": "पन्ना सिर्जना मिति", "pageinfo-edits": "कूल सम्पादन सङ्ख्या", + "pageinfo-magic-words": "जादुयी {{PLURAL:$1|आँखर|आँखरअन}} ($1)", "pageinfo-toolboxlink": "पन्नाइ जानकारी", + "pageinfo-contentpage": "सामग्री पन्नाः रूप मी गणियाऽ", + "pageinfo-contentpage-yes": "हाँ", "rcpatroldisabled": "अहिलका परिवर्तनहरू गस्ती निष्क्रिय पार्याको छ ।", "rcpatroldisabledtext": "अहिलका परिवर्तनहरू गस्ती गुण अहिलको लागि निष्कृय पारियाको छ ।", "markedaspatrollederror-noautopatrol": "तमी आफ्नै सम्पादनलाई गस्ती गरियाको भनि चिनो लगाउन नाइसक्दा ।", @@ -1356,11 +1389,17 @@ "watchlistedit-clear-done": "तमरो ध्यान सूची खाली गरीयाको छ।", "watchlisttools-view": "आधारित फेरबदलीहरू हेर", "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|कुरडी]])", + "redirect-submit": "जाऽ", + "redirect-user": "प्रयोगकर्ता आइडी", + "redirect-page": "पन्ना आइडी", + "redirect-revision": "पन्ना संशोधन", + "redirect-file": "फाइलनाउँ", "specialpages": "खास पन्नाअन", "specialpages-group-changes": "अल्लैका परिवर्तन लगहरू", "tags": "मान्य परिवर्तन ट्यागहरू", "tag-filter": "[[Special:Tags|पुछड]] छानिन्या", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ट्याग|ट्यागहरू}}]]: $2)", + "tags-active-no": "नाइँ", "tags-hitcount": "$1 {{PLURAL:$1|परिवर्तन|परिवर्तनहरू}}", "tags-create-no-name": "तमीले ट्याग नाम निर्दिष्ट गद्दु पड्ड्या हुन्छ ।", "tags-create-warnings-below": "क्या तमी यो ट्याग बनाउन्या काम जारी राख्न चाहन्छौ ?", @@ -1383,6 +1422,7 @@ "logentry-upload-upload": "$1 ले $3 {{GENDER:$2|अपलोड अरेका छन्}}", "feedback-bugornote": "यदि तमी कुनै प्राविधिक समस्यालाई विस्तारले सम्झाउन तयार छौ भण्या कृपया [$1 बग राख]।\nयदि हैन, भण्या तमी तल दियाको सरल फारमको प्रयोग गद्दसक्द्याहौ । तमरो टिप्पणी, तमरो प्रयोगकर्ता नाम र तमरो ब्राउजरको नाम सहित \"[$3 $2]\" पानामी जोडिन्याछ ।", "searchsuggest-search": "{{SITENAME}} खोजऽ", + "duration-days": "$1 {{PLURAL:$1|दिन|दिनअन}}", "expand_templates_preview_fail_html": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्ववावलोकन प्रयास हो भण्या पुन प्रयास गर ।</strong>\nयदि यसले कार्य पूर्ण भएन भण्या [[Special:UserLogout|लग आउट गरिबर]] फेरी लग इन गर्या ।", "expand_templates_preview_fail_html_anon": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्वावलोकन प्रयास हो भण्या कृपया [[Special:UserLogin|लग इन गरिबर]] पुनः प्रयास गर्या ।</strong>", "default-skin-not-found": "ओह! तमरो विकिको पूर्व निर्धारित खोल जस्तो कि <code dir=\"ltr\">$wgDefaultSkin</code> मी बताइयाको<code>$1</code>, उपलब्ध नाईथिन् ।\n\nतमरो इन्स्टलेसन यी खोलहरूलाई सम्मिलित गर्दछ {{PLURAL:$4|खोल|खोलहरू}}। हेर [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: खोललाई सम्मिलित गर्नु] ताकि तमीलाई जानकारी होस् कि कसरि {{PLURAL:$4|उसलाई|उसलाई सम्मिलित गर्न सकियोस् र निर्धारितलाई तय गद्दे}}।\n\n$2\n\n; यदि तमीले अहिले मीडियाविकि इन्स्टाल गर्याका छौ:\n: तमीले सम्भवत गिटबठे इन्स्टाल गर्याका छौ, वा सिधै स्रोत कोडबठे गर्याका छौ जैको लागि कुनै अर्कै तारिका प्रयोग गरियाको छ । यो आशा अनुरूप छ । कोशिश गर केहि खोलहरू\n[https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's मीडियाविकिको खोल डाइरेक्ट्रीबाट डाउनलोड गद्या], जैको लागि तमी:\n:* डाउनलोड गर [https://www.mediawiki.org/wiki/Download टरबल इन्स्टालर], जुन कयौं खोलहरू र विस्तारमी उपलब्ध छन्। तमी खोलहरूको कोड <code>skins/</code> त्यसको डाइरेक्ट्रीबाट कपी-पेस्ट गद्द सक्द्या हौ। \n:* व्यक्तिगत खोलहरू टरबलबठे डाउनलोड गर\n[https://www.mediawiki.org/wiki/Special:SkinDistributor मीडिया विकि] बठे।\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins गिटको प्रयोग गरेर डाउनलोड गद्द सकन्छौ]।\n: यदि तमी विकासकर्ता हौ भण्या यसो गद्दा तमरो गिट-रिपजिटरीमी केहि हुनुहुँदैन । \n; यदि तमीले अहिले मीडियाविकिलाई अपग्रेड गर्याका छौ:\n: मीडियाविकि १.२४ र यैको नवीन रूप स्वतः रूपले खोलहरूलाई सक्षम गद्दैनन् (हेर [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual:खोलहरूको स्वतः खोज])। तमी निम्नलिखितलाई पेस्ट गद्द सकन्छौ: {{PLURAL:$5|लाइन|लाइनहरू}} <code>LocalSettings.php</code> मी ताकि {{PLURAL:$5|उसले|सबै}} सक्षम होस् जस्तो कि तमीले इन्स्टाल गर्याको {{PLURAL:$5|खोल|खोलहरू}}को मामिलामी:\n\n<pre dir=\"ltr\">$3</pre>\n\n; यदि तमीले अहिले परिवर्तन गर्याका छौ<code>LocalSettings.php</code>:\n: खोल नामहरूको अगाडी डबल-क्लिक गर जसले तमलाई विभिन्न प्रकारहरूको विकल्प दिन्छ।", diff --git a/languages/i18n/el.json b/languages/i18n/el.json index 7a84fd8525..829f5b0ec9 100644 --- a/languages/i18n/el.json +++ b/languages/i18n/el.json @@ -1327,6 +1327,7 @@ "recentchanges-noresult": "Χωρίς αλλαγές κατά τη διάρκεια της συγκεκριμένης χρονικής περιόδου, με αυτά τα κριτήρια.", "recentchanges-timeout": "Ο χρόνος της αναζήτησης τελείωσε. Μπορεί να θέλετε να δοκιμάσετε διαφορετικές παραμέτρους αναζήτησης.", "recentchanges-network": "Λόγω ενός τεχνικού λάθους, δεν μπόρεσαν να προβληθούν τα αποτελέσματα. Παρακαλώ προσπαθήστε να ανανεώσετε την σελίδα.", + "recentchanges-notargetpage": "Ορίστε το όνομα σελίδας παραπάνω για να δείτε τις αλλαγές σχετικά με αυτή την σελίδα.", "recentchanges-feed-description": "Παρακολούθηση των πιο πρόσφατων αλλαγών στο wiki σε αυτή τη ροή.", "recentchanges-label-newpage": "Αυτή η επεξεργασία δημιούργησε μια νέα σελίδα", "recentchanges-label-minor": "Αυτή είναι μια μικροεπεξεργασία", @@ -1344,6 +1345,7 @@ "rcfilters-activefilters": "Ενεργά φίλτρα", "rcfilters-advancedfilters": "Σύνθετα Φίλτρα", "rcfilters-limit-title": "Αλλαγές προς εμφάνιση", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|αλλαγή|$1 αλλαγές}}, $2", "rcfilters-date-popup-title": "Χρονική περίοδος αναζήτησης", "rcfilters-days-title": "Πρόσφατες ημέρες", "rcfilters-hours-title": "Πρόσφατες ώρες", @@ -1415,6 +1417,11 @@ "rcfilters-filter-watchlist-watched-label": "Στην λίστα παρακολούθησης", "rcfilters-filter-watchlist-watched-description": "Αλλαγές σελίδων στην λίστα παρακολούθησης σας", "rcfilters-filter-watchlist-watchednew-label": "Νέες αλλαγές λίστας παρακολούθησης", + "rcfilters-filter-watchlist-notwatched-label": "Εκτός λίστας παρακολούθησης", + "rcfilters-filter-watchlist-notwatched-description": "Τα πάντα εκτός από τις αλλαγές των σελίδων στην λίστα παρακολούθησης σας.", + "rcfilters-filtergroup-watchlistactivity": "Δραστηριότητα λίστας παρακολούθησης", + "rcfilters-filter-watchlistactivity-unseen-label": "Μη ελεγμένες αλλαγές", + "rcfilters-filter-watchlistactivity-seen-label": "Ελεγμένες αλλαγές", "rcfilters-filtergroup-changetype": "Τύπος αλλαγής", "rcfilters-filter-pageedits-label": "Επεξεργασίες σελίδων", "rcfilters-filter-pageedits-description": "Επεξεργασίες σε περιεχόμενο του wiki, συζητήσεις, περιγραφές κατηγοριών…", @@ -1424,6 +1431,11 @@ "rcfilters-filter-categorization-description": "Καταγραφές σελίδων που προστίθενται ή αφαιρούνται από κατηγορίες.", "rcfilters-filter-logactions-label": "Καταγραφόμενες ενέργειες", "rcfilters-filter-logactions-description": "Διαχειριστικές ενέργειες, δημιουργίες λογαριασμών, διαγραφές σελίδων, ανεβάσματα αρχείων…", + "rcfilters-filtergroup-lastRevision": "Πρόσφατες αναθεωρήσεις", + "rcfilters-filter-lastrevision-label": "Τρέχουσα αναθεώρηση", + "rcfilters-filter-lastrevision-description": "Μόνο η πλέον πρόσφατη αλλαγή σε σελίδα.", + "rcfilters-filter-previousrevision-label": "Μη πρόσφατη αναθεώρηση", + "rcfilters-filter-previousrevision-description": "Όλες οι αλλαγές που δεν αποτελούν την \"πρόσφατη αναθεώρηση\"", "rcfilters-exclude-button-off": "Εξαίρεση επιλεγμένων", "rcfilters-view-tags": "Επεξεργασίες με ετικέτες", "rcfilters-view-namespaces-tooltip": "Φιλτράρισμα αποτελεσμάτων κατά ονοματοχώρο", @@ -1813,7 +1825,7 @@ "statistics-edits-average": "Μέσος όρος επεξεργασιών ανά σελίδα", "statistics-users": "Εγγεγραμμένοι [[Special:ListUsers|χρήστες]]", "statistics-users-active": "Ενεργοί χρήστες", - "statistics-users-active-desc": "Χρήστες που έχουν κάνει κάποια επεξεργασία μέσα σε {{PLURAL:$1|μέρα|$1 μέρες}}", + "statistics-users-active-desc": "Χρήστες που έχουν κάνει κάποια επεξεργασία μέσα σε {{PLURAL:$1|ημέρα|$1 ημέρες}}", "pageswithprop": "Σελίδες με ιδιότητα σελίδας", "pageswithprop-legend": "Σελίδες με ιδιότητα σελίδας", "pageswithprop-text": "Αυτή η σελίδα ταξινομεί σελίδες που χρησιμοποιούν μια συγκεκριμένη ιδιότητα σελίδας.", @@ -2001,8 +2013,8 @@ "listusers-noresult": "Δεν βρέθηκε χρήστης.", "listusers-blocked": "(υπό φραγή)", "activeusers": "Κατάλογος ενεργών χρηστών", - "activeusers-intro": "Αυτή είναι μια λίστα από χρήστες που είχαν κάποιου είδους δραστηριότητα {{PLURAL:$1|την τελευταία $1 μέρα|τις τελευταίες $1 μέρες}}.", - "activeusers-count": "$1 {{PLURAL:$1|ενέργεια|ενέργειες}} {{PLURAL:$3|την τελευταία μέρα|τις τελευταίες $3 μέρες}}", + "activeusers-intro": "Αυτή είναι μια λίστα από χρήστες που είχαν κάποιου είδους δραστηριότητα {{PLURAL:$1|την τελευταία $1 ημέρα|τις τελευταίες $1 ημέρες}}.", + "activeusers-count": "$1 {{PLURAL:$1|ενέργεια|ενέργειες}} {{PLURAL:$3|την τελευταία ημέρα|τις τελευταίες $3 ημέρες}}", "activeusers-from": "Προβολή χρηστών ξεκινώντας από:", "activeusers-noresult": "Δεν βρέθηκε χρήστης.", "activeusers-submit": "Προβολή ενεργών χρηστών", @@ -2124,6 +2136,7 @@ "enotif_lastdiff": "Δείτε το $1 για να εμφανίσετε αυτή την αλλαγή.", "enotif_anon_editor": "ανώνυμος χρήστης $1", "enotif_body": "{{GENDER:$WATCHINGUSERNAME|Αγαπητέ|Αγαπητή}} $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nΠεριγραφή επεξεργασίας: $PAGESUMMARY $PAGEMINOREDIT\n\nΕπικοινωνία με το χρήστη που έκανε την επεξεργασία:\nδιεύθυνση ηλεκτρονικής αλληλογραφίας: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nΔεν θα υπάρξουν άλλες ειδοποιήσεις σε περίπτωση περαιτέρω δραστηριότητας αν δεν επισκεφθείτε τη σελίδα έχοντας κάνει σύνδεση. Μπορείτε επίσης να επαναφέρετε την επιλογή ειδοποίησης για όλες τις παρακολουθούμενες σελίδες στη λίστα παρακολούθησής σας.\n\nΦιλικά, \nτο σύστημα ειδοποίησης του ιστοχώρου {{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": "Διαγραφή σελίδας", @@ -2366,6 +2379,7 @@ "unblocked-id": "Η φραγή του $1 έχει τερματιστεί", "unblocked-ip": "Έχει αρθεί η φραγή της [[Special:Contributions/$1|$1]].", "blocklist": "Αποκλεισμένοι χρήστες", + "autoblocklist-submit": "Αναζήτηση", "ipblocklist": "Αποκλεισμένοι χρήστες", "ipblocklist-legend": "Εύρεση ενός χρήστη που έχει υποστεί φραγή", "blocklist-userblocks": "Απόκρυψη φραγών λογαριασμού", @@ -2707,6 +2721,7 @@ "pageinfo-length": "Μήκος σελίδας (σε bytes)", "pageinfo-article-id": "Αναγνωριστικό σελίδας", "pageinfo-language": "Γλώσσα περιεχομένου σελίδας", + "pageinfo-language-change": "αλλαγή", "pageinfo-content-model": "Μοντέλο περιεχομένου σελίδας", "pageinfo-content-model-change": "αλλαγή", "pageinfo-robot-policy": "Ευρετηρίαση από ρομπότ", @@ -2810,7 +2825,7 @@ "seconds": "{{PLURAL:$1|$1 δευτερόλεπτο|$1 δευτερόλεπτα}}", "minutes": "{{PLURAL:$1|$1 λεπτό|$1 λεπτά}}", "hours": "{{PLURAL:$1|$1 ώρα|$1 ώρες}}", - "days": "{{PLURAL:$1|$1 μέρα|$1 μέρες}}", + "days": "{{PLURAL:$1|$1 ημέρα|$1 ημέρες}}", "weeks": "{{PLURAL:$1|$1 εβδομάδα|$1 εβδομάδες}}", "months": "{{PLURAL:$1|$1 μήνας|$1 μήνες}}", "years": "{{PLURAL:$1|$1 έτος|$1 έτη}}", @@ -3351,6 +3366,12 @@ "tag-filter": "Φίλτρο [[Special:Tags|ετικετών]]:", "tag-filter-submit": "Φίλτρο", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Ετικέτα|Ετικέτες}}]]: $2)", + "tag-mw-new-redirect": "Νέα ανακατεύθυνση", + "tag-mw-blank": "Άδειασμα περιεχομένου σελίδας", + "tag-mw-blank-description": "Επεξεργασίες που σβήνουν όλο το περιεχόμενο σελίδας", + "tag-mw-replace": "Αντικαταστάθηκε", + "tag-mw-replace-description": "Επεξεργασίες που αφαιρούν πάνω από 90% του περιεχομένου της σελίδας", + "tag-mw-rollback": "Επαναφορά", "tags-title": "Ετικέτες", "tags-intro": "Η σελίδα καταγράφει τις ετικέτες, καθώς και το τι σημαίνει η κάθε μία, με τις οποίες το λογισμικό μπορεί να μαρκάρει μία επεξεργασία.", "tags-tag": "Όνομα ετικέτας", @@ -3440,6 +3461,8 @@ "compare-title-not-exists": "Ο τίτλος που καθορίσατε δεν υπάρχει.", "compare-revision-not-exists": "Η αναθεώρηση που καθορίσατε δεν υπάρχει.", "diff-form": "μια '''φόρμα'''", + "permanentlink": "Σταθερός σύνδεσμος", + "permanentlink-revid": "Αναγνωριστικό αναθεώρησης", "dberr-problems": "Λυπούμαστε! Αυτός ο ιστότοπος αντιμετωπίζει τεχνικές δυσκολίες.", "dberr-again": "Δοκιμάστε να περιμενένετε λίγα λεπτά και να ανανεώσετε.", "dberr-info": "(Δεν είναι δυνατή η πρόσβαση στη βάση δεδομένων: $1)", @@ -3685,6 +3708,8 @@ "mw-widgets-titleinput-description-redirect": "ανακατεύθυνση στο $1", "mw-widgets-categoryselector-add-category-placeholder": "Προσθήκη κατηγορίας...", "mw-widgets-usersmultiselect-placeholder": "Προσθήκη περισσότερων...", + "date-range-from": "Από ημερομηνία:", + "date-range-to": "Έως ημερομηνία:", "sessionprovider-generic": "$1 συνεδρίες", "sessionprovider-mediawiki-session-cookiesessionprovider": "συνεδρίες με βάση τα cookies", "sessionprovider-nocookies": "Τα Cookies μπορούν να απενεργοποιηθούν. Βεβαιωθείτε ότι έχετε ενεργοποιημένα τα cookies και ξεκινήστε πάλι.", @@ -3707,6 +3732,7 @@ "log-action-filter-import-upload": "Εισαγωγή μέσω ανεβάσματος XML", "log-action-filter-managetags-create": "Δημιουργία ετικέτας", "log-action-filter-managetags-delete": "Διαγραφή ετικέττας", + "log-action-filter-newusers-autocreate": "Αυτόματη δημιουργία", "log-action-filter-protect-protect": "Προστασία", "log-action-filter-protect-modify": "Τροποποίηση προστασίας", "log-action-filter-protect-unprotect": "Άρση προστασίας", @@ -3714,6 +3740,8 @@ "log-action-filter-rights-rights": "Χειροκίνητη αλλαγή", "log-action-filter-rights-autopromote": "Αυτόματη αλλαγή", "authmanager-create-disabled": "Η δημιουργία λογαριασμού έχει απενεργοποιηθεί.", + "authmanager-authplugin-setpass-failed-title": "Η αλλαγή κωδικού πρόσβασης απέτυχε", + "authmanager-email-label": "Διεύθυνση ηλ. ταχυδρομείου", "authmanager-realname-label": "Πραγματικό όνομα", "authmanager-realname-help": "Πραγματικό όνομα του χρήστη", "authmanager-provider-temporarypassword": "Προσωρινός κωδικός", @@ -3725,5 +3753,7 @@ "credentialsform-account": "Όνομα λογαριασμού:", "linkaccounts": "Σύνδεση λογαριασμών", "linkaccounts-success-text": "Ο λογαριασμός συνδέθηκε", - "linkaccounts-submit": "Σύνδεση λογαριασμών" + "linkaccounts-submit": "Σύνδεση λογαριασμών", + "revid": "αναθεώρηση $1", + "pagedata-bad-title": "Μη έγκυρος τίτλος: $1." } diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 4b65fbc7eb..301408fca8 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -1064,6 +1064,7 @@ "timezoneregion-indian": "Indian Ocean", "timezoneregion-pacific": "Pacific Ocean", "allowemail": "Allow other users to email me", + "email-allow-new-users-label": "Allow emails from brand-new users", "email-blacklist-label": "Prohibit these users from emailing me:", "prefs-searchoptions": "Search", "prefs-namespaces": "Namespaces", @@ -1239,6 +1240,7 @@ "right-siteadmin": "Lock and unlock the database", "right-override-export-depth": "Export pages including linked pages up to a depth of 5", "right-sendemail": "Send email to other users", + "right-sendemail-new-users": "Send email to users with no logged actions", "right-managechangetags": "Create and (de)activate [[Special:Tags|tags]]", "right-applychangetags": "Apply [[Special:Tags|tags]] along with one's changes", "right-changetags": "Add and remove arbitrary [[Special:Tags|tags]] on individual revisions and log entries", @@ -1482,9 +1484,9 @@ "rcfilters-preference-label": "Hide the improved version of Recent Changes", "rcfilters-preference-help": "Rolls back the 2017 interface redesign and all tools added then and since.", "rcfilters-filter-showlinkedfrom-label": "Show changes on pages linked from", - "rcfilters-filter-showlinkedfrom-option-label": "Show changes on pages linked <strong>FROM</strong> a page", - "rcfilters-filter-showlinkedto-label": "Show changes on pages linked to", - "rcfilters-filter-showlinkedto-option-label": "Show changes on pages linked <strong>TO</strong> a page", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pages linked from</strong> the selected page", + "rcfilters-filter-showlinkedto-label": "Show changes on pages linking to", + "rcfilters-filter-showlinkedto-option-label": "<strong>Pages linking to</strong> the selected page", "rcfilters-target-page-placeholder": "Enter a page name", "rcnotefrom": "Below {{PLURAL:$5|is the change|are the changes}} since <strong>$3, $4</strong> (up to <strong>$1</strong> shown).", "rclistfromreset": "Reset date selection", @@ -3889,6 +3891,8 @@ "tag-mw-replace-description": "Edits that remove more than 90% of the content of a page", "tag-mw-rollback": "Rollback", "tag-mw-rollback-description": "Edits that roll back previous edits using the rollback link", + "tag-mw-undo": "Undo", + "tag-mw-undo-description": "Edits that undo previous edits using the undo link", "tags-title": "Tags", "tags-intro": "This page lists the tags that the software may mark an edit with, and their meaning.", "tags-tag": "Tag name", diff --git a/languages/i18n/eo.json b/languages/i18n/eo.json index 7d5692ba3b..2056034ffe 100644 --- a/languages/i18n/eo.json +++ b/languages/i18n/eo.json @@ -81,13 +81,14 @@ "tog-shownumberswatching": "Montri la nombron da priatentaj uzantoj", "tog-oldsig": "Via ekzistanta subskribo:", "tog-fancysig": "Trakti subskribon kiel vikitekston (sen aÅ­tomata ligo)", - "tog-uselivepreview": "Uzadi tujan antaÅ­rigardon", + "tog-uselivepreview": "Montri antaÅ­rigardojn sen reŝarĝi la paĝon", "tog-forceeditsummary": "Averti min kiam mi konservas malplenan redaktoresumon", "tog-watchlisthideown": "Kaŝi miajn redaktojn de la atentaro", "tog-watchlisthidebots": "Kaŝi robotajn redaktojn de la atentaro", "tog-watchlisthideminor": "Kaŝi malgrandajn redaktojn de la atentaro", "tog-watchlisthideliu": "Kaŝi redaktojn de ensalutitaj uzantoj de la atentaro", "tog-watchlistreloadautomatically": "Reŝargi la atentaron aÅ­tomate ĉiam, kiam filtrilo estas ŝanĝita (bezonas Ĝavoskripton)", + "tog-watchlistunwatchlinks": "Aldoni rektajn (mal)atentendajn ligojn al atentaro (bezonas Javascripton por komuti la funkciecon)", "tog-watchlisthideanons": "Kaŝi redaktojn de anonimuloj de la atentaro", "tog-watchlisthidepatrolled": "Kaŝi patrolitajn redaktojn de la atentaro", "tog-watchlisthidecategorization": "Kaŝi enkategoriigon de paĝoj", @@ -487,7 +488,7 @@ "nosuchusershort": "Ne ekzistas uzanto kun la nomo \"$1\". Bonvolu kontroli vian ortografion.", "nouserspecified": "Vi devas entajpi salutnomon.", "login-userblocked": "Ĉi tiu uzanto estas forbarita. Ensalutado ne estas permesita.", - "wrongpassword": "Vi tajpis malĝustan pasvorton. Bonvolu provi denove.", + "wrongpassword": "Vi tajpis malĝustan uzantnomon aÅ­ pasvorton.\nBonvolu provi denove.", "wrongpasswordempty": "Vi tajpis malplenan pasvorton. Bonvolu provi denove.", "passwordtooshort": "Pasvortoj devas esti longaj almenaÅ­ $1 {{PLURAL:$1|1 signon|$1 signojn}}.", "passwordtoolong": "Pasvorto ne povas esti pli longa ol {{PLURAL:$1|1 signo|$1 signoj}}.", @@ -560,9 +561,9 @@ "botpasswords-insert-failed": "Aldono de la robota nomo \"$1\" ne sukcesis. Ĉu ĝi jam estis aldonita?", "botpasswords-update-failed": "Ĝisdatigo de la robota nomo \"$1\" ne sukcesis. Ĉu ĝi estis forigita?", "botpasswords-created-title": "Robota pasvorto kreita", - "botpasswords-created-body": "La robota pasvorto por robota nomo \"$1\" de la uzanto \"$2\" estis kreita.", + "botpasswords-created-body": "La pasvorto de roboto por nomo de roboto \"$1\" de la uzanto \"$2\" estis kreita.", "botpasswords-updated-title": "Robota pasvorto ĝisdatigita", - "botpasswords-updated-body": "La robota pasvorto por robota nomo \"$1\" de la uzanto \"$2\" estis ĝisdatigita.", + "botpasswords-updated-body": "La pasvorto de roboto por nomo de roboto \"$1\" de la uzanto \"$2\" estis ĝisdatigita.", "botpasswords-deleted-title": "Robota pasvorto forigita", "botpasswords-deleted-body": "La robota pasvorto por robota nomo \"$1\" de la uzanto \"$2\" estis forigita.", "botpasswords-newpassword": "La nova pasvorto por ensaluti per <strong>$1</strong> estas <strong>$2</strong>. <em>Bonvolu noti ĝin por estonta konsultado.</em> <br> (Por malnovaj robotoj, kiuj postulas, ke la ensaluta nomo estu sama kiel la eventuala uzantonomo, vi povas uzi <strong>$3</strong> kiel uzantonomon kaj <strong>$4</strong> kiel pasvorton.)", @@ -1432,7 +1433,7 @@ "recentchangeslinked-feed": "Rilataj paĝoj", "recentchangeslinked-toolbox": "Rilataj ŝanĝoj", "recentchangeslinked-title": "Ŝanĝoj rilataj al \"$1\"", - "recentchangeslinked-summary": "Jen listo de ŝanĝoj faritaj lastatempe al paĝoj ligitaj el specifa paĝo (aÅ­ al membroj de specifa kategorio).\nPaĝoj en [[Special:Watchlist|via atentaro]] estas '''grasaj'''.", + "recentchangeslinked-summary": "Entajpu nomon de paĝo por vidi ŝanĝojn sur paĝoj ligitaj el aÅ­ al tiu ĉi paĝo. (Por vidi anojn de kategorio, entajpu Category:nomo de kategorio). Ŝanĝoj sur paĝoj en [[Special:Watchlist|via atentaro]] markiĝas <strong>grase</strong>.", "recentchangeslinked-page": "Nomo de paĝo:", "recentchangeslinked-to": "Montru ŝanĝojn al paĝoj ligitaj al la specifa paĝo anstataÅ­e.", "recentchanges-page-added-to-category": "[[:$1]] aldonita al la kategorio", @@ -2550,7 +2551,7 @@ "import-mapping-subpage": "Importi kiel subpaĝojn de la jena paĝo:", "import-upload-filename": "Dosiernomo:", "import-comment": "Komento:", - "importtext": "Bonvolu elporti la dosieron el la fonta vikio per la [[Special:Export|eksportilo]]. Konservu ĝin sur via persona komputilo kaj poste alŝutu ĝin ĉi tien.", + "importtext": "Bonvolu elporti la dosieron el la fonta vikio per la [[Special:Export|elportilo]]. Konservu ĝin sur via persona komputilo kaj poste alŝutu ĝin ĉi tien.", "importstart": "Importante paĝojn...", "import-revision-count": "$1 {{PLURAL:$1|versio|versioj}}", "importnopages": "Neniu paĝo por importi.", diff --git a/languages/i18n/es-formal.json b/languages/i18n/es-formal.json index a86ccea638..945db30686 100644 --- a/languages/i18n/es-formal.json +++ b/languages/i18n/es-formal.json @@ -272,7 +272,7 @@ "tooltip-ca-edit": "Usted puede editar esta página. Por favor, use el botón de previsualización antes de grabar.", "tooltip-ca-history": "Versiones anteriores de esta página y sus autores", "tooltip-ca-watch": "Añadir esta página a tu lista de seguimiento", - "tooltip-ca-unwatch": "Borrar esta página de su lista de seguimiento", + "tooltip-ca-unwatch": "Quitar esta página de su lista de seguimiento", "tooltip-search": "Buscar en {{SITENAME}}", "tooltip-search-fulltext": "Busca este texto en las páginas", "tooltip-p-logo": "Visitar la página principal", diff --git a/languages/i18n/es.json b/languages/i18n/es.json index 8c5d900e4e..dc1af802f8 100644 --- a/languages/i18n/es.json +++ b/languages/i18n/es.json @@ -165,7 +165,8 @@ "Luisangelrg", "Pierpao", "Ohlila", - "KATRINE1992" + "KATRINE1992", + "Athena in Wonderland" ] }, "tog-underline": "Subrayar los enlaces:", @@ -676,11 +677,11 @@ "botpasswords-insert-failed": "No se pudo agregar el nombre del bot \"$1\". ¿Ya ha sido añadido?", "botpasswords-update-failed": "No se pudo actualizar el nombre del bot \"$1\". ¿Ha sido borrado?", "botpasswords-created-title": "Se creó la contraseña de bot", - "botpasswords-created-body": "Se creó la contraseña del bot llamado \"$1\" del usuario \"$2\".", + "botpasswords-created-body": "Se creó la contraseña del robot «$1» perteneciente {{GENDER:$2|al usuario|a la usuaria}} «$2».", "botpasswords-updated-title": "Se actualizó la contraseña de bot", - "botpasswords-updated-body": "Se actualizó la contraseña del bot llamado \"$1\" del usuario \"$2\".", + "botpasswords-updated-body": "Se actualizó la contraseña del robot «$1» perteneciente {{GENDER:$2|al usuario|a la usuaria}} «$2».", "botpasswords-deleted-title": "Se eliminó la contraseña de bot", - "botpasswords-deleted-body": "Se eliminó la contraseña del bot llamado \"$1\" del usuario \"$2\".", + "botpasswords-deleted-body": "Se eliminó la contraseña del robot «$1» perteneciente {{GENDER:$2|al usuario|a la usuaria}} «$2».", "botpasswords-newpassword": "La contraseña nueva para acceder con <strong>$1</strong> es <strong>$2</strong>. <em>Guarda esta información para su consulta futura.</em> <br> (En caso de robots antiguos que requieren que el nombre de acceso coincida con el de usuario, también puedes utilizar <strong>$3</strong> como nombre de usuario y <strong>$4</strong> como contraseña.)", "botpasswords-no-provider": "BotPasswordsSessionProvider no está disponible.", "botpasswords-restriction-failed": "Las restricciones de la contraseña de bot impiden este inicio de sesión.", @@ -879,7 +880,7 @@ "content-json-empty-object": "Objeto vacío", "content-json-empty-array": "Matriz vacía", "deprecated-self-close-category": "Páginas que utilizan etiquetas HTML autocerradas no válidas", - "deprecated-self-close-category-desc": "Esta página contiene etiquetas HTML de autocierre no válidas, tales como <code><b/></code> o <code><span/></code>. El comportamiento de estas cambiará pronto para ser consistente con la especificación de HTML5, por lo que su utilización en el wikitexto está obsoleta.", + "deprecated-self-close-category-desc": "Esta página contiene etiquetas HTML autocerradas no válidas, tales como <code><b/></code> o <code><span/></code>. El comportamiento de estas cambiará pronto para ser coherente con la especificación de HTML5, por lo que su utilización en el wikitexto está obsoleta.", "duplicate-args-warning": "<strong>Aviso:</strong> [[:$1]] llama a [[:$2]] con más de un valor para el parámetro «$3». Se usará solo el último valor proporcionado.", "duplicate-args-category": "Páginas que usan argumentos duplicados en invocaciones de plantillas", "duplicate-args-category-desc": "La página contiene invocaciones de plantillas que utilizan argumentos duplicados, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.", @@ -1164,6 +1165,7 @@ "timezoneregion-indian": "Océano Índico", "timezoneregion-pacific": "Océano Pacífico", "allowemail": "Permitir que otros usuarios me envíen mensajes de correo", + "email-allow-new-users-label": "Permitir mensajes de correo de usuarios nuevos", "email-blacklist-label": "Prohibir a estos usuarios enviarme mensajes de correo:", "prefs-searchoptions": "Buscar", "prefs-namespaces": "Espacios de nombres", @@ -1333,6 +1335,7 @@ "right-siteadmin": "Bloquear y desbloquear la base de datos", "right-override-export-depth": "Exportar páginas, incluidas aquellas enlazadas hasta una profundidad de 5", "right-sendemail": "Enviar mensajes de correo a otros usuarios", + "right-sendemail-new-users": "Enviar correo electrónico a usuarios sin acciones en el registro", "right-managechangetags": "Crear y (des)activar [[Special:Tags|etiquetas]]", "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] junto con los cambios propios", "right-changetags": "Agregar y quitar [[Special:Tags|etiquetas]] arbitrarias a revisiones individuales y entradas del registro", @@ -1434,6 +1437,7 @@ "recentchanges-noresult": "No hubo cambios que respondan a esos criterios durante el período seleccionado.", "recentchanges-timeout": "Se agotó el tiempo de espera de esta búsqueda. Puede que quieras probar con otros parámetros de búsqueda.", "recentchanges-network": "No se cargó ningún resultado debido a un problema técnico. Prueba a volver a cargar la página.", + "recentchanges-notargetpage": "Escribe el nombre de una página más arriba para ver cambios relativos a esa página.", "recentchanges-feed-description": "Realiza un seguimiento de los cambios más recientes en el wiki con este canal.", "recentchanges-label-newpage": "Esta edición creó una página", "recentchanges-label-minor": "Esta es una edición menor", @@ -1450,6 +1454,8 @@ "rcfilters-activefilters": "Filtros activos", "rcfilters-advancedfilters": "Filtros avanzados", "rcfilters-limit-title": "Resultados que mostrar", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|cambio|$1 cambios}}, $2", + "rcfilters-date-popup-title": "Período de tiempo en que buscar", "rcfilters-days-title": "Días recientes", "rcfilters-hours-title": "Horas recientes", "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|días}}", @@ -1460,7 +1466,7 @@ "rcfilters-quickfilters-placeholder-description": "Para guardar tus ajustes de filtro y reutilizarlos más tarde, pulsa en el icono del marcador en el área de Filtro activo que se encuentra a continuación.", "rcfilters-savedqueries-defaultlabel": "Filtros guardados", "rcfilters-savedqueries-rename": "Cambiar nombre", - "rcfilters-savedqueries-setdefault": "Activar por defecto", + "rcfilters-savedqueries-setdefault": "Predeterminar", "rcfilters-savedqueries-unsetdefault": "Desmarcar como predeterminado", "rcfilters-savedqueries-remove": "Eliminar", "rcfilters-savedqueries-new-name-label": "Nombre", @@ -1473,7 +1479,7 @@ "rcfilters-restore-default-filters": "Restaurar filtros predeterminados", "rcfilters-clear-all-filters": "Borrar todos los filtros", "rcfilters-show-new-changes": "Ver cambios más recientes", - "rcfilters-search-placeholder": "Filtrar cambios (usa el menú o buscar para aplicar filtro)", + "rcfilters-search-placeholder": "Filtrar cambios (utiliza el menú o busca el nombre de un filtro)", "rcfilters-invalid-filter": "Filtro no válido", "rcfilters-empty-filter": "No hay filtros activos. Se muestran todas las contribuciones.", "rcfilters-filterlist-title": "Filtros", @@ -1563,7 +1569,10 @@ "rcfilters-watchlist-showupdated": "Los cambios hechos a páginas que no has visitado desde que se efectuaron aparecen en <strong>negrita</strong>, acompañados de marcadores sólidos.", "rcfilters-preference-label": "Ocultar la versión mejorada de Cambios recientes", "rcfilters-preference-help": "Revierte el rediseño de interfaz de 2017 y desactiva todas las herramientas añadidas desde entonces.", - "rcfilters-target-page-placeholder": "Selecciona una página", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páginas enlazadas desde</strong> la página seleccionada", + "rcfilters-filter-showlinkedto-label": "Mostrar cambios en páginas que enlazan a", + "rcfilters-filter-showlinkedto-option-label": "<strong>Páginas que enlazan hacia</strong> la página seleccionada", + "rcfilters-target-page-placeholder": "Escribe el nombre de una página", "rcnotefrom": "Debajo {{PLURAL:$5|aparece el cambio|aparecen los cambios}} desde <strong>$3, $4</strong> (se muestran hasta <strong>$1</strong>).", "rclistfromreset": "Restablecer selección de fecha", "rclistfrom": "Mostrar cambios nuevos desde las $2 del $3", @@ -1608,7 +1617,7 @@ "recentchangeslinked-feed": "Cambios relacionados", "recentchangeslinked-toolbox": "Cambios relacionados", "recentchangeslinked-title": "Cambios relacionados con «$1»", - "recentchangeslinked-summary": "Esta página es una lista de los últimos cambios en las páginas enlazadas desde una página dada (o en las pertenecientes a una categoría dada). Las páginas que están en tu [[Special:Watchlist|lista de seguimiento]] aparecen en <strong>negrita</strong>.", + "recentchangeslinked-summary": "Escribe el nombre de una página para ver cambios realizados en páginas con enlaces entrantes o salientes a esa página. (Para ver lo que pertenece a una categoría, escribe «Categoría:Nombre de la categoría»). Los cambios en páginas de tu [[Special:Watchlist|lista de seguimiento]] aparecen en <strong>negrita</strong>.", "recentchangeslinked-page": "Nombre de la página:", "recentchangeslinked-to": "Mostrar los cambios en páginas enlazadas con la página seleccionada", "recentchanges-page-added-to-category": "[[:$1]] añadida a la categoría", @@ -1817,7 +1826,7 @@ "uploadstash-wrong-owner": "Este archivo ($1) no pertenece al usuario actual.", "uploadstash-no-such-key": "No existe esta clave ($1); no se puede eliminar.", "uploadstash-no-extension": "No hay ninguna extension", - "uploadstash-zero-length": "El fichero esta vacio", + "uploadstash-zero-length": "El archivo está vacío.", "invalid-chunk-offset": "Desplazamiento inválido del fragmento", "img-auth-accessdenied": "Acceso denegado", "img-auth-nopathinfo": "Falta PATH_INFO.\nEl servidor no está configurado para proporcionar esta información.\nEs posible que esté basado en CGI y que no sea compatible con img_auth.\nConsulte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.", @@ -2081,7 +2090,7 @@ "apisandbox-reset": "Limpiar", "apisandbox-retry": "Reintentar", "apisandbox-loading": "Cargando la información para el módulo API \"$1\"...", - "apisandbox-load-error": "Ocurrió un error al cargar la información para el módulo API \"$1\": $2", + "apisandbox-load-error": "Ocurrió un error al cargar la información del módulo «$1» de la API: $2", "apisandbox-no-parameters": "Este módulo API no tiene parámetros.", "apisandbox-helpurls": "Enlaces de ayuda", "apisandbox-examples": "Ejemplos", @@ -2831,7 +2840,7 @@ "tooltip-ca-undelete": "Restaurar las ediciones hechas a esta página antes de que fuese borrada", "tooltip-ca-move": "Trasladar esta página", "tooltip-ca-watch": "Añadir esta página a tu lista de seguimiento", - "tooltip-ca-unwatch": "Borrar esta página de su lista de seguimiento", + "tooltip-ca-unwatch": "Quitar esta página de tu lista de seguimiento", "tooltip-search": "Buscar en {{SITENAME}}", "tooltip-search-go": "Ir a la página con este nombre exacto si existe", "tooltip-search-fulltext": "Buscar este texto en las páginas", @@ -2857,7 +2866,7 @@ "tooltip-ca-nstab-main": "Ver la página de contenido", "tooltip-ca-nstab-user": "Ver la página del usuario", "tooltip-ca-nstab-media": "Ver la página de multimedia", - "tooltip-ca-nstab-special": "Esta es una página especial, y no puede editarse", + "tooltip-ca-nstab-special": "Esta es una página especial y no puede editarse", "tooltip-ca-nstab-project": "Ver la página del proyecto", "tooltip-ca-nstab-image": "Ver la página del archivo", "tooltip-ca-nstab-mediawiki": "Ver el mensaje de sistema", @@ -3366,8 +3375,8 @@ "exif-gpsdop-moderate": "Moderado ($1)", "exif-gpsdop-fair": "Pasable ($1)", "exif-gpsdop-poor": "Pobre ( $1 )", - "exif-objectcycle-a": "Sólo por la mañana", - "exif-objectcycle-p": "Sólo por el atardecer", + "exif-objectcycle-a": "Por la mañana únicamente", + "exif-objectcycle-p": "Por el atardecer únicamente", "exif-objectcycle-b": "Tanto por la mañana y por la tarde", "exif-gpsdirection-t": "Dirección real", "exif-gpsdirection-m": "Dirección magnética", @@ -3599,11 +3608,19 @@ "tag-mw-contentmodelchange": "cambio de modelo de contenido", "tag-mw-contentmodelchange-description": "Ediciones que [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambian el modelo de contenido] de una página", "tag-mw-new-redirect": "Redirección nueva", + "tag-mw-new-redirect-description": "Ediciones que crean una nueva redirección o convierten la página en una redirección", + "tag-mw-removed-redirect": "Redirección eliminada", + "tag-mw-removed-redirect-description": "Ediciones que convierten una página de redirección existente en una sin redirección", + "tag-mw-changed-redirect-target": "Destino de redirección modificado", "tag-mw-changed-redirect-target-description": "Ediciones que modifican el destino de una redirección", "tag-mw-blank": "Vaciado", "tag-mw-blank-description": "Ediciones que blanquean una página", + "tag-mw-replace": "Reemplazo", "tag-mw-replace-description": "Ediciones que eliminan más del 90 % del contenido de una página", "tag-mw-rollback": "Reversión", + "tag-mw-rollback-description": "Ediciones que deshacen modificaciones previas usando la herramienta de reversor", + "tag-mw-undo": "Deshacer", + "tag-mw-undo-description": "Ediciones que deshacen modificaciones anteriores mediante el enlace «Deshacer»", "tags-title": "Etiquetas", "tags-intro": "Esta página lista las etiquetas con las que el software puede marcar una edición y su significado.", "tags-tag": "Nombre de etiqueta", @@ -3912,7 +3929,7 @@ "mediastatistics-summary": "Estadísticas sobre los tipos de archivos cargados. Solo se tiene en cuenta la versión más reciente de cada archivo. Los archivos antiguos o eliminados están excluidos.", "mediastatistics-nfiles": "$1 ($2 %)", "mediastatistics-nbytes": "{{PLURAL:$1|$1 ''byte''|$1 ''bytes''}} ($2; $3 %)", - "mediastatistics-bytespertype": "Tamaño de archivo total para esta sección: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).", + "mediastatistics-bytespertype": "Tamaño de archivo total de esta sección: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3 %).", "mediastatistics-allbytes": "Tamaño de archivo total para todos los archivos: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).", "mediastatistics-table-mimetype": "Tipo MIME", "mediastatistics-table-extensions": "Extensiones posibles", diff --git a/languages/i18n/et.json b/languages/i18n/et.json index 8136065d95..86a9236e3d 100644 --- a/languages/i18n/et.json +++ b/languages/i18n/et.json @@ -1008,7 +1008,7 @@ "recentchangesdays-max": "Ülemmäär $1 {{PLURAL:$1|päev|päeva}}", "recentchangescount": "Mitut redaktsiooni vaikimisi näidata:", "prefs-help-recentchangescount": "See käib viimaste muudatuste, lehekülgede ajalugude ja logide kohta.", - "prefs-help-watchlist-token2": "#REDIRECT [[MediaWiki:Prefs-help-tokenmanagement/et]]", + "prefs-help-watchlist-token2": "See on sinu jälgimisloendi veebivoo salavõti.\nIgaüks, kes seda teab, saab lugeda sinu jälgimisloendit. Seega ära jaga seda.\n[[Special:ResetTokens|Klõpsa siia, kui sul on vaja see lähtestada]].", "savedprefs": "Sinu eelistused on salvestatud.", "savedrights": "Kasutaja $1 rühmad on salvestatud.", "timezonelegend": "Ajavöönd:", @@ -1028,6 +1028,7 @@ "timezoneregion-indian": "India ookean", "timezoneregion-pacific": "Vaikne ookean", "allowemail": "Luba teistel kasutajatel mulle e-kirju saata", + "email-allow-new-users-label": "Luba e-kirjad tuliuutelt kasutajatelt", "email-blacklist-label": "Keela neil kasutajatel mulle e-kirju saata:", "prefs-searchoptions": "Otsimine", "prefs-namespaces": "Nimeruumid", @@ -1197,6 +1198,7 @@ "right-siteadmin": "Panna lukku ja lukust lahti teha andmebaasi", "right-override-export-depth": "Eksportida lehekülgi, kaasates viidatud leheküljed kuni viienda tasemeni", "right-sendemail": "Saata teistele kasutajatele e-kirju", + "right-sendemail-new-users": "Saata e-kirju logitud toiminguteta kasutajatele", "right-managechangetags": "Koostada ja (in)aktiveerida [[Special:Tags|märgiseid]]", "right-applychangetags": "Rakendada [[Special:Tags|märgiseid]] enda muudatuste suhtes", "right-changetags": "Lisada ja eemaldada käsitsi rakendatavaid [[Special:Tags|märgiseid]] üksikute redaktsioonide ja logisissekannete juures", @@ -1297,6 +1299,7 @@ "recentchanges-noresult": "Selles ajavahemikus pole tehtud neile kriteeriumitele vastavaid muudatusi.", "recentchanges-timeout": "See otsing aegus. Võid proovida teisi otsiparameetreid.", "recentchanges-network": "Tehnilise tõrke tõttu ei õnnestunud tulemusi laadida. Palun proovi lehekülge värskendada.", + "recentchanges-notargetpage": "Sisesta ülal lehekülje pealkiri, et näha selle leheküljega seotud muudatusi.", "recentchanges-feed-description": "Jälgi vikisse tehtud viimaseid muudatusi.", "recentchanges-label-newpage": "Uus lehekülg", "recentchanges-label-minor": "Pisiparandus", @@ -1312,7 +1315,9 @@ "rcfilters-group-results-by-page": "Rühmita tulemused lehekülje kaupa", "rcfilters-activefilters": "Aktiivsed filtrid", "rcfilters-advancedfilters": "Täpsemad filtrid", - "rcfilters-limit-title": "Näita nii mitut muudatust", + "rcfilters-limit-title": "Näita nii mitut tulemust", + "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|muudatus|muudatust}}, $2", + "rcfilters-date-popup-title": "Otsitav ajavahemik", "rcfilters-days-title": "Viimased päevad", "rcfilters-hours-title": "Viimased tunnid", "rcfilters-days-show-days": "$1 {{PLURAL:$1|päev|päeva}}", @@ -1426,6 +1431,11 @@ "rcfilters-watchlist-showupdated": "Muudatused lehekülgedel, mida sa pole pärast muudatuste tegemist külastanud, on <strong>rasvases</strong> kirjas ja tähistatud täidetud punktiga.", "rcfilters-preference-label": "Peida viimaste muudatuste täiustatud versioon", "rcfilters-preference-help": "Pöörab tagasi 2017. aastast alates tehtud muudatused kujunduses ja lisatud tööriistad.", + "rcfilters-filter-showlinkedfrom-label": "Näita muudatusi lehekülgedel, millele viidatakse leheküljelt:", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Leheküljed, millele viidatakse</strong> valitud leheküljel", + "rcfilters-filter-showlinkedto-label": "Näita muudatusi lehekülgedel, millel viidatakse leheküljele", + "rcfilters-filter-showlinkedto-option-label": "<strong>Leheküljed, mis viitavad</strong> valitud leheküljele", + "rcfilters-target-page-placeholder": "Sisesta lehekülje pealkiri", "rcnotefrom": "Allpool on toodud {{PLURAL:$5|muudatus|muudatused}} alates: <strong>$3, kell $4</strong> (näidatakse kuni <strong>$1</strong> muudatust)", "rclistfromreset": "Lähtesta kuupäeva valik", "rclistfrom": "Näita muudatusi alates: $3, kell $2", @@ -1470,7 +1480,7 @@ "recentchangeslinked-feed": "Seotud muudatused", "recentchangeslinked-toolbox": "Seotud muudatused", "recentchangeslinked-title": "Leheküljega \"$1\" seotud muudatused", - "recentchangeslinked-summary": "Siin on loetletud määratud leheküljelt viidatud (või määratud kategooria) lehekülgedel tehtud viimased muudatused.\nSinu [[Special:Watchlist|jälgimisloendi]] leheküljed on '''rasvaselt''' esile toodud.", + "recentchangeslinked-summary": "Sisesta lehekülje pealkri, et näha muudatusi lehekülgedel, mis viitavad sellele leheküljele, või millele viidatakse sellel leheküljel. Et näha kategooria liikmeid, sisesta \"Kategooria:Kategooria pealkiri\". Sinu [[Special:Watchlist|jälgimisloendi]] leheküljed on <strong>rasvases kirjas</strong>.", "recentchangeslinked-page": "Lehekülje nimi:", "recentchangeslinked-to": "Näita hoopis muudatusi lehekülgedel, mis sellele lehele lingivad", "recentchanges-page-added-to-category": "kategooriasse lisatud \"[[:$1]]\"", @@ -3327,6 +3337,8 @@ "autosumm-blank": "Kustutatud kogu lehekülje sisu", "autosumm-replace": "Lehekülg asendatud tekstiga '$1'", "autoredircomment": "Ümbersuunamine lehele [[$1]]", + "autosumm-removed-redirect": "Eemaldatud ümbersuunamine leheküljele [[$1]]", + "autosumm-changed-redirect-target": "Muudetud ümbersuunamise sihtkoht: [[$1]] → [[$2]]", "autosumm-new": "Uus lehekülg: '$1'", "autosumm-newblank": "Alustatud tühja leheküljega", "size-bytes": "$1 {{PLURAL:$1|bait|baiti}}", @@ -3464,6 +3476,20 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Märgis|Märgised}}]]: $2)", "tag-mw-contentmodelchange": "sisumudeli muudatus", "tag-mw-contentmodelchange-description": "Lehekülje [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel sisumudelit muutvad] redaktsioonid", + "tag-mw-new-redirect": "Uus ümbersuunamine", + "tag-mw-new-redirect-description": "Muudatused, millega luuakse uus ümbersuunamislehekülg või muudetakse lehekülg ümbersuunamiseks", + "tag-mw-removed-redirect": "Ümbersuunamine eemaldatud", + "tag-mw-removed-redirect-description": "Muudatused, milles asendatakse ümbersuunamine millegi muuga", + "tag-mw-changed-redirect-target": "Ümbersuunamise sihtkoht muudetud", + "tag-mw-changed-redirect-target-description": "Muudatused, millega muudetakse ümbersuunamise sihtkoht", + "tag-mw-blank": "Tühjendamine", + "tag-mw-blank-description": "Muudatused, millega lehekülg tühjendatakse", + "tag-mw-replace": "Asendatud", + "tag-mw-replace-description": "Muudatused, millega asendatakse lehekülje sisust enam kui 90%", + "tag-mw-rollback": "Tühistamine", + "tag-mw-rollback-description": "Muudatused, millega eelmised muudatused pööratakse tagasi, kasutades tühistuslinki", + "tag-mw-undo": "Eemaldamine", + "tag-mw-undo-description": "Muudatused, millega eelmised muudatused pööratakse tagasi, kasutades eemaldamislinki", "tags-title": "Märgised", "tags-intro": "See lehekülg loetleb märgised, millega tarkvara võib muudatused märgistada, ja nende kirjeldused.", "tags-tag": "Märgise nimi", diff --git a/languages/i18n/eu.json b/languages/i18n/eu.json index 9ea4e2666c..3cddb1ac23 100644 --- a/languages/i18n/eu.json +++ b/languages/i18n/eu.json @@ -74,7 +74,7 @@ "tog-ccmeonemails": "Beste erabiltzaileei bidaltzen dizkiedan mezuen kopiak niri ere bidali", "tog-diffonly": "''Diff''-ak agertzen direnean, orrialdearen edukiera ezkutatu", "tog-showhiddencats": "Ikusi kategoria ezkutuak", - "tog-norollbackdiff": "Rollback bat egin ondoren ezberdintasunak ez erakutsi", + "tog-norollbackdiff": "Lehengoratze bat egin ondoren ezberdintasunak ez erakutsi", "tog-useeditwarning": "Abisa nazazu gorde gabeko aldaketak eginez edizio orrialde bat uzten dudanean", "tog-prefershttps": "Erabili beti konexio seguru bat sartzerakoan", "underline-always": "Beti", @@ -540,7 +540,7 @@ "botpasswords-insert-failed": "Ezin izan da bot \"$1\" izen hori gehitu. Dagoeneko gehituta zegoen?", "botpasswords-update-failed": "\"$1\" Errobota ezin eguneratu. Ezabatua izan al da?", "botpasswords-created-title": "Botaren pasahitza sortu da", - "botpasswords-created-body": "$2 erabiltzailearen $1 errobot izenarentzako pasahitza sortu egin da.", + "botpasswords-created-body": "$2 erabiltzailearen $1 bot izenarentzako pasahitza sortu egin da.", "botpasswords-updated-title": "Botaren pasahitza eguneratu da", "botpasswords-updated-body": "$2 erabiltzailearen $1 errobot izenarentzako pasahitza gaurkotua izan da.", "botpasswords-deleted-title": "Botaren pasahitza ezabatu da", @@ -1028,6 +1028,7 @@ "timezoneregion-indian": "Indiar Ozeanoa", "timezoneregion-pacific": "Ozeano Barea", "allowemail": "Beste erabiltzaileei niri posta mezuak bidaltzea gaitu", + "email-allow-new-users-label": "Baimendu mezuak erabiltzaile berrietatik", "email-blacklist-label": "Erabiltzaile hauei niri mezu elektronikoak bidaltzen debekatu:", "prefs-searchoptions": "Bilatu", "prefs-namespaces": "Izen-tarteak", @@ -1197,6 +1198,7 @@ "right-siteadmin": "Blokeatu eta desblokeatu datu basea blokeatu", "right-override-export-depth": "5eko sakonerararteko loturiko orrialdeak barne esportatu", "right-sendemail": "Beste erabiltzaileei e-posta bidali", + "right-sendemail-new-users": "Bidali mezu elektronikoa ekintzarik gabe dauden erabiltzaileei", "right-managechangetags": "Sortu eta (des)aktibatzeko [[Special:Tags|tags]]", "right-applychangetags": "Aplikatu [[Special:Tags|tags]] bakoitzaren aldaketekin batera", "right-changetags": "[[Special:Tags|tags]] arbitrarioak gehitu edo kendu berrikusketa eta sarrera indibidualetan", @@ -1298,6 +1300,7 @@ "recentchanges-noresult": "Ez da egon aldaketarik emandako tartean irizpide hau betetzen dutenik.", "recentchanges-timeout": "Bilaketa honek denbora muga gainditu du. Agian beste parametro batzuekin bilatu nahi duzu.", "recentchanges-network": "Errore tekniko baten ondorioz, ez da emaitzarik kargatu. Saiatu orria freskatzen.", + "recentchanges-notargetpage": "Idatzi goian orriaren izena orri horri lotutako aldaketak ikusteko.", "recentchanges-feed-description": "Sindikazio honetan wikian eginiko azkeneko aldaketak jarrai daitezke.", "recentchanges-label-newpage": "Aldaketa honek orri berri bat sortu du", "recentchanges-label-minor": "Aldaketa hau txikia da", @@ -1313,7 +1316,9 @@ "rcfilters-group-results-by-page": "Talde emaitzak orrika", "rcfilters-activefilters": "Iragazki aktiboak", "rcfilters-advancedfilters": "Iragazki aurreratuak", - "rcfilters-limit-title": "Aldaketak erakutsi", + "rcfilters-limit-title": "Erakusteko emaitzak", + "rcfilters-limit-and-date-label": "{{PLURAL:aldaketa|$1|$1 aldaketa}}, $2", + "rcfilters-date-popup-title": "Bilatzeko denbora tartea", "rcfilters-days-title": "Azken egunak", "rcfilters-hours-title": "Azken orduak", "rcfilters-days-show-days": "{{PLURAL:$1|Egun $1|$1 egun}}", @@ -1427,6 +1432,7 @@ "rcfilters-watchlist-showupdated": "Azkenengo aldaketak egin zirenetik bisitatu ez dituzun orrietan eman diren aldaketak <strong>lodi estiloan</strong> daude, markatzaile sendoekin.", "rcfilters-preference-label": "Azkenengo Aldaketen hobetutako bertsioa ezkutatu", "rcfilters-preference-help": "2017 interfazearen birmoldaketa eta geroztik gehitu diren tresna guztietara bueltatzen da.", + "rcfilters-target-page-placeholder": "Sartu orrialde baten izena", "rcnotefrom": "Jarraian azaltzen diren {{PLURAL:$5|aldaketak}} data honetatik aurrerakoak dira: <strong>$3,$4</strong> (gehienez <b>$1</b> erakusten dira).", "rclistfromreset": "Data aukeraketa berrezarri", "rclistfrom": "Erakutsi $3 $2 ondorengo aldaketa berriak", @@ -1666,6 +1672,7 @@ "uploadstash-bad-path": "Bidea ez da existitzen.", "uploadstash-bad-path-invalid": "Bideak ez du balio.", "uploadstash-bad-path-unknown-type": "Mota ezezaguna \"$1\".", + "uploadstash-bad-path-bad-format": "\"$1\" giltza ez dago formatu apropos batean.", "uploadstash-file-not-found-missing-content-type": "Eduki-motako goiburua falta da.", "uploadstash-file-not-found-not-exists": "Ezin da bidea aurkitu, edo ez da fitxategi arrunta.", "uploadstash-file-too-large": "Ezin da $1 byte baino handiagoa den fitxategia zerbitzatu.", @@ -2546,7 +2553,7 @@ "delete_and_move_text": "\"[[:$1]]\" helmuga orria jadanik existitzen da. Mugimenduari lekua egiteko ezabatu nahi duzu?", "delete_and_move_confirm": "Bai, orrialdea ezabatu", "delete_and_move_reason": "[[$1]] mugitzeko ezabatu da", - "selfmove": "Izenburua berdina da; ezin da orri bat bere gainera mugitu.", + "selfmove": "Izenburua lehengoaren berdina da; ezin da orri bat bere gainera mugitu.", "immobile-source-namespace": "\"$1\" motako orrialdeak ezin dira mugitu", "immobile-target-namespace": "Orrialdeak ezin dira \"$1\" motara mugitu", "immobile-target-namespace-iw": "Interwiki lotura ez da baliagarria orrialdea mugitu ahal izateko.", @@ -2620,6 +2627,7 @@ "import-mapping-namespace": "Izen eremu batera inportatu:", "import-mapping-subpage": "Hurrengo orriaren azpi-orri bezala inportatu:", "import-upload-filename": "Fitxategiaren izena:", + "import-upload-username-prefix": "Interwiki aurrizkia:", "import-comment": "Iruzkina:", "importtext": "Mesedez, jatorrizko wikitik orrialdea esportatzeko [[Special:Export|esportazio tresna]] erabil ezazu, zure diskoan gorde eta jarraian hona igo.", "importstart": "Orrialdeak inportatzen...", @@ -3441,6 +3449,11 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiketa|Etiketak}}]]: $2)", "tag-mw-contentmodelchange": "Eduki eredu aldaketa", "tag-mw-contentmodelchange-description": "Orri baten [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel change the content model] aldaketak", + "tag-mw-new-redirect": "Birbideratze berria", + "tag-mw-blank-description": "Orria zuriz jartzen duten aldaketak", + "tag-mw-replace": "Ordezkatuta", + "tag-mw-replace-description": "Orrialde baten edukiaren %90a baino gehiagok ezabatzen duten aldaketak", + "tag-mw-rollback": "Desegin", "tags-title": "Etiketak", "tags-intro": "Orri honek softwareak aldatzeko bezala marka ditzazkeen etiketak zerrendatzen ditu, eta berauen esanahia.", "tags-tag": "Etiketaren izena", diff --git a/languages/i18n/fa.json b/languages/i18n/fa.json index 97ec17334a..873c74f0a8 100644 --- a/languages/i18n/fa.json +++ b/languages/i18n/fa.json @@ -62,7 +62,8 @@ "Yoosef Pooranvary", "DEXi", "Obzord", - "Alp Er Tunqa" + "Alp Er Tunqa", + "Baloch Khan" ] }, "tog-underline": "خط کشیدن زیر پیوندها:", @@ -3350,6 +3351,7 @@ "autosumm-blank": "صفحه را خالی کرد", "autosumm-replace": "جایگزینی صفحه با '$1'", "autoredircomment": "تغییرمسیر به [[$1]]", + "autosumm-removed-redirect": "تغییرمسیر به [[$1]] حذف شد", "autosumm-new": "صفحه‌ای تازه حاوی «$1» ایجاد کرد", "autosumm-newblank": "ایجاد صفحه خالی", "size-bytes": "$1 بایت", @@ -3532,6 +3534,8 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|برچسب|برچسب‌ها}}]]: $2)", "tag-mw-contentmodelchange": "تغییر مدل محتوا", "tag-mw-contentmodelchange-description": "ویرایش‌هایی که [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel مدل محتوای صفحه را تغییر می‌دهند]", + "tag-mw-removed-redirect": "تغییرمسیر حذف شد", + "tag-mw-rollback": "واگردانی", "tags-title": "برچسب‌ها", "tags-intro": "این صفحه فهرستی‌است از برچسب‌هایی که نرم‌افزار با آن‌ها ویرایش‌ها را علامت‌گذری می‌کند، به همراه معانی آن‌ها.", "tags-tag": "نام برچسب", @@ -3551,41 +3555,41 @@ "tags-activate": "فعال‌سازی", "tags-deactivate": "غیرفعال کردن", "tags-hitcount": "$1 {{PLURAL:$1|تغییر|تغییر}}", - "tags-manage-no-permission": "شما اجازه مدیریت تغییر تگ‌ها را ندارید.", + "tags-manage-no-permission": "شما اجازه مدیریت تغییر برچسب‌ها را ندارید.", "tags-manage-blocked": "امکان تغییر برچسب‌ها را در زمان بسته‌بودن {{GENDER:$1|ندارید}}", "tags-create-heading": "ایجاد یک برچسب جدید", "tags-create-explanation": "به طور پیش‌فرض، تگ‌های تازه ایجاد شده برای استفاده کاربران و ربات‌ها در دسترس قرار می‌گیرند.", "tags-create-tag-name": "نام برچسب:", "tags-create-reason": "دلیل:", "tags-create-submit": "ایجاد", - "tags-create-no-name": "نام تگ باید مشخص شود.", + "tags-create-no-name": "نام برچسب باید مشخص شود.", "tags-create-invalid-chars": "نام برچسب‌ها نباید حاوی کاما (<code>,</code>) یا خط مورب (<code>/</code>) باشد.", - "tags-create-invalid-title-chars": "نام تگ‌ها نباید شامل حروفی شود که نمی‌توان از آن‌ها در عنوان صفحات استفاده کرد.", - "tags-create-already-exists": "تگ \"$1\" هم‌اکنون موجود است.", - "tags-create-warnings-above": "در هنگام ایجاد تگ \"$1\" با {{PLURAL:$2|هشدار|هشدارهای}} زیر پیش آمد:", - "tags-create-warnings-below": "آیا مایل به ادامه ایجاد تگ هستید؟", + "tags-create-invalid-title-chars": "نام برچسب‌ها نباید شامل حروفی شود که نمی‌توان از آن‌ها در عنوان صفحات استفاده کرد.", + "tags-create-already-exists": "برچسب \"$1\" هم‌اکنون موجود است.", + "tags-create-warnings-above": "در هنگام ایجاد برچسب \"$1\" با {{PLURAL:$2|هشدار|هشدارهای}} زیر پیش آمد:", + "tags-create-warnings-below": "آیا مایل به ادامه ایجاد برچسب هستید؟", "tags-delete-title": "حذف برچسب", - "tags-delete-explanation-initial": "شما در حال حذف تگ «$1» از پایگاه داده هستید.", + "tags-delete-explanation-initial": "شما در حال حذف برچسب «$1» از پایگاه داده هستید.", "tags-delete-explanation-in-use": "این از {{PLURAL:$2|$2 ویرایش یا ورودی سیاهه|همهٔ $2 ویرایش و/یا ورودی سیاهه}} حذف خواهد شد با وجودی که الان تائید شده‌است.", "tags-delete-explanation-warning": "این عمل <strong>غیر قابل بازگشت</strong> است، حتی توسط مدیران پایگاه داده. مطمئن باشید که این همان تگی است که می‌خواهید آن‌را حذف کنید.", "tags-delete-explanation-active": "<strong>برچسب \"$1\" هنوز فعال است و در آینده اعمال خواهد شد.</strong> برای جلوگیری از این اتفاق، به قسمت(‌هایی) که برچسب فعال شده رفته و از آنجا غیرفعالش کنید.", "tags-delete-reason": "دلیل:", - "tags-delete-submit": "این تگ را به‌صورت غیرقابل بازگشت حذف کن", + "tags-delete-submit": "این برچسب را به‌صورت غیرقابل بازگشت حذف کن", "tags-delete-not-allowed": "برچسب‌هایی که در یک افزونه تعریف می‌شوند قابل حذف نیستند، مگر اینکه آن افزونه در این مورد خاص این قابلیت را بدهد.", - "tags-delete-not-found": "تگ «$1» وجود ندارد.", + "tags-delete-not-found": "برچسب «$1» وجود ندارد.", "tags-delete-too-many-uses": "برچسب \"$1\" در بیش از $2 نسخه اعمال شده است و نمی‌توان آن را حذف نمود.", "tags-delete-warnings-after-delete": "برچسب \"$1\" حذف شد، اما با {{PLURAL:$2|خطای|خطاهای}} زیر همراه بود:", "tags-delete-no-permission": "شما اجازهٔ حذف برچسب‌های تغییر را ندارید.", "tags-activate-title": "فعال‌سازی برچسب", - "tags-activate-question": "شما در حال فعال‌سازی تگ «$1» هستید.", + "tags-activate-question": "شما در حال فعال‌سازی برچسب «$1» هستید.", "tags-activate-reason": "دلیل:", - "tags-activate-not-allowed": "فعال‌سازی تگ «$1» ممکن نیست.", - "tags-activate-not-found": "تگ «$1» وجود ندارد.", + "tags-activate-not-allowed": "فعال‌سازی برچسب «$1» ممکن نیست.", + "tags-activate-not-found": "برچسب «$1» وجود ندارد.", "tags-activate-submit": "فعال‌سازی", "tags-deactivate-title": "غیرفعال‌سازی برچسب", - "tags-deactivate-question": "شما در حال غیرفعال‌سازی تگ «$1» هستید.", + "tags-deactivate-question": "شما در حال غیرفعال‌سازی برچسب «$1» هستید.", "tags-deactivate-reason": "دلیل:", - "tags-deactivate-not-allowed": "غیرفعال‌سازی تگ «$1» ممکن نیست.", + "tags-deactivate-not-allowed": "غیرفعال‌سازی برچسب «$1» ممکن نیست.", "tags-deactivate-submit": "غیرفعال‌سازی", "tags-apply-no-permission": "دسترسی برای تغییر برچسب تغییراتتان را ندارید.", "tags-apply-blocked": "در زمان بسته‌بودن امکان اعمال تغییراتتان بر روی برچسب‌ها را {{GENDER:$1|ندارید}}.", @@ -3731,7 +3735,7 @@ "logentry-upload-upload": "$1 $3 را {{GENDER:$2|بارگذاری کرد}}", "logentry-upload-overwrite": "$1 نسخهٔ تازه‌ای از $3 را {{GENDER:$2|بارگذاری کرد}}", "logentry-upload-revert": "$1 {{GENDER:$2|بارگذاری کرد}} $3", - "log-name-managetags": "تاریخچه مدیریت تگ", + "log-name-managetags": "تاریخچه مدیریت برچسب", "log-description-managetags": "این صفحه امور مدیریتی مربوط به [[Special:Tags|برچسب‌ها]] را فهرست می‌کند. سیاهه فقط حاوی فعالیت‌هایی است که توسط یک مدیر به صورت دستی انجام شده‌اند؛ برچسب‌ها ممکن است توسط نرم‌افزار ویکی ساخته یا حذف بشوند بدون اینکه هیچ ورودی در این سیاهه ثبت گردد.", "logentry-managetags-create": "$1 برچسب «$4» را {{GENDER:$2|ایجاد کرد}}", "logentry-managetags-delete": "$1 برچسب را از \"$4\" {{GENDER:$2|حذف کرد}} (حذف شده از $5 {{PLURAL:$5|نسخه یا ورودی سیاهه|نسخه یا/و ورودی سیاهه}})", @@ -3930,10 +3934,10 @@ "log-action-filter-delete-revision": "حذف ویرایش", "log-action-filter-import-interwiki": "ورودی ترانسویکی", "log-action-filter-import-upload": "درون‌ریزی به کمک بارگذاری XML", - "log-action-filter-managetags-create": "ایجاد تگ", - "log-action-filter-managetags-delete": "حذف کردن تگ", - "log-action-filter-managetags-activate": "فعالسازی تگ", - "log-action-filter-managetags-deactivate": "تغییر تگ", + "log-action-filter-managetags-create": "ایجاد برچسب", + "log-action-filter-managetags-delete": "حذف کردن برچسب", + "log-action-filter-managetags-activate": "فعالسازی برچسب", + "log-action-filter-managetags-deactivate": "تغییر برچسب", "log-action-filter-move-move": "انتقال بدون بازنویسی تغییر مسیرها", "log-action-filter-move-move_redir": "انتقال با بازنویسی تغییر مسیرها", "log-action-filter-newusers-create": "ایجاد شده توسط کاربر ناشناس", diff --git a/languages/i18n/fi.json b/languages/i18n/fi.json index 72084d2e5e..b0ad41e8c9 100644 --- a/languages/i18n/fi.json +++ b/languages/i18n/fi.json @@ -491,7 +491,7 @@ "nosuchusershort": "Käyttäjää nimeltä ”$1” ei ole. Kirjoititko nimen oikein?", "nouserspecified": "Käyttäjätunnusta ei ole määritelty.", "login-userblocked": "Tämä käyttäjä on estetty. Kirjautuminen ei ole sallittua.", - "wrongpassword": "Syöttämäsi salasana ei ole oikein. Ole hyvä ja yritä uudelleen.", + "wrongpassword": "Virheellinen käyttäjätunnus tai salasana.\nOle hyvä ja yritä uudelleen.", "wrongpasswordempty": "Et voi antaa tyhjää salasanaa.", "passwordtooshort": "Salasanan täytyy olla vähintään {{PLURAL:$1|yhden merkin pituinen|$1 merkkiä pitkä}}.", "passwordtoolong": "Salasanat saavat olla enintään $1 {{PLURAL:$1|merkin}} pituisia.", @@ -1326,7 +1326,8 @@ "rcfilters-group-results-by-page": "Ryhmitä tulokset sivujen mukaan", "rcfilters-activefilters": "Aktiiviset suodattimet", "rcfilters-advancedfilters": "Kehittyneet suodattimet", - "rcfilters-limit-title": "Näytettävät muutokset", + "rcfilters-limit-title": "Näytettävät tulokset", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|muutos|$1 muutosta}}, $2", "rcfilters-days-title": "Viimeisimmät päivät", "rcfilters-hours-title": "Viimeisimmät tunnit", "rcfilters-days-show-days": "$1 {{PLURAL:$1|päivä|päivää}}", @@ -2080,7 +2081,7 @@ "emailccsubject": "Kopio lähettämästäsi viestistä osoitteeseen $1: $2", "emailsent": "Sähköposti lähetetty", "emailsenttext": "Sähköpostiviestisi on lähetetty.", - "emailuserfooter": "Tämän sähköpostin {{GENDER:$1|lähetti}} $1 vastaanottajalle {{GENDER:$2|$2}} käyttämällä ”{{int:emailuser}}” -toimintoa {{GRAMMAR:inessive|{{SITENAME}}}}. Jos vastaat tähän sähköpostiin, sähköpostisi lähetetään suoraan {{GENDER:$1|alkuperäiselle lähettäjälle}}, paljastaen {{GENDER:$2|sinun}} sähköpostiosoitteesi {{GENDER:$1|hänelle}}.", + "emailuserfooter": "Tämän sähköpostin {{GENDER:$1|lähetti}} $1 vastaanottajalle {{GENDER:$2|$2}} käyttämällä ”{{int:emailuser}}” -toimintoa {{GRAMMAR:inessive|{{SITENAME}}}}. Jos vastaat tähän sähköpostiin, sinun sähköpostiviestisi lähetetään suoraan {{GENDER:$1|alkuperäiselle lähettäjälle}} ja samalla paljastetaan {{GENDER:$2|sinun}} sähköpostiosoitteesi {{GENDER:$1|hänelle}}.", "usermessage-summary": "Jätetään järjestelmäviesti.", "usermessage-editor": "Järjestelmäviestittäjä", "watchlist": "Tarkkailulista", @@ -3293,6 +3294,8 @@ "autosumm-blank": "Ak: Sivu tyhjennettiin", "autosumm-replace": "Ak: Sivun sisältö korvattiin sisällöllä ”$1”", "autoredircomment": "Ak: Ohjaus sivulle [[$1]]", + "autosumm-removed-redirect": "Ak: Poistettu ohjaus sivulle [[$1]]", + "autosumm-changed-redirect-target": "Ak: Muutettu ohjauksen kohdesivua: ennen [[$1]], nyt [[$2]]", "autosumm-new": "Ak: Uusi sivu: $1", "autosumm-newblank": "Ak: Luotiin tyhjä sivu", "size-bytes": "$1 {{PLURAL:$1|tavu|tavua}}", @@ -3436,6 +3439,7 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Merkkaus|Merkkaukset}}]]: $2)", "tag-mw-contentmodelchange": "sisältömallin muutos", "tag-mw-contentmodelchange-description": "Muokkaukset, jotka [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel muuttavat sivun sisältömallia]", + "tag-mw-removed-redirect": "Ohjaus on poistettu", "tags-title": "Merkkaukset", "tags-intro": "Tämä sivu luetteloi ne merkkaukset (''engl.'' tags), joilla ohjelmisto voi merkitä muokkauksia, ja mitä ne tarkoittavat.", "tags-tag": "Merkkauksen nimi", diff --git a/languages/i18n/fr.json b/languages/i18n/fr.json index 592a4e1900..928250e60c 100644 --- a/languages/i18n/fr.json +++ b/languages/i18n/fr.json @@ -351,8 +351,8 @@ "views": "Affichages", "toolbox": "Outils", "tool-link-userrights": "Modifier les groupes de l’utilisat{{GENDER:$1|eur|rice}}", - "tool-link-userrights-readonly": "Voir les groupes de l’utilisateur", - "tool-link-emailuser": "Envoyer un courriel à {{GENDER:$1|l’utilisateur|l’utilisatrice}}", + "tool-link-userrights-readonly": "Voir les groupes de l’utilisat{{GENDER:$1|eur|rice}}", + "tool-link-emailuser": "Envoyer un courriel à {{GENDER:$1|cet utilisateur|cette utilisatrice}}", "imagepage": "Voir la page du fichier", "mediawikipage": "Voir la page du message", "templatepage": "Voir la page du modèle", @@ -384,7 +384,7 @@ "currentevents-url": "Project: Actualités", "disclaimers": "Avertissements", "disclaimerpage": "Project: Avertissements généraux", - "edithelp": "Aide pour l'édition", + "edithelp": "Aide pour la modification", "helppage-top-gethelp": "Aide", "mainpage": "Accueil", "mainpage-description": "Accueil", @@ -692,7 +692,7 @@ "botpasswords-not-exist": "L’{{GENDER:$1|utilisateur|utilisatrice}} « $1 » n’a pas de mot de passe de robot nommé « $2 ».", "resetpass_forbidden": "Les mots de passe ne peuvent pas être changés", "resetpass_forbidden-reason": "Les mots de passe ne peuvent pas être modifiés : $1", - "resetpass-no-info": "Vous devez être connecté pour avoir accès directement à cette page.", + "resetpass-no-info": "Vous devez être connecté(e) pour accéder directement à cette page.", "resetpass-submit-loggedin": "Changer de mot de passe", "resetpass-submit-cancel": "Annuler", "resetpass-wrong-oldpass": "Mot de passe actuel ou temporaire invalide.\nVous avez peut-être déjà changé votre mot de passe ou demandé un nouveau mot de passe temporaire.", @@ -712,8 +712,8 @@ "passwordreset-domain": "Domaine :", "passwordreset-email": "Adresse de courriel :", "passwordreset-emailtitle": "Détails du compte sur {{SITENAME}}", - "passwordreset-emailtext-ip": "Quelqu’un (probablement vous, depuis l’adresse IP $1) a demandé une réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou si vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.", - "passwordreset-emailtext-user": "L’utilisateur $1 sur {{SITENAME}} a demandé une réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou si vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.", + "passwordreset-emailtext-ip": "Quelqu’un (probablement vous, depuis l’adresse IP $1) a demandé une réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte de l’{{GENDER:$2|utilisateur suivant|utilisatrice suivante}} est associé|Les comptes des utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou si vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.", + "passwordreset-emailtext-user": "L’{{GENDER:$1|utilisateur|utilisatrice}} $1 sur {{SITENAME}} a demandé une réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte de l’{{GENDER:$2|utilisateur suivant|utilisatrice suivante}} est associé|Les comptes des utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou si vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.", "passwordreset-emailelement": "Nom d’utilisateur : \n$1\n\nMot de passe temporaire : \n$2", "passwordreset-emailsentemail": "Si cette adresse de courriel est associée à votre compte, alors un courriel de réinitialisation de mot de passe sera envoyé.", "passwordreset-emailsentusername": "S’il y a une adresse de courriel associée à ce nom d’utilisateur, alors un courriel de réinitialisation de mot de passe sera envoyé.", @@ -771,8 +771,8 @@ "showpreview": "Prévisualiser", "showdiff": "Voir les modifications", "blankarticle": "<strong>Attention :</strong> la page que vous créez est vide.\nSi vous cliquez de nouveau sur « $1 », la page sera créée sans aucun contenu.", - "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté. Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre nom d’utilisateur, avec d'autres avantages.", - "anonpreviewwarning": "<em>Vous n’êtes pas connecté{{GENDER:||e}}. Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>", + "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté(e). Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre propre nom d’utilisateur(rice) et vous aurez d’autres avantages.", + "anonpreviewwarning": "<em>Vous n’êtes pas connecté(e). Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>", "missingsummary": "<strong>Rappel :</strong> vous n’avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « $1 », vos modifications seront sauvegardées sans résumé.", "selfredirect": "<strong>Attention :</strong> vous êtes en train de rediriger la page vers elle-même.\nIl se peut que vous ayez spécifié la mauvaise cible pour la redirection, ou que vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « $1 », la redirection sera tout de même créée.", "missingcommenttext": "Veuillez entrer un commentaire.", @@ -781,9 +781,9 @@ "subject-preview": "Aperçu du sujet :", "previewerrortext": "Une erreur s’est produite lors de la tentative de prévisualisation de vos modifications.", "blockedtitle": "L’utilisateur est bloqué.", - "blockedtext": "<strong>Votre compte utilisateur ou votre adresse IP a été bloqué.</strong>\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.", - "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:<em>$2:</em>\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.", - "systemblockedtext": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués par MediaWiki.\nLa raison donnée est la suivante:\n\n:<em>$2</em>\n\n* Le début du blocage: $8\n* Expiration d'un délai de blocage: $6\n* Elément concerné: $7\n\nVotre adresse IP actuelle est de $3.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous faites.", + "blockedtext": "<strong>Votre compte utilisateur ou votre adresse IP a été bloqué.</strong>\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", + "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", + "systemblockedtext": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués par MediaWiki.\nLa raison donnée est la suivante:\n\n: <em>$2</em>.\n\n* Le début du blocage: $8\n* Expiration d'un délai de blocage: $6\n* Elément concerné: $7\n\nVotre adresse IP actuelle est de $3.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", "blockednoreason": "aucune raison donnée", "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.", "confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].", @@ -1081,7 +1081,7 @@ "searchprofile-articles-tooltip": "Rechercher dans $1", "searchprofile-images-tooltip": "Rechercher des fichiers multimédias", "searchprofile-everything-tooltip": "Rechercher dans tout le site (y compris dans les pages de discussion)", - "searchprofile-advanced-tooltip": "Chercher dans les espaces de noms clients", + "searchprofile-advanced-tooltip": "Chercher dans les espaces de noms personnalisés", "search-result-size": "$1 ($2 mot{{PLURAL:$2||s}})", "search-result-category-size": "$1 membre{{PLURAL:$1||s}} ($2 sous-catégorie{{PLURAL:$2||s}}, $3 fichier{{PLURAL:$3||s}})", "search-redirect": "(redirection depuis $1)", @@ -1171,6 +1171,7 @@ "timezoneregion-indian": "Océan indien", "timezoneregion-pacific": "Océan pacifique", "allowemail": "Autoriser les autres utilisateurs à m'envoyer des courriels", + "email-allow-new-users-label": "Autoriser les courriels émis par les nouveaux utilisateurs", "email-blacklist-label": "Empêcher ces utilisateurs de m'envoyer des courriels :", "prefs-searchoptions": "Recherche", "prefs-namespaces": "Espaces de noms", @@ -1342,6 +1343,7 @@ "right-siteadmin": "Verrouiller ou déverrouiller la base de données", "right-override-export-depth": "Exporter les pages en incluant les pages liées jusqu'à une profondeur de 5 niveaux", "right-sendemail": "Envoyer un courriel aux autres utilisateurs", + "right-sendemail-new-users": "Envoyer un courriel aux utilisateurs dont le journal ne comporte pas d'enregistrement de connexion", "right-managechangetags": "Créer et (dés)activer des [[Special:Tags|balises]]", "right-applychangetags": "Appliquer [[Special:Tags|les balises]] avec ses propres modifications", "right-changetags": "Ajouter et supprimer de façon arbitraire [[Special:Tags|des balises]] sur des révisions individuelles et des entrées de journal", @@ -1443,6 +1445,7 @@ "recentchanges-noresult": "Aucune modification correspondant à ces critères sur la période indiquée.", "recentchanges-timeout": "Cette recherche a dépassé le délai imparti. Vous pouvez vouloir essayer avec des paramètres de recherche différents.", "recentchanges-network": "Aucun résultat ne peut être affiché du fait d’une erreur technique. Veuillez actualiser la page.", + "recentchanges-notargetpage": "Entrer un nom de page ci-dessus pour voir les modifications relatives à cette page.", "recentchanges-feed-description": "Suivez les dernières modifications du wiki dans ce flux.", "recentchanges-label-newpage": "Cette modification a créé une nouvelle page", "recentchanges-label-minor": "Cette modification est mineure.", @@ -1461,7 +1464,7 @@ "rcfilters-advancedfilters": "Filtres avancés", "rcfilters-limit-title": "Résultats à afficher", "rcfilters-limit-and-date-label": "{{PLURAL:$1|modification|$1 modifications}}, $2", - "rcfilters-date-popup-title": "Periode de temps pour chercher", + "rcfilters-date-popup-title": "Période de temps à rechercher", "rcfilters-days-title": "Derniers jours", "rcfilters-hours-title": "Dernières heures", "rcfilters-days-show-days": "$1 {{PLURAL:$1|jour|jours}}", @@ -1572,14 +1575,14 @@ "rcfilters-liveupdates-button-title-off": "Afficher les nouveaux changements dès qu'ils se produisent", "rcfilters-watchlist-markseen-button": "Marquer toutes les modifications comme vues", "rcfilters-watchlist-edit-watchlist-button": "Modifier votre liste de pages suivies", - "rcfilters-watchlist-showupdated": "Les modifications faites aux pages que vous n’avez pas visitées depuis qu’elles ont été modifiées sont en <strong>gras</strong>, avec des balises unies.", + "rcfilters-watchlist-showupdated": "Les modifications faites aux pages que vous n’avez pas visitées depuis qu’elles ont été modifiées sont en <strong>gras</strong>, avec des puces pleines.", "rcfilters-preference-label": "Masquer la version améliorée des modifications récentes", "rcfilters-preference-help": "Désactive la version 2017 de l'interface ainsi que de tous les outils ajoutés alors et depuis.", - "rcfilters-filter-showlinkedfrom-label": "Affiche les modifications sur pages liées de :", - "rcfilters-filter-showlinkedfrom-option-label": "Faites montrer les changements sur pages lies D'une page", - "rcfilters-filter-showlinkedto-label": "Afficher des changements sur pages liees (connectees) a:", - "rcfilters-filter-showlinkedto-option-label": "Afficher des changements sur pages liees A une page.", - "rcfilters-target-page-placeholder": "Selecionner una page", + "rcfilters-filter-showlinkedfrom-label": "Montrer les modifications des pages liées depuis", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pages liées depuis</strong> la page sélectionnée", + "rcfilters-filter-showlinkedto-label": "Montrer les modifications des pages pointant vers", + "rcfilters-filter-showlinkedto-option-label": "<strong>Pages pointant vers</strong> la page sélectionnée", + "rcfilters-target-page-placeholder": "Entrer un nom de page", "rcnotefrom": "Ci-dessous {{PLURAL:$5|la modification effectuée|les modifications effectuées}} depuis le <strong>$3, $4</strong> (affichées jusqu’à <strong>$1</strong>).", "rclistfromreset": "Réinitialiser la sélection de la date", "rclistfrom": "Afficher les nouvelles modifications depuis le $3 à $2", @@ -1626,7 +1629,7 @@ "recentchangeslinked-feed": "Suivi des pages liées", "recentchangeslinked-toolbox": "Suivi des pages liées", "recentchangeslinked-title": "Suivi des pages associées à « $1 »", - "recentchangeslinked-summary": "Voici les modifications faites récemment sur des pages liées depuis une page spécifique ou vers des membres d'une catégorie spécifique. \nLes pages de [[Special:Watchlist|votre liste de suivi]] sont <strong>en gras</strong>.", + "recentchangeslinked-summary": "Entrer un nom de page pour voir les modifications faites récemment sur des pages liées depuis ou vers cette page (pour voir les membres d’une catégorie, entrez Catégorie:Nom de catégorie). Les modifications des pages de [[Special:Watchlist|votre liste de suivi]] sont <strong>en gras</strong>.", "recentchangeslinked-page": "Nom de la page :", "recentchangeslinked-to": "Afficher les modifications des pages qui comportent un lien vers la page donnée plutôt que l'inverse", "recentchanges-page-added-to-category": "[[:$1]] ajouté à la catégorie", @@ -2488,14 +2491,14 @@ "tooltip-invert": "Cochez cette case pour cacher les modifications des pages dans l'espace de noms sélectionné (et l'espace de noms associé si coché)", "tooltip-whatlinkshere-invert": "Cochez cette case pour cacher les liens des pages dans l'espace de nom sélectionné.", "namespace_association": "Espace de noms associé", - "tooltip-namespace_association": "Cochez cette case pour inclure également l'espace de noms de discussion ou du sujet, associé à l'espace de noms sélectionné", + "tooltip-namespace_association": "Cochez cette case pour inclure également l’espace de noms de discussion ou du sujet, associé à l’espace de noms sélectionné", "blanknamespace": "(Principal)", "contributions": "Contributions de l’{{GENDER:$1|utilisateur|utilisatrice}}", "contributions-title": "Liste des contributions de l’utilisat{{GENDER:$1|eur|rice|eur}} $1", "mycontris": "Contributions", "anoncontribs": "Contributions", "contribsub2": "Pour {{GENDER:$3|$1}} ($2)", - "contributions-userdoesnotexist": "Le compte utilisateur « $1 » n’est pas enregistré.", + "contributions-userdoesnotexist": "Le compte d’utilisateur « $1 » n’est pas enregistré.", "nocontribs": "Aucune modification correspondant à ces critères n'a été trouvée.", "uctop": "(actuelle)", "month": "À partir du mois (et précédents) :", @@ -2797,7 +2800,7 @@ "imported-log-entries": "$1 {{PLURAL:$1|entrée|entrées}} du journal {{PLURAL:$1|importée|importées}}.", "importfailed": "Échec de l'importation : <nowiki>$1</nowiki>", "importunknownsource": "Type inconnu de la source à importer", - "importnoprefix": "Aucun prefixe de interwiki n'a ete fourni", + "importnoprefix": "Aucun prefixe interwiki n’a été fourni", "importcantopen": "Impossible d'ouvrir le fichier à importer", "importbadinterwiki": "Mauvais lien inter-wiki", "importsuccess": "L'importation a réussi !", @@ -3024,7 +3027,7 @@ "svg-long-desc": "Fichier SVG, résolution de $1 × $2 pixels, taille : $3", "svg-long-desc-animated": "Fichier SVG animé, résolution $1 × $2 pixels, taille du fichier : $3", "svg-long-error": "Fichier SVG non valide : $1", - "show-big-image": "Fichier d'origine", + "show-big-image": "Fichier d’origine", "show-big-image-preview": "Taille de cet aperçu : $1.", "show-big-image-preview-differ": "Taille de cet aperçu $3 de ce fichier $2 : $1.", "show-big-image-other": "{{PLURAL:$2|Autre résolution|Autres résolutions}} : $1.", @@ -3549,7 +3552,7 @@ "watchlistedit-clear-removed": "{{PLURAL:$1|Un titre a été|$1 titres ont été}} retirés :", "watchlistedit-too-many": "Il y a trop de pages à afficher ici.", "watchlisttools-clear": "Effacer la liste de suivi", - "watchlisttools-view": "Voir les changements intervenus", + "watchlisttools-view": "Voir les changements correspondants", "watchlisttools-edit": "Voir et modifier la liste de suivi", "watchlisttools-raw": "Modifier la liste de suivi en mode brut", "iranian-calendar-m1": "Farvardin", @@ -3702,17 +3705,19 @@ "tag-mw-contentmodelchange": "modification du modèle de contenu", "tag-mw-contentmodelchange-description": "Modifications qui [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel changent le modèle de contenu] d'une page", "tag-mw-new-redirect": "Nouvelle redirection", - "tag-mw-new-redirect-description": "Editions qui vont creer une nouvelle redirection ou modifier una page vers une redirection", + "tag-mw-new-redirect-description": "Modifications qui créent une nouvelle redirection ou transforment une page en redirection", "tag-mw-removed-redirect": "Redirection supprimée", - "tag-mw-removed-redirect-description": "Les editions qui vont changer la redirection courante a une non redirection", - "tag-mw-changed-redirect-target": "La destination de redirection a ete modifiee", + "tag-mw-removed-redirect-description": "Modifications qui remplacent une redirection existante par une page sans redirection", + "tag-mw-changed-redirect-target": "La destination de redirection a été modifiée", "tag-mw-changed-redirect-target-description": "Modifications qui modifient la cible d’une redirection", - "tag-mw-blank": "Effacement", + "tag-mw-blank": "Blanchiment", "tag-mw-blank-description": "Modifications qui suppriment le contenu des pages", "tag-mw-replace": "Remplacé", "tag-mw-replace-description": "Modifications qui enlèvent plus de 90% du contenu des pages", "tag-mw-rollback": "Révocation", - "tag-mw-rollback-description": "Les editios qui font retourner les editions precedentes a l'aide du lien de retour(''rollback'')", + "tag-mw-rollback-description": "Modifications qui annulent des modifications existantes en utilisant le lien de révocation (''rollback'')", + "tag-mw-undo": "Annuler", + "tag-mw-undo-description": "Modifications qui annulent les précédentes en utilisant le lien annuler", "tags-title": "Balises", "tags-intro": "Cette page liste les balises que le logiciel peut utiliser pour marquer une modification et la signification de chacune d’elles.", "tags-tag": "Nom de la balise", @@ -3856,10 +3861,10 @@ "htmlform-title-not-exists": "$1 n’existe pas", "htmlform-user-not-exists": "<strong>$1</strong> n’existe pas.", "htmlform-user-not-valid": "<strong>$1</strong> n’est pas un nom d’utilisateur valide.", - "logentry-delete-delete": "$1 {{GENDER:$2|a supprimé}} la page $3", + "logentry-delete-delete": "$1 a supprimé la page $3", "logentry-delete-delete_redir": "$1 a {{GENDER:$2|supprimé}} la redirection vers $3 par écrasement", - "logentry-delete-restore": "$1 {{GENDER:$2|a restauré}} la page $3 ($4)", - "logentry-delete-restore-nocount": "$1 {{GENDER:$2|a restauré}} la page $3", + "logentry-delete-restore": "$1 a restauré la page $3 ($4)", + "logentry-delete-restore-nocount": "$1 {{GENDER:$2||}}a restauré la page $3", "restore-count-revisions": "{{PLURAL:$1|1 révision|$1 révisions}}", "restore-count-files": "{{PLURAL:$1|1 fichier|$1 fichiers}}", "logentry-delete-event": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'un événement du journal|de $5 événements du journal}} sur $3: $4", @@ -3889,15 +3894,15 @@ "logentry-import-interwiki": "$1 {{GENDER:$2|a importé}} $3 depuis un autre wiki", "logentry-import-interwiki-details": "$1 {{GENDER:$2|a importé}} $3 depuis $5 ($4 {{PLURAL:$4|révision|révisions}})", "logentry-merge-merge": "$1 {{GENDER:$2|a fusionné}} $3 en $4 (révisions jusqu’à $5)", - "logentry-move-move": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4", + "logentry-move-move": "$1 a déplacé la page $3 vers $4", "logentry-move-move-noredirect": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4 sans laisser de redirection", "logentry-move-move_redir": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4 par-dessus une redirection", "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4 par-dessus une redirection sans laisser de redirection", "logentry-patrol-patrol": "$1 {{GENDER:$2|a marqué}} la révision $4 de la page $3 comme relue", "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|a automatiquement marqué}} la révision $4 de la page $3 comme relue", - "logentry-newusers-newusers": "Le compte d’utilisateur $1 a été créé", + "logentry-newusers-newusers": "Le compte de l’utilisat{{GENDER:$2|eur|rice}} $1 a été créé", "logentry-newusers-create": "Le compte d’utilisat{{GENDER:$4|eur|rice}} $1 a été créé", - "logentry-newusers-create2": "Le compte utilisateur $3 {{GENDER:$2|a été créé}} par $1", + "logentry-newusers-create2": "Le compte utilisateur $3 a été créé par $1", "logentry-newusers-byemail": "Le compte utilisateur $3 {{GENDER:$2|a été créé}} par $1 et le mot de passe a été envoyé par courriel", "logentry-newusers-autocreate": "Le compte $1 {{GENDER:$2|a été créé}} automatiquement", "logentry-protect-move_prot": "$1 {{GENDER:$2|a déplacé}} les paramètres de protection de $4 vers $3", diff --git a/languages/i18n/frr.json b/languages/i18n/frr.json index 9b055aba6b..8281e6b9e3 100644 --- a/languages/i18n/frr.json +++ b/languages/i18n/frr.json @@ -1247,6 +1247,7 @@ "recentchanges-legend-heading": "<strong>Ferklaarang:</strong>", "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (luke uk bi't [[Special:NewPages|list mä nei sidjen]])", "recentchanges-legend-plusminus": "(''±123'')", + "rcfilters-preference-label": "Det nei werjuun faan a Leetst Feranrangen versteeg", "rcnotefrom": "Diar {{PLURAL:$5|woort det feranrang|wurd a feranrangen}} sant <strong>$3,$4</strong>uunwiset (ei muar üs <strong>$1</strong> feranrangen).", "rclistfrom": "Bluas feranrangen sant $3 $2 wise", "rcshowhideminor": "$1 letj feranrangen", @@ -2929,6 +2930,7 @@ "autosumm-blank": "Det sidj as leesag maaget wurden.", "autosumm-replace": "Di tekst as ütjbütjet wurden mä \"$1\"", "autoredircomment": "Sidj tu [[$1]] widjerfeerd", + "autosumm-changed-redirect-target": "Widjerfeerang feranert faan [[$1]] tu [[$2]]", "autosumm-new": "Det sidj as nei uunlaanj wurden: \"$1\"", "autosumm-newblank": "En leesag sidj maaget", "lag-warn-normal": "Feranrangen faan {{PLURAL:$1|at leetst sekund|a leetst $1 sekunden}} kön noch ei uunwiset wurd.", @@ -3047,6 +3049,8 @@ "tag-filter": "[[Special:Tags|Kääntiaken]] filter:", "tag-filter-submit": "Filter", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Kääntiaken|Kääntiakens}}]]: $2)", + "tag-mw-new-redirect": "Nei widjerfeerang", + "tag-mw-new-redirect-description": "Feranrangen, diar en nei widjerfeerang iinracht.", "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", diff --git a/languages/i18n/gl.json b/languages/i18n/gl.json index c33329e97b..a530e16e84 100644 --- a/languages/i18n/gl.json +++ b/languages/i18n/gl.json @@ -24,7 +24,8 @@ "Banjo", "Josep Maria Roca Peña", "Luan", - "Hamilton Abreu" + "Hamilton Abreu", + "Athena in Wonderland" ] }, "tog-underline": "Subliñar as ligazóns:", @@ -1033,6 +1034,7 @@ "timezoneregion-indian": "Océano Índico", "timezoneregion-pacific": "Océano Pacífico", "allowemail": "Admitir mensaxes de correo electrónico doutros usuarios", + "email-allow-new-users-label": "Permite correos electrónicos de usuarios novos", "email-blacklist-label": "Prohibir a eses usuarios enviarme correos electrónicosː", "prefs-searchoptions": "Procura", "prefs-namespaces": "Espazos de nomes", @@ -1207,6 +1209,7 @@ "right-siteadmin": "Pechar e abrir a base de datos", "right-override-export-depth": "Exportar páxinas incluíndo as páxinas ligadas ata unha profundidade de 5", "right-sendemail": "Enviar correos electrónicos a outros usuarios", + "right-sendemail-new-users": "Enviar correo electrónico a usuarios sen accións rexistradas", "right-managechangetags": "Crear e (des)activar [[Special:Tags|etiquetas]]", "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] xunto coas modificacións propias", "right-changetags": "Engadir e quitar [[Special:Tags|etiquetas]] arbitrarias a revisións individuais e entradas do rexistro", @@ -1308,6 +1311,7 @@ "recentchanges-noresult": "Non se produciron cambios que coincidisen con eses criterios durante o período especificado.", "recentchanges-timeout": "Esta procura rematou o temo límite de consulta. Tente usar parámetros de procura diferentes.", "recentchanges-network": "Debido a un erro técnico, non foi posible cargar ningún resultado. Por favor, probe refrescando a páxina.", + "recentchanges-notargetpage": "Introducir un nome de páxina para ver os cambios relacionados con esa páxina.", "recentchanges-feed-description": "Nesta fonte de novas pode seguir as modificacións máis recentes feitas no wiki.", "recentchanges-label-newpage": "Esta edición creou unha nova páxina", "recentchanges-label-minor": "Esta é unha edición pequena", @@ -1440,6 +1444,11 @@ "rcfilters-watchlist-showupdated": "Os cambios feitos en páxinas que non visitou dende que se efectuaron aparecen en <strong>grosas</strong>, acompañados de marcadores sólidos.", "rcfilters-preference-label": "Ocultar a versión mellorada de cambios recentes", "rcfilters-preference-help": "Reverte o redeseño da interface de 2017 e tódalas ferramentas engadidas dende entón.", + "rcfilters-filter-showlinkedfrom-label": "Amosar os cambios en páxinas ligadas desde", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páxinas ligadas desde</strong> a páxina seleccionada", + "rcfilters-filter-showlinkedto-label": "Amosar os cambios en páxinas que ligan con", + "rcfilters-filter-showlinkedto-option-label": "<strong>Páxinas que ligan</strong> para a páxina seleccionada", + "rcfilters-target-page-placeholder": "Insire un nome de páxina", "rcnotefrom": "A continuación {{PLURAL:$5|móstrase o cambio feito|móstranse os cambios feitos}} desde o <strong>$3</strong> ás <strong>$4</strong> (móstranse <strong>$1</strong> como máximo).", "rclistfromreset": "Reinicializar a selección da data", "rclistfrom": "Mostrar os cambios novos desde o $3 ás $2", @@ -1486,7 +1495,7 @@ "recentchangeslinked-feed": "Cambios relacionados", "recentchangeslinked-toolbox": "Cambios relacionados", "recentchangeslinked-title": "Cambios relacionados con \"$1\"", - "recentchangeslinked-summary": "Esta é unha lista dos cambios que se realizaron recentemente nas páxinas vinculadas a esta (ou nos membros da categoría especificada).\nAs páxinas da súa [[Special:Watchlist|lista de vixilancia]] aparecen en '''negra'''.", + "recentchangeslinked-summary": "Introduce un nome de páxina para ver os cambios en páxinas ligadas dende ou ata esa páxina. (Para ver os membros dunha categoría, introduce Categoría:Nome da categoría). Os cambios na túa [[Special:Watchlist|lista de vixiancia]] están en <strong>negra</strong>.", "recentchangeslinked-page": "Nome da páxina:", "recentchangeslinked-to": "Mostrar os cambios relacionados das páxinas que ligan coa dada", "recentchanges-page-added-to-category": "\"[[:$1]]\" engadiuse á categoría", @@ -3527,6 +3536,7 @@ "tag-mw-contentmodelchange": "cambio de modelo de contido", "tag-mw-contentmodelchange-description": "Edicións que [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambian o modelo de contido] dunha páxina", "tag-mw-new-redirect": "Nova redirección", + "tag-mw-new-redirect-description": "Edicións que crean unha nova redirección ou converten unha páxina nunha redirección", "tag-mw-removed-redirect": "Redirección eliminada", "tag-mw-removed-redirect-description": "Edicións que cambian unha redirección existente a unha non redirección", "tag-mw-changed-redirect-target": "Destino de redireccionamento cambiado", @@ -3537,6 +3547,7 @@ "tag-mw-replace-description": "Edicións que eliminan máis do 90% do contido dunha páxina", "tag-mw-rollback": "Desfacer", "tag-mw-rollback-description": "Edicións que desfán modificacións previas usando a ligazón de desfacer", + "tag-mw-undo": "Desfacer", "tags-title": "Etiquetas", "tags-intro": "Esta páxina lista as etiquetas coas que o software pode marcar unha edición, e mailos seus significados.", "tags-tag": "Nome da etiqueta", diff --git a/languages/i18n/got.json b/languages/i18n/got.json index e28581c6d7..88cc946682 100644 --- a/languages/i18n/got.json +++ b/languages/i18n/got.json @@ -12,7 +12,7 @@ ] }, "tog-previewontop": "𐌰𐌽𐌳𐌷𐌿𐌻𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽 𐍆𐌰𐌿𐍂𐌰 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌰𐍂𐌺𐌰", - "tog-previewonfirst": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽 𐌰𐍄 𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐌰 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽", + "tog-previewonfirst": "𐌰𐌽𐌳𐌷𐌿𐌻𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽 𐌰𐍄 𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐌰 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽", "tog-enotifusertalkpages": "𐍃𐌰𐌽𐌳𐌴𐌹 𐌼𐌹𐌺 𐌴-𐌱𐍉𐌺𐍉𐍃 𐌾𐌰𐌱𐌰𐌹 𐌼𐌴𐌹𐌽𐍃 𐌻𐌰𐌿𐍆𐍃 𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌹𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌳𐌰", "tog-shownumberswatching": "𐌰𐌽𐌳𐌷𐌿𐌻𐌴𐌹 𐍂𐌰𐌸𐌾𐍉𐌽 𐍅𐌹𐍄𐌰𐌽𐌳𐌰𐌹𐌶𐌴 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌴", "tog-oldsig": "𐌸𐌴𐌹𐌽𐌰 𐍅𐌹𐍃𐌰𐌽𐌳𐌴𐌹 𐌿𐍆𐌼𐌴𐌻𐌴𐌹𐌽𐍃", @@ -106,6 +106,7 @@ "searcharticle": "𐌲𐌰𐌲𐌲", "history": "𐌻𐌰𐌿𐌱𐌰𐍃𐍀𐌹𐌻𐌻", "history_short": "𐍃𐍀𐌹𐌻𐌻", + "history_small": "𐍃𐍀𐌹𐌻𐌻", "printableversion": "𐌿𐍃𐌼𐌴𐍂𐌴𐌹𐌽𐍃 𐌳𐌿 𐌿𐍃𐌼𐌴𐌻𐌾𐌰𐌽", "permalink": "𐌰𐌹𐍅𐌴𐌹𐌽𐌰 𐌲𐌰𐍅𐌹𐍃𐍃", "print": "𐌿𐍃𐌼𐌴𐌻𐌴𐌹", @@ -164,6 +165,7 @@ "toc": "𐌹𐌽𐌽𐌰𐌽𐌰", "showtoc": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹", "hidetoc": "𐌰𐍆𐍆𐌹𐌻𐌷", + "confirmable-confirm": "𐌹𐍃 {{GENDER:$1:𐌸𐌿}} 𐌰𐍂𐌽𐌹𐌱𐌰?", "confirmable-yes": "𐌾𐌰", "confirmable-no": "𐌽𐌴", "site-rss-feed": "$1 RSS 𐍂𐌹𐌽𐌽𐍉", @@ -190,6 +192,7 @@ "viewsource": "𐍃𐌰𐌹𐍈 𐌱𐍂𐌿𐌽𐌽𐌰𐌽", "protectedpagetext": "𐍃𐌰 𐌻𐌰𐌿𐍆𐍃 𐌷𐌰𐌱𐌰𐌹𐌸 𐌼𐌿𐌽𐌳, 𐌴𐌹 𐌽𐌹 𐍅𐌰𐌹𐍂𐌸𐌴𐌹𐌽𐌰 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃 𐌸𐌰𐌿 𐌰𐌽𐌸𐌰𐍂𐍉𐍃 𐍅𐌰𐌹𐌷𐍄𐍃 𐌹𐌽 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰", "mycustomjsprotected": "𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐍃 𐌰𐌽𐌳𐌻𐌴𐍄 𐌳𐌿 𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽 𐌸𐌰𐌽𐌰 JavaScript 𐌻𐌰𐌿𐍆.", + "welcomeuser": "𐍅𐌰𐌹𐌻𐌰 𐌰𐌽𐌳𐌰𐌽𐌴𐌼𐍃, $1!", "yourname": "𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉:", "userlogin-yourname": "𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉", "userlogin-yourname-ph": "𐌼𐌴𐌻𐌴𐌹 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉 𐌸𐌴𐌹𐌽", diff --git a/languages/i18n/he.json b/languages/i18n/he.json index 0b3f9c663a..cf76e44cc7 100644 --- a/languages/i18n/he.json +++ b/languages/i18n/he.json @@ -1036,6 +1036,7 @@ "timezoneregion-indian": "האוקיינוס ההודי", "timezoneregion-pacific": "האוקיינוס השקט", "allowemail": "לאפשר למשתמשים אחרים לשלוח לי דואר אלקטרוני", + "email-allow-new-users-label": "לאפשר למשתמשים חדשים מאוד לשלוח לי דואר אלקטרוני", "email-blacklist-label": "חסימת המשתמשים הבאים משליחת דואר אלקטרוני אליי:", "prefs-searchoptions": "חיפוש", "prefs-namespaces": "מרחבי שם", @@ -1205,6 +1206,7 @@ "right-siteadmin": "נעילה וביטול נעילה של בסיס הנתונים", "right-override-export-depth": "ייצוא דפים כולל הדפים המקושרים (עד עומק של 5 דפים)", "right-sendemail": "שליחת דואר אלקטרוני למשתמשים אחרים", + "right-sendemail-new-users": "שליחת דואר אלקטרוני למשתמשים שלא ביצעו פעולות מתועדות", "right-managechangetags": "יצירה, הפעלה וביטול של [[Special:Tags|תגיות]]", "right-applychangetags": "החלת [[Special:Tags|תגיות]] יחד עם שינויים", "right-changetags": "הוספה והסרה של [[Special:Tags|תגיות]] לגרסאות ולרשומות יומן", @@ -1306,6 +1308,7 @@ "recentchanges-noresult": "לא היו בתקופה זו שינויים המתאימים לאפשרויות שנבחרו.", "recentchanges-timeout": "נגמר זמן ההמתנה לקבלת תוצאות החיפוש. ניתן לנסות פרמטרים אחרים לחיפוש.", "recentchanges-network": "בשל תקלה טכנית, לא ניתן היה לטעון את התוצאות. אפשר לנסות לרענן את הדף.", + "recentchanges-notargetpage": "יש להקליד שם דף למעלה כדי לראות את השינויים הקשורים לדף זה.", "recentchanges-feed-description": "ניתן לעקוב אחרי השינויים האחרונים באתר בדף זה.", "recentchanges-label-newpage": "בעריכה זו נוצר דף חדש", "recentchanges-label-minor": "זוהי עריכה משנית", @@ -1438,11 +1441,11 @@ "rcfilters-watchlist-showupdated": "שינויים בדפים שלא ביקרת בהם מאז ביצוע השינויים מופיעים בכתב <strong>מודגש</strong>, ומודגשים בצבע.", "rcfilters-preference-label": "הסתרת הגרסה המשופרת של השינויים האחרונים", "rcfilters-preference-help": "ביטול של העיצוב מחדש של הממשק (שבוצע בשנת 2017) ושל כל הכלים שנוספו אז ומאז.", - "rcfilters-filter-showlinkedfrom-label": "הצגת שינויים בדפים המקושרים מ:", - "rcfilters-filter-showlinkedfrom-option-label": "הצגת שינויים בדפים המקושרים *מתוך* דף", - "rcfilters-filter-showlinkedto-label": "הצגת שינויים בדפים המקשרים ל:", - "rcfilters-filter-showlinkedto-option-label": "הצגת שינויים בדפים המקשרים *אל* דף", - "rcfilters-target-page-placeholder": "בחירת דף", + "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": "להלן {{PLURAL:$5|השינוי שבוצע|השינויים שבוצעו}} מאז <strong>$3, $4</strong> (מוצגים עד <strong>$1</strong>).", "rclistfromreset": "איפוס בחירת התאריך", "rclistfrom": "הצגת שינויים חדשים החל מ־$2, $3", @@ -1487,7 +1490,7 @@ "recentchangeslinked-feed": "שינויים בדפים המקושרים", "recentchangeslinked-toolbox": "שינויים בדפים המקושרים", "recentchangeslinked-title": "שינויים בדפים המקושרים מהדף \"$1\"", - "recentchangeslinked-summary": "בדף מיוחד זה רשומים השינויים האחרונים בדפים המקושרים מתוך הדף (או בדפים הכלולים בקטגוריה).\nדפים ב[[Special:Watchlist|רשימת המעקב שלכם]] מוצגים ב'''הדגשה'''.", + "recentchangeslinked-summary": "יש להקליד שם דף כדי לראות את השינויים בדפים המקשרים לדף זה או המקושרים ממנו. (כדי לראות את הדפים החברים בקטגוריה, יש להקליד \"קטגוריה:שם הקטגוריה\".) שינויים בדפים ב[[Special:Watchlist|רשימת המעקב שלך]] מוצגים ב<strong>הדגשה</strong>.", "recentchangeslinked-page": "שם הדף:", "recentchangeslinked-to": "הצגת השינויים בדפים המקשרים לדף הנתון במקום זאת", "recentchanges-page-added-to-category": "הדף [[:$1]] נוסף לקטגוריה", @@ -3565,6 +3568,8 @@ "tag-mw-replace-description": "עריכות שמסירות יותר מ־90% מהתוכן של דף", "tag-mw-rollback": "שחזור", "tag-mw-rollback-description": "עריכות שמשחזרות עריכות קודמות בעזרת קישור השחזור", + "tag-mw-undo": "ביטול", + "tag-mw-undo-description": "עריכות שמבטלות עריכות קודמות בעזרת קישור הביטול", "tags-title": "תגיות", "tags-intro": "דף זה מכיל רשימה של תגיות שהתוכנה יכולה לסמן איתן עריכה, ומשמעויותיהן.", "tags-tag": "שם התגית", diff --git a/languages/i18n/hi.json b/languages/i18n/hi.json index 43dff0f0da..981706c3ae 100644 --- a/languages/i18n/hi.json +++ b/languages/i18n/hi.json @@ -84,17 +84,20 @@ "चक्रपाणी", "Anamdas", "Sachinkatiyar", - "Rishi.Singh" + "Rishi.Singh", + "Clockery", + "Rajatkatiyar10", + "Dcljr" ] }, - "tog-underline": "कड़ियाँ अधोरेखन:", - "tog-hideminor": "हाल में हुए परिवर्तन में छोटे बदलाव छिपाएँ", - "tog-hidepatrolled": "हाल में हुए परिवर्तन में परीक्षित बदलाव छिपाएँ", - "tog-newpageshidepatrolled": "नये पृष्ठों की सूची में परीक्षित पृष्ठ छिपाएँ", + "tog-underline": "लिंक रेखांकित करें:", + "tog-hideminor": "हाल में हुए परिवर्तनों में छोटे बदलाव छिपाएँ", + "tog-hidepatrolled": "हाल में हुए परिवर्तनों में परीक्षित बदलाव छिपाएँ", + "tog-newpageshidepatrolled": "नये पृष्ठ की सूची में परीक्षित पृष्ठों को छिपाएँ", "tog-hidecategorization": "पृष्ठों का श्रेणीकरण छिपाएं", - "tog-extendwatchlist": "केवल हालिया ही नहीं, बल्कि सभी परिवर्तनों को दिखाने के लिए ध्यानसूची को विस्तारित करें", + "tog-extendwatchlist": "केवल हालिया ही नहीं, बल्कि सभी परिवर्तनों को दिखाने के लिए ध्यानसूची को विस्तृत करें", "tog-usenewrc": "हाल में हुए परिवर्तनों और ध्यानसूची में परिवर्तनों को पृष्ठ अनुसार समूहों में बाँटें", - "tog-numberheadings": "शीर्षक स्व-क्रमांकित करें", + "tog-numberheadings": "स्व-क्रमांकित शीर्षक", "tog-showtoolbar": "सम्पादन उपकरण पट्टी दिखाएँ", "tog-editondblclick": "डबल क्लिक पर पृष्ठ संपादित करें", "tog-editsectiononrightclick": "अनुभाग शीर्षक पर दायाँ क्लिक करने पर अनुभाग सम्पादित करें", @@ -102,7 +105,7 @@ "tog-watchdefault": "मेरे द्वारा सम्पादित पृष्ठों और फ़ाइलों को मेरी ध्यानसूची में जोड़ें", "tog-watchmoves": "मेरे द्वारा स्थानांतरित पृष्ठों एवं फ़ाइलों को मेरी ध्यानसूची में जोड़ें", "tog-watchdeletion": "मेरे द्वारा हटाए गए पृष्ठों एवं फ़ाइलों को मेरी ध्यानसूची में जोड़ें", - "tog-watchuploads": "मेरे नए फ़ाइलों को मेरे ध्यानसूची में डालें।", + "tog-watchuploads": "मेरी ध्यानसूची में मेरी अपलोड करने वाली नई फ़ाइलें डालें|", "tog-watchrollback": "मेरे द्वारा प्रत्यापन्न (रोलबैक) किये हुये पृष्ठों को मेरी ध्यानसूची में जोड़ें।", "tog-minordefault": "मेरे सभी सम्पादनों को छोटे बदलाव के रूप में चिह्नित करें", "tog-previewontop": "सम्पादन सन्दूक से पहले झलक दिखायें", @@ -262,7 +265,7 @@ "viewdeleted_short": "देखें {{PLURAL:$1|एक हटाया गया सम्पादन|$1 हटाए गए सम्पादन}}", "protect": "सुरक्षित करें", "protect_change": "बदलें", - "unprotect": "असुरक्षित", + "unprotect": "सुरक्षा बदलें", "newpage": "नया पृष्ठ", "talkpagelinktext": "चर्चा", "specialpage": "विशेष पृष्ठ", @@ -295,9 +298,9 @@ "pool-queuefull": "पूल पंक्ति भरी हुई है", "pool-errorunknown": "अज्ञात त्रुटि", "pool-servererror": "पूल काउंटर सेवा उपलब्ध नहीं है ($1)।", - "poolcounter-usage-error": "उपयोग त्रुटि: $1", + "poolcounter-usage-error": "प्रयोग त्रुटि: $1", "aboutsite": "{{SITENAME}} के बारे में", - "aboutpage": "Project:परिचय", + "aboutpage": "Project:के बारे में", "copyright": "उपलब्ध सामग्री $1 के अधीन है जब तक अलग से उल्लेख ना किया गया हो।", "copyrightpage": "{{ns:project}}:कॉपीराइट", "currentevents": "हाल की घटनाएँ", @@ -526,7 +529,7 @@ "nosuchusershort": "\"$1\" नाम का कोई सदस्य नहीं है।\nकृपया अपनी दी हुई वर्तनी जाँचें।", "nouserspecified": "सदस्यनाम देना अनिवार्य है।", "login-userblocked": "यह सदस्य प्रतिबन्धित है। सत्रारम्भ की अनुमति नहीं है।", - "wrongpassword": "आपने जो कूटशब्द लिखा है वह गलत है। कृपया पुनः प्रयास करें।", + "wrongpassword": "आपने जो कूटशब्द लिखा है वह गलत है। \nकृपया पुनः प्रयास करें।", "wrongpasswordempty": "कूटशब्द खाली है।\nपुनः यत्न करें।", "passwordtooshort": "आपका कूटशब्द कम से कम {{PLURAL:$1|1 अक्षर|$1 अक्षरों}} का होना चाहिये।", "passwordtoolong": "पासवर्ड {{PLURAL:$1|1 वर्ण|$1 वर्णों}} से ज़्यादा लम्बे नही हो सकते।", @@ -973,6 +976,8 @@ "diff-multi-sameuser": "(इसी सदस्य द्वारा {{PLURAL:$1|किया गया बीच का एक अवतरण नहीं दर्शाया गया|किये गये बीच के $1 अवतरण नहीं दर्शाए गए}})", "diff-multi-otherusers": "({{PLURAL:$2|एक अन्य सदस्य|$2 सदस्यों}} द्वारा {{PLURAL:$1|किया गया बीच का एक अवतरण नहीं दर्शाया गया|किये गये बीच के $1 अवतरण नहीं दर्शाए गए}})", "diff-multi-manyusers": "({{PLURAL:$2|एक योगदानकर्ता|$2 योगदानकर्ताओं}} द्वारा {{PLURAL:$1|किया बीच का एक|किए बीच के $1}} अवतरण दर्शाए नहीं हैं।)", + "diff-paragraph-moved-tonew": "अनुच्छेद को स्थानांतरित कर दिया गया था| नए स्थान पर जाने के लिए क्लिक करें|", + "diff-paragraph-moved-toold": "पैराग्राफ को स्थानांतरित कर दिया गया था| पुराने स्थान पर जाने के लिए क्लिक करें|", "difference-missing-revision": "इस अंतर {{PLURAL:$2|का एक अवतरण|के $2 अवतरण}} ($1) नहीं {{PLURAL:$2|पाया गया|पाए गए}}।\n\nयह आम तौर पर एक हटाए गए पृष्ठ के अवतरणों में अंतर ढूँढने पर होता है। अधिक जानकारी [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} हटाने के लॉग] में पायी जा सकती है।", "searchresults": "खोज परिणाम", "searchresults-title": "\"$1\" के लिए खोज परिणाम", @@ -1066,7 +1071,7 @@ "recentchangesdays-max": "अधिकतम $1 {{PLURAL:$1|दिन}}", "recentchangescount": "मूल रूप से कितने संपादन दिखाएँ:", "prefs-help-recentchangescount": "इसमें हाल के बदलाव, पृष्ठ इतिहास व लॉग शामिल हैं।", - "prefs-help-watchlist-token2": "यह आपकी ध्यानसूची की वेब फ़ीड की गोपनीय चाबी है।\nयह जिसके भी पास होगी वह आपकी ध्यानसूची पढ़ सकेगा, इसिलए इसे किसी के साथ बांटियेगा नहीं।\n[[Special:ResetTokens|इसे रीसेट करने के लिए यहाँ क्लिक करें]]।", + "prefs-help-watchlist-token2": "यह आपकी ध्यानसूची की वेब फ़ीड की गोपनीय चाबी है।\nयह जिसके भी पास होगी वह आपकी ध्यानसूची पढ़ सकेगा, इसिलए इसे किसी के साथ बांटियेगा नहीं।\nअगर आप की जरूरत है, [[Special:ResetTokens|आप इसे रीसेट कर सकते हैं]]।", "savedprefs": "आपकी वरीयताएँ संजोई गई हैं।", "savedrights": "सदस्य {{GENDER:$1|$1}} का सदस्य अधिकार सहेजा गया।", "timezonelegend": "समयमंडल:", @@ -1086,6 +1091,7 @@ "timezoneregion-indian": "हिंद महासागर", "timezoneregion-pacific": "प्रशांत महासागर", "allowemail": "अन्य सदस्यों से ई-मेल सक्षम करें", + "email-allow-new-users-label": "एकदम नये उपयोगकर्ताओं को ईमेल की अनुमति दें", "email-blacklist-label": "इन उपयोगकर्ताओं को मुझे ईमेल भेजने से रोकना:", "prefs-searchoptions": "खोज", "prefs-namespaces": "नामस्थान", @@ -1255,6 +1261,7 @@ "right-siteadmin": "डाटाबेस को ताला लगायें या खोलें", "right-override-export-depth": "पृष्ठ निर्यात करें, पाँच स्तर की गहराई तक जुड़े हुए पृष्ठों समेत", "right-sendemail": "अन्य सदस्यों को ई-मेल भेजें", + "right-sendemail-new-users": "एक भी लॉग इन क्रिया नहीं करने वाले उपयोगकर्ताओं को ईमेल भेजें", "right-managechangetags": "डेटाबेस से [[Special:Tags|चिप्पियाँ]] बनायें और हटायें", "right-applychangetags": "प्रयोग में लाइये [[Special:Tags|tags]] किसी के बदलाव के साथ।", "right-changetags": "जमा करो और हटाओ स्वतंत्र [[Special:Tags|टैग]] व्यक्तिगत अवतरणों और लॉग प्रविक्तियों पर", @@ -1355,6 +1362,8 @@ "recentchanges-summary": "इस विकि पर हाल में हुए बदलाव इस पन्ने पर देखे जा सकते हैं।", "recentchanges-noresult": "इस अवधि के दौरान इन मापदंडों को पूर्ण करते कोई परिवर्तन नहीं किए गए हैं।", "recentchanges-timeout": "इस खोज का समय समाप्त हो गया है आप विभिन्न खोज मापदंडों की कोशिश करना चाहेंगे।", + "recentchanges-network": "तकनीकी त्रुटि के कारण, कोई भी परिणाम लोड नहीं किया जा सकता। कृपया पृष्ठ को रिफ्रेश करते रहें।", + "recentchanges-notargetpage": "उस पृष्ठ से संबंधित ऊपर परिवर्तन देखने के लिए पृष्ठ का नाम डालें|", "recentchanges-feed-description": "इस विकि पर हाल में हुए बदलाव इस फ़ीड में देखे जा सकते हैं।", "recentchanges-label-newpage": "इस संपादन से नया पृष्ठ बना", "recentchanges-label-minor": "यह एक छोटा सम्पादन है", @@ -1370,7 +1379,9 @@ "rcfilters-group-results-by-page": "पेज द्वारा समूह परिणाम", "rcfilters-activefilters": "सक्रिय फिल्टर", "rcfilters-advancedfilters": "उन्नत फ़िल्टर", - "rcfilters-limit-title": "दिखाने के लिए बदलाव", + "rcfilters-limit-title": "दिखाने के लिए परिणाम", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|बदलाव|$1 परिवर्तन}}, $2", + "rcfilters-date-popup-title": "खोजने के लिए समय अवधि", "rcfilters-days-title": "कुछ दिनों के", "rcfilters-hours-title": "कुछ घंटों के", "rcfilters-days-show-days": "$1 {{PLURAL:$1|दिन}}", @@ -1390,10 +1401,11 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "डिफ़ॉल्ट फ़िल्टर बनाएं", "rcfilters-savedqueries-cancel-label": "रद्द करें", "rcfilters-savedqueries-add-new-title": "वर्तमान फ़िल्टर सेटिंग को सहेजें", + "rcfilters-savedqueries-already-saved": "ये फ़िल्टर पहले ही सुरक्षित कर लिए गए हैं| नए सुरक्षित फ़िल्टर बनाने के लिए अपनी सेटिंग बदले|", "rcfilters-restore-default-filters": "मूलभूत फिल्टर पुनर्स्थापित करे", "rcfilters-clear-all-filters": "सभी फिल्टर हटाएँ", "rcfilters-show-new-changes": "नवीनतम बदलाव दिखाएँ", - "rcfilters-search-placeholder": "हाल में हुए बदलाव फ़िल्टर (ब्राउज़ या टाइप करना आरंभ करें)", + "rcfilters-search-placeholder": "परिवर्तन फ़िल्टर करें (मेन्यू का इस्तेमाल करें या फ़िल्टर नाम के लिए खोज करें)", "rcfilters-invalid-filter": "अमान्य फ़िल्टर", "rcfilters-empty-filter": "कोई सक्रिय फिल्टर नहीं। सभी योगदान दिखाए गए है।", "rcfilters-filterlist-title": "फिल्टर", @@ -1483,6 +1495,11 @@ "rcfilters-watchlist-showupdated": "उन पन्नों में परिवर्तन जिनपर आप परिवर्तन के बाद से नहीं गए हैं, ठोस चिन्ह के साथ <strong>bold</strong> दिखाए गए हैं।", "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>$2</strong> के बाद से (<strong>$1</strong> तक) {{PLURAL:$5|हुआ बदलाव दर्शाया गया है|हुए बदलाव दर्शाए गये हैं}}।", "rclistfromreset": "चुने दिनांक पहले जैसा करें", "rclistfrom": "$3 $2 से नये बदलाव दिखाएँ", @@ -1527,7 +1544,7 @@ "recentchangeslinked-feed": "पृष्ठ से जुड़े बदलाव", "recentchangeslinked-toolbox": "पृष्ठ से जुड़े बदलाव", "recentchangeslinked-title": "\"$1\" से जुड़े बदलाव", - "recentchangeslinked-summary": "यह पृष्ठ किसी विशिष्ट पृष्ठ से जुड़े पृष्ठों (या किसी श्रेणी में श्रेणीबद्ध पृष्ठों) में हाल में हुए बदलावों की सूची दर्शाता है।\n[[Special:Watchlist|आपकी ध्यानसूची]] में मौजूद पृष्ठ '''मोटे''' अक्षरों में दिखेंगे।", + "recentchangeslinked-summary": "उस पृष्ठ पर या उस पृष्ठ से जुड़े पृष्ठों पर परिवर्तन देखने के लिए पृष्ठ का नाम डालें। (एक वर्ग के सदस्यों को देखने के लिए, श्रेणी दर्ज करें: श्रेणी का नाम)| [[Special:Watchlist|your Watchlist]] के पृष्ठों में परिवर्तन <strong>बोल्ड</strong> में हैं|", "recentchangeslinked-page": "पृष्ठ नाम:", "recentchangeslinked-to": "इसके बदले में दिये हुए पृष्ठसे जुडे पन्नोंके बदलाव दर्शायें", "recentchanges-page-added-to-category": "[[:$1]] श्रेणी में जुड़ा", @@ -1611,7 +1628,7 @@ "uploaded-script-svg": "अपलोड की गयी एसवीजी फ़ाइल में स्क्रीप्ट अवयव \"$1\" पाया गया।", "uploaded-hostile-svg": "अपलोड की गयी एसवीजी फाइल के शैली अवयव में असुरक्षित सीएसएस पायी गयी।", "uploaded-event-handler-on-svg": "सेटिंग ईवेंट हैंडलर (आयोजन प्रबन्धनकर्ता वरियता) <code>$1=\"$2\"</code> एसवीजी फ़ाइल में अनुमत नहीं है।", - "uploaded-href-attribute-svg": "href केवल एसवीजी फ़ाइल हेतु ही http:// या https:// उपयोग करने देता है। <code><$1 $2=\"$3\"></code>", + "uploaded-href-attribute-svg": "<a> तत्व केवल डेटा से लिंक किया जा सकता है: (अंतःस्थापित दस्तावेज), http:// or https://, या टुकड़ा (#, समरूप दस्तावेज) लक्ष्य| अन्य तत्वों के लिए, जैसे <image>, केवल डेटा: और टुकड़ों की अनुमति है| अपने एसवीजी को निर्यात करते समय छवियों को अंतःस्थापित करने का प्रयास करें| मिला <code> <$1 $2=\"$3\"></code>।", "uploaded-href-unsafe-target-svg": "अपलोड की गयी फ़ाइल में असुरक्षित लक्ष्य <code><$1 $2=\"$3\"></code> पाये गए।", "uploaded-animate-svg": "चिप्पि \"animate\" पायी गई जिससे href परिवर्तित हो सकता है, अपलोड की गयी फ़ाइल में \"from\" विशेषता <code><$1 $2=\"$3\"></code> काम में ली जा रही है।", "uploaded-setting-event-handler-svg": "विकल्प आयोजन-संभालने वाला अवरोधित है, एसवीजी फ़ाइल में मिला <code><$1 $2=\"$3\"></code> है।", @@ -1720,6 +1737,25 @@ "uploadstash-refresh": "फ़ाइलों की सूची रिफ़्रेश करें", "uploadstash-thumbnail": "छवि देखें", "uploadstash-exception": "गुप्त कोष में अपलोड स्टोर नहीं किया जा सका ($1): \"$2\".", + "uploadstash-bad-path": "पथ मौजूद नहीं है|", + "uploadstash-bad-path-invalid": "पथ मौजूद नहीं है|", + "uploadstash-bad-path-unknown-type": "अज्ञात प्रकार \"$1\"", + "uploadstash-bad-path-unrecognized-thumb-name": "अपरिचित अंगूठे का नाम|", + "uploadstash-bad-path-no-handler": "फ़ाइल $2 में से $1 के लिए कोई प्रहस्तक नहीं मिला|", + "uploadstash-bad-path-bad-format": "कुंजी \"$1\" एक उचित प्रारूप में नहीं है|", + "uploadstash-file-not-found": "छिपाने की जगह में कुंजी \"$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\nयूआरएल = $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": "आयतन शून्य है|", + "uploadstash-zero-length": "फ़ाइल शून्य लंबाई की है|", "invalid-chunk-offset": "अग्राह्य चंक ऑफ़सेट", "img-auth-accessdenied": "अनुमति नहीं है", "img-auth-nopathinfo": "PATH_INFO मौजूद नहीं है।\nआपके सर्वर में इस जानकारी को भेजने के लिए जमाव नहीं है।\nयह सी॰जी॰आई-आधारित हो सकता है और img_auth को स्वीकार नहीं करता है।\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization देखें।", @@ -2190,16 +2226,16 @@ "watcherrortext": "\"$1\" के लिये आपकी ध्यानसूची सेटिंग बदलते समय त्रुटि हुई।", "enotif_reset": "सभी पृष्ठ देखे हुए दर्शाएँ", "enotif_impersonal_salutation": "{{SITENAME}} सदस्य", - "enotif_subject_deleted": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने हटा दिया है", - "enotif_subject_created": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने बना दिया है", - "enotif_subject_moved": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने स्थानांतरित कर दिया है", - "enotif_subject_restored": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने पुनर्स्थापित कर दिया है", - "enotif_subject_changed": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने परिवर्तित किया है", - "enotif_body_intro_deleted": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने $PAGEEDITDATE को हटा दिया है, देखें <$3>।", - "enotif_body_intro_created": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने $PAGEEDITDATE को बनाया है, वर्तमान अवतरण के लिए $3 देखें।", - "enotif_body_intro_moved": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने $PAGEEDITDATE को स्थानांतरित किया है, वर्तमान अवतरण के लिए $3 देखें।", - "enotif_body_intro_restored": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने $PAGEEDITDATE को पुनर्स्थापित किया है, वर्तमान अवतरण के लिए $3 देखें।", - "enotif_body_intro_changed": "{{SITENAME}} पृष्ठ $1 को {{gender:$2|$2}} ने $PAGEEDITDATE को परिवर्तित किया है, वर्तमान अवतरण के लिए $3 देखें।", + "enotif_subject_deleted": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने हटा दिया है", + "enotif_subject_created": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने बना दिया है", + "enotif_subject_moved": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|चले गए}} $2 द्वारा चले जा चुका है", + "enotif_subject_restored": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने पुनर्स्थापित कर दिया है", + "enotif_subject_changed": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने परिवर्तित किया है", + "enotif_body_intro_deleted": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने $PAGEEDITDATE को हटा दिया है, देखें <$3>।", + "enotif_body_intro_created": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने $PAGEEDITDATE को बनाया है, वर्तमान अवतरण के लिए $3 देखें।", + "enotif_body_intro_moved": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने $PAGEEDITDATE को स्थानांतरित किया है, वर्तमान अवतरण के लिए $3 देखें।", + "enotif_body_intro_restored": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने $PAGEEDITDATE को पुनर्स्थापित किया है, वर्तमान अवतरण के लिए $3 देखें।", + "enotif_body_intro_changed": "{{SITENAME}} पृष्ठ $1 को {{GENDER:$2|$2}} ने $PAGEEDITDATE को परिवर्तित किया है, वर्तमान अवतरण के लिए $3 देखें।", "enotif_lastvisited": "आपकी आखिरी भेंट के बाद हुए बदलाव देखने के लिये $1 देखें।", "enotif_lastdiff": "इस बदलाव को देखने के लिये $1 देखें।", "enotif_anon_editor": "अनामक सदस्य $1", @@ -2668,6 +2704,8 @@ "import-mapping-namespace": "किसी नामस्थान पर आयात करें", "import-mapping-subpage": "निम्न लिखित पृष्ठ के उपपृष्ठ के रूप में आयात करें:", "import-upload-filename": "संचिका नाम:", + "import-upload-username-prefix": "इंटरविकी उपसर्ग:", + "import-assign-known-users": "स्थानीय उपयोगकर्ताओं को संपादन नियुक्त करें जहां नामित उपयोगकर्ता स्थानीय स्तर पर मौजूद है", "import-comment": "टिप्पणी:", "importtext": "कृपया स्रोत विकि से संचिका निर्यातित करने के लिए [[Special:Export|निर्यात सुविधा]] का इस्तेमाल करें।\nइसे अपने संगणक पर सँजो के यहाँ चढ़ा दें।", "importstart": "पृष्ठ आयात कर रहें हैं...", @@ -2676,6 +2714,7 @@ "imported-log-entries": "आयातित $1 {{PLURAL:$1|लॉग प्रविष्टि|लॉग प्रविष्टियाँ}}.\nजब कभी कोई फाइल आपको import करनी हो", "importfailed": "आयात विफल हुआ: <nowiki>$1</nowiki>", "importunknownsource": "अज्ञात आयात स्रोत प्रकार", + "importnoprefix": "कोई इंटरविकी उपसर्ग नहीं दिया गया था", "importcantopen": "आयात फ़ाईल खोल नहीं पायें", "importbadinterwiki": "अवैध अन्तरविकि कड़ी", "importsuccess": "आयात सफल हुआ!", @@ -3358,6 +3397,8 @@ "autosumm-blank": "पृष्ठ को खाली किया", "autosumm-replace": "पृष्ठ को '$1' से बदल रहा है।", "autoredircomment": "[[$1]] को अनुप्रेषित", + "autosumm-removed-redirect": "हटाया गया रीडायरेक्ट [[$1]] के लिए", + "autosumm-changed-redirect-target": "[[$1]] से [[$2]] तक पुन्नः प्रेषित लक्ष्य बदल गया|", "autosumm-new": "'$1' के साथ नया पृष्ठ बनाया", "autosumm-newblank": "रिक्त पृष्ठ बनाया", "size-bytes": "$1 B", @@ -3528,6 +3569,20 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|टैग}}]]: $2)", "tag-mw-contentmodelchange": "सामग्री मॉडल परिवर्तन", "tag-mw-contentmodelchange-description": "पृष्ठ [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel सामग्री मॉडल को परिवर्तित करें] के संपादन।", + "tag-mw-new-redirect": "नया पुन्नः प्रेषित लक्ष्य", + "tag-mw-new-redirect-description": "बदलाव जो एक नया रीडायरेक्ट बनाते हैं या पुनर्निर्देशन के लिए एक पृष्ठ बदलते हैं", + "tag-mw-removed-redirect": "हटाया गया पुनर्निर्देशन", + "tag-mw-removed-redirect-description": "संपादन जो किसी मौजूदा रीडायरेक्ट को गैर रीडायरेक्ट में बदलता है", + "tag-mw-changed-redirect-target": "रीडायरेक्ट लक्ष्य बदल गया", + "tag-mw-changed-redirect-target-description": "संपादन जो रीडायरेक्ट लक्ष्य को बदलते हैं", + "tag-mw-blank": "रिक्त", + "tag-mw-blank-description": "सम्पादन जो पृष्ट को खाली कर देता है", + "tag-mw-replace": "बदला गया", + "tag-mw-replace-description": "संपादन जिसने 90% से अधिक पृष्ट की सामग्री को हटा दिया", + "tag-mw-rollback": "पीछे हटना", + "tag-mw-rollback-description": "संपादन जो रोलबैक लिंक का उपयोग करके पिछला संपादन वापस रोल करता है", + "tag-mw-undo": "किए हुए कार्य को पूर्वत करना", + "tag-mw-undo-description": "संपादन जो पिछले लिंक का उपयोग करके पिछले संपादन को पूर्वत करता है", "tags-title": "चिप्पियाँ", "tags-intro": "यह पृष्ठ अर्थ सहित वह चिप्पियाँ दर्शाता है जिनका कोई तंत्रांश किसी संपादन पर निशान लगाने के लिए इस्तेमाल कर सकता है।", "tags-tag": "चिप्पी का नाम", diff --git a/languages/i18n/hr.json b/languages/i18n/hr.json index 107c891812..53328dfa87 100644 --- a/languages/i18n/hr.json +++ b/languages/i18n/hr.json @@ -196,7 +196,7 @@ "go": "Kreni", "searcharticle": "Kreni", "history": "Povijest stranice", - "history_short": "Povijest", + "history_short": "Stare izmjene", "history_small": "povijest", "updatedmarker": "obnovljeno od posljednjeg posjeta", "printableversion": "Inačica za ispis", @@ -467,9 +467,11 @@ "nosuchusershort": "Ne postoji suradnik s imenom \"$1\". Provjerite VaÅ¡ unos.", "nouserspecified": "Molimo navedite suradničko ime.", "login-userblocked": "Ovaj je suradnik blokiran. Prijava nije dopuÅ¡tena.", - "wrongpassword": "Zaporka koju ste unijeli nije ispravna. Molimo Vas, pokuÅ¡ajte ponovo.", + "wrongpassword": "Suradničko ime ili zaporka koju ste unijeli nije ispravno. Molimo Vas, pokuÅ¡ajte ponovo.", "wrongpasswordempty": "Niste unijeli zaporku. PokuÅ¡ajte ponovno.", "passwordtooshort": "Zaporka mora sadržavati najmanje {{PLURAL:$1|1 znak|$1 znaka|$1 znakova}}.", + "passwordtoolong": "Zaporke ne mogu biti duže od {{PLURAL:$1|jednoga znaka|$1 znaka|$1 znakova}}.", + "passwordtoopopular": "Uobičajeno upotrebljavane zaporke ne može se koristiti. Molimo Vas da izaberete Å¡to jedinstveniju zaporku.", "password-name-match": "VaÅ¡a zaporka mora biti različita od VaÅ¡eg suradničkog imena.", "password-login-forbidden": "Uporaba ovog suradničkog imena i lozinke nije dozvoljena.", "mailmypassword": "PoÅ¡alji mi novu zaporku", @@ -567,6 +569,7 @@ "passwordreset-emailsentemail": "Ako je ova adresa povezana s VaÅ¡im suradničkim računom, na nju će biti poslan podsjetnik na zaporku.", "passwordreset-emailsentusername": "Ukoliko je ova adresa povezana s VaÅ¡im suradničkim računom, na istu će biti poslan podsjetnik sa zaporkom.", "passwordreset-invalidemail": "Nevaljala adresa e-poÅ¡te", + "passwordreset-nodata": "Niste naveli ni suradničko ime, a niti adresu e-poÅ¡te", "changeemail": "Promijeni ili izbriÅ¡i e-mail adresu", "changeemail-header": "Ispunite ovaj obrazac da biste promijenili svoju adresu e-poÅ¡te. Ukoliko želite ukloniti povezanost svoje adrese e-poÅ¡te i suradničkoga računa, prilikom popunjavanja obrasca ostavite prazno polje umjesto upisivanja nove adrese e-poÅ¡te.", "changeemail-no-info": "Morate biti prijavljeni da biste izravno pristupili ovoj stranici.", @@ -628,6 +631,7 @@ "blockedtitle": "Suradnik je blokiran", "blockedtext": "<strong>VaÅ¡e je suradničko ime blokirano ili je VaÅ¡a IP adresa blokirana.</strong>\n\nBlokirao Vas je $1.\nRazlog blokiranja je sljedeći: <em>$2</em>.\n\n* Početak blokade: $8\n* Blokada istječe: $6\n* Blokirani suradnik: $7\n\nMožete kontaktirati $1 ili jednog od [[{{MediaWiki:Grouppage-sysop}}|administratora]] kako bi Vam pojasnili razlog blokiranja.\n\nPrimijetite da ne možete koristiti opciju \"PoÅ¡alji e-poruku suradnici – suradniku\" ako niste upisali valjanu adresu e-poÅ¡te u VaÅ¡im [[Special:Preferences|suradničkim postavkama]] i ako niste u tome onemogućeni prilikom blokiranja.\n\nVaÅ¡a trenutačna IP adresa je $3, a oznaka bloka #$5. Molimo uvrstite sve gore navedene detalje u svaki upit koji napiÅ¡ete.", "autoblockedtext": "VaÅ¡a IP adresa automatski je blokirana zbog toga Å¡to ju je koristio drugi suradnik, kojeg je blokirao $1.\nRazlog blokiranja je sljedeći:\n\n:<em>$2</em>\n\n* Početak blokade: $8\n* Blokada istječe: $6\n* Blokirani suradnik: $7\n\nMožete kontaktirati $1 ili jednog od [[{{MediaWiki:Grouppage-sysop}}|administratora]] kako bi Vam pojasnili razlog blokiranja.\n\nPrimijetite da ne možete rabiti opciju \"PoÅ¡alji e-poruku suradnici – suradniku\" ako niste upisali valjanu adresu e-poÅ¡te u VaÅ¡im [[Special:Preferences|suradničkim postavkama]] i ako niste u tome onemogućeni prilikom blokiranja.\n\nVaÅ¡a trenutačna IP adresa je $3, a oznaka bloka #$5. Molimo uvrstite sve gore navedene detalje u svaki upit koji napiÅ¡ete.", + "systemblockedtext": "MediaWiki je automatski blokirao VaÅ¡e suradničko ime ili IP-adresu.\nDano je sljedeće obrazloženje:\n\n:<em>$2</em>\n\n* Početak blokade: $8\n* Istek blokade: $6\n* Blokada je namijenjena za: $7\n\nVaÅ¡a trenutačna IP-adresa je $3.\nAko imate pitanja u svezi s blokadom, priložite sve pojedinosti koje su prethodno navedene.", "blockednoreason": "bez obrazloženja", "whitelistedittext": "Za uređivanje stranice molimo $1.", "confirmedittext": "Morate potvrditi VaÅ¡u adresu e-poÅ¡te prije nego Å¡to Vam bude omogućeno uređivanje. Molim unesite i ovjerite VaÅ¡u adresu e-poÅ¡te u [[Special:Preferences|suradničkim postavkama]].", @@ -685,7 +689,7 @@ "cascadeprotectedwarning": "'''UPOZORENJE:''' Ova stranica je zaključana i mogu je uređivati samo suradnici s administratorskim pravima, jer je uključena u {{PLURAL:$1|slijedeću stranicu|slijedeće stranice}} koje su zaÅ¡tićene \"prenosivom\" zaÅ¡titom:", "titleprotectedwarning": "<strong>Upozorenje: Ova je stranica zaključana i samo ju suradnici s [[Special:ListGroupRights|određenim pravima]] mogu stvoriti.</strong>\nPosljednja stavka u evidenciji navedena je niže kao napomena:", "templatesused": "{{PLURAL:$1|Predložak koji se rabi|PredloÅ¡ci koji se rabe}} na ovoj stranici:", - "templatesusedpreview": "{{PLURAL:$1|Predložak koji se rabi|PredloÅ¡ci koji se rabe}} u ovom predpregledu:", + "templatesusedpreview": "{{PLURAL:$1|Predložak koji se rabi|PredloÅ¡ci koji se rabe}} u ovom pretpregledu:", "templatesusedsection": "{{PLURAL:$1|Predložak koji se rabi|PredloÅ¡ci koji se rabe}} u ovom odjeljku:", "template-protected": "(zaÅ¡tićen)", "template-semiprotected": "(djelomično zaÅ¡tićen)", @@ -940,6 +944,7 @@ "search-external": "Vanjski pretraživač", "searchdisabled": "<p>Oprostite! Pretraga po cjelokupnoj bazi je zbog bržeg rada projekta {{SITENAME}} trenutačno onemogućena. Možete se poslužiti tražilicom Google.</p>", "search-error": "GreÅ¡ka prilikom pretrage: $1", + "search-warning": "DoÅ¡lo je do upozorenja prilikom pretraživanja: $1", "preferences": "Postavke", "mypreferences": "Postavke", "prefs-edits": "Broj uređivanja:", @@ -999,6 +1004,7 @@ "timezoneregion-indian": "Indijski ocean", "timezoneregion-pacific": "Tihi ocean", "allowemail": "Omogući primanje e-maila od drugih suradnika", + "email-allow-new-users-label": "Dopusti e-poruke od posve novopridoÅ¡lih suradnika", "email-blacklist-label": "Zabrani sljedećim suradnicima da mi Å¡alju e-poruke:", "prefs-searchoptions": "Način traženja", "prefs-namespaces": "Imenski prostori", @@ -1168,11 +1174,15 @@ "right-siteadmin": "Zaključavanje i otključavanje baze podataka", "right-override-export-depth": "Izvezi stranice uključujući i povezane stranice do dubine od 5", "right-sendemail": "Slanje e-poÅ¡te drugim suradnicima", + "right-sendemail-new-users": "PoÅ¡alji e-poruku suradnicima bez radnji zabilježenih u evidencijama", + "right-managechangetags": "Stvaranje i (de)aktiviranje [[Special:Tags|oznaka]]", "grant-generic": "Snop prava »$1«", "grant-group-email": "PoÅ¡alji e-mail", + "grant-group-other": "Raznovrsno djelovanje", "grant-blockusers": "Blokiraj i odblokiraj korisnike", "grant-createaccount": "Otvori račune", "grant-createeditmovepage": "Stvaranje, uređivanje i premjeÅ¡tanje stranica", + "grant-delete": "Brisanje stranica, izmjena i unosa u evidencijama", "grant-editmyoptions": "Uređivanje vlastitih suradničkih postavki", "grant-editmywatchlist": "Uređivanje VaÅ¡ega popisa praćenih stranica", "grant-editpage": "Uređivanje postojećih stranica", @@ -1226,6 +1236,7 @@ "action-userrights-interwiki": "uređivanje suradničkih prava suradnika na drugim wikijima", "action-siteadmin": "zaključavanje ili otključavanje baze podataka", "action-sendemail": "poÅ¡alji e-poÅ¡tu", + "action-editmyoptions": "uređujete vlastite postavke", "action-editmywatchlist": "uredite svoj popis praćenih stranica", "action-viewmywatchlist": "pregled popisa VaÅ¡ih praćenih stranica", "action-viewmyprivateinfo": "pregled VaÅ¡ih privatnih podataka", @@ -1248,18 +1259,22 @@ "recentchanges-legend-newpage": "Nova stranica", "recentchanges-legend-plusminus": "(<em>±123</em>)", "recentchanges-submit": "Prikaži", + "rcfilters-tag-remove": "Ukloni »$1«", "rcfilters-legend-heading": "<strong>Popis kratica:</strong>", "rcfilters-other-review-tools": "Ostali alati za pregledavanje:", "rcfilters-group-results-by-page": "Grupiranje rezultata po stranici", "rcfilters-activefilters": "Aktivni filtri", "rcfilters-advancedfilters": "Napredni filtri", - "rcfilters-limit-title": "Prikaz izmjena", + "rcfilters-limit-title": "Rezultata za prikaz", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|$1 izmjena|$1 izmjene|$1 izmjena}}, $2", + "rcfilters-date-popup-title": "Vremensko razdoblje za pretragu", "rcfilters-days-title": "Nedavnih dana", "rcfilters-hours-title": "Nedavnih sati", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dan|dana}}", "rcfilters-days-show-hours": "$1 {{PLURAL:$1|sat|sata|sati}}", + "rcfilters-highlighted-filters-list": "Istaknuto: $1", "rcfilters-quickfilters": "Spremljeni filtri", - "rcfilters-quickfilters-placeholder-title": "JoÅ¡ nema spremljenih poveznica", + "rcfilters-quickfilters-placeholder-title": "JoÅ¡ nema spremljenih filtara", "rcfilters-quickfilters-placeholder-description": "Da biste spremili postavke filtra i rabili ih poslije, kliknite ispod na oznaku dočitnice u polju Aktivni filtri.", "rcfilters-savedqueries-defaultlabel": "Spremljeni filteri", "rcfilters-savedqueries-rename": "Preimenuj", @@ -1269,6 +1284,7 @@ "rcfilters-savedqueries-new-name-label": "Naziv", "rcfilters-savedqueries-new-name-placeholder": "OpiÅ¡ite svrhu filtra", "rcfilters-savedqueries-apply-label": "Stvori filtar", + "rcfilters-savedqueries-apply-and-setdefault-label": "Stvori predodređeni filtar", "rcfilters-savedqueries-cancel-label": "Otkaži", "rcfilters-savedqueries-add-new-title": "Spremi trenutačne postavke filtra", "rcfilters-restore-default-filters": "Vrati zadane filtre", @@ -1324,6 +1340,10 @@ "rcfilters-filter-watchlist-watchednew-description": "Izmjene stranica na popisu praćenja koje niste posjetili od vremena učinjenih izmjena.", "rcfilters-filter-watchlist-notwatched-label": "Izvan popisa praćenja", "rcfilters-filter-watchlist-notwatched-description": "Sve izmjene na stranicama osim onih na popisu praćenja.", + "rcfilters-filtergroup-watchlistactivity": "Aktivnost na popisu praćenja", + "rcfilters-filter-watchlistactivity-unseen-label": "Nepregledane izmjene", + "rcfilters-filter-watchlistactivity-seen-label": "Pregledane izmjene", + "rcfilters-filter-watchlistactivity-seen-description": "Izmjene na stranicama koje ste posjetili otkako su načinjene.", "rcfilters-filtergroup-changetype": "Vrste promjena", "rcfilters-filter-pageedits-label": "Uređivanja stranica", "rcfilters-filter-pageedits-description": "Uređivanja wikisadržaja, rasprave, opisi kategorija...", @@ -1348,6 +1368,8 @@ "rcfilters-liveupdates-button": "Ažuriranja uživo", "rcfilters-liveupdates-button-title-on": "Isključi ažuriranja uživo", "rcfilters-liveupdates-button-title-off": "Prikaži nove izmjene uživo", + "rcfilters-watchlist-markseen-button": "Označi sve izmjene kao pregledane", + "rcfilters-watchlist-edit-watchlist-button": "Izmijeni popis praćenih stranica", "rcfilters-preference-label": "Skrij poboljÅ¡anu inačicu nedavnih promjena", "rcfilters-preference-help": "Vraća natrag stanje prije redizajna sučelja 2017., te svih oruđa dodanih tada i poslije toga.", "rcnotefrom": "Ispod {{PLURAL:$5|je izmjena|su izmjene}} od <strong>$3, $4</strong> (prikazano ih do <strong>$1</strong>).", @@ -1552,6 +1574,20 @@ "uploadstash-refresh": "Osvježi popis datoteka", "uploadstash-thumbnail": "pogledaj kao minijaturu", "uploadstash-exception": "Postavljanje u zalihu nije bilo moguće ($1): »$2«.", + "uploadstash-bad-path": "Putanja ne postoji.", + "uploadstash-bad-path-invalid": "Putanja nije valjana.", + "uploadstash-bad-path-unknown-type": "Neprepoznata vrsta »$1«.", + "uploadstash-bad-path-unrecognized-thumb-name": "Neprepoznano ime sličice.", + "uploadstash-file-not-found-no-thumb": "Ne mogu dobiti minijaturu.", + "uploadstash-file-not-found-no-local-path": "Nema lokalne putanje za umanjenu stavku.", + "uploadstash-file-not-found-no-object": "Ne mogu načiniti lokalni podatkovni objekt za minijaturu.", + "uploadstash-file-not-found-no-remote-thumb": "Dobavljanje minijature nije uspjelo: $1\nURL = $2", + "uploadstash-file-not-found-missing-content-type": "Nedostaje zaglavlje za vrstu sadržaja.", + "uploadstash-file-not-found-not-exists": "Ne mogu naći putanju ili ovo nije uobičajena datoteka.", + "uploadstash-file-too-large": "Ne mogu poslužiti datoteku veću od $1 {{PLURAL:$1|bajta|bajtova}}.", + "uploadstash-not-logged-in": "Nije prijavljen ijedan suradnik. Datoteke moraju biti povezane sa suradnicima koji ih postavljaju.", + "uploadstash-no-extension": "Nema traženog proÅ¡irenja.", + "uploadstash-zero-length": "Veličina datoteke je nula bajtova.", "invalid-chunk-offset": "Nevaljana točka nastavka snimanja", "img-auth-accessdenied": "Pristup onemogućen", "img-auth-nopathinfo": "Nedostaje PATH_INFO.\nVaÅ¡ poslužitelj nije postavljen da prosljeđuje ovu informaciju.\nMožda se temelji na CGI skripti i ne može podržavati img_auth.\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization?uselang=hr Pogledajte stranicu o autorizaciji slika]", @@ -1696,6 +1732,7 @@ "pageswithprop-legend": "Stranice s određenim osobinama", "pageswithprop-text": "Ovo je popis stranica koje koriste određene osobine stranica.", "pageswithprop-prop": "Ime osobine:", + "pageswithprop-reverse": "Sortiraj obrnutim redoslijedom", "pageswithprop-sortbyvalue": "Sortiraj po vrijednosti svojstva", "pageswithprop-submit": "Idi", "doubleredirects": "Dvostruka preusmjeravanja", @@ -1826,7 +1863,7 @@ "magiclink-tracking-isbn": "Stranice s čarobnim ISBN poveznicama", "magiclink-tracking-isbn-desc": "Ova stranica rabi čarobne ISBN poveznice. Za njihovu migraciju vidi [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org].", "specialloguserlabel": "Suradnik:", - "speciallogtitlelabel": "Cilj (naslov ili suradnik):", + "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:suradničko ime):", "log": "Evidencije", "logeventslist-submit": "Prikaži", "all-logs-page": "Sve javne evidencije", @@ -1919,7 +1956,7 @@ "defemailsubject": "{{SITENAME}} e-mail od suradnika \"$1\"", "usermaildisabled": "Suradnička e-poÅ¡ta je onemogućena", "usermaildisabledtext": "Ne možete slati e-poÅ¡tu drugim suradnicima na ovom wikiju", - "noemailtitle": "Nema adrese primaoca", + "noemailtitle": "Nema adrese e-poÅ¡te", "noemailtext": "Ovaj suradnik nije odredio valjanu adresu e-poÅ¡te.", "nowikiemailtext": "Ovaj suradnik je odlučio ne primati e-mail od drugih suradnika.", "emailnotarget": "Nepostojeće ili nevažeće suradničko ime za primatelja.", @@ -1966,7 +2003,7 @@ "wlshowlast": "Prikaži posljednjih $1 sati $2 dana", "watchlist-hide": "Sakrij", "watchlist-submit": "Prikaži", - "wlshowtime": "Prikaži posljednjih:", + "wlshowtime": "Vremensko razdoblje za prikaz:", "wlshowhideminor": "manje promjene", "wlshowhidebots": "botove", "wlshowhideliu": "prijavljene suradnike", @@ -1990,7 +2027,7 @@ "enotif_body_intro_moved": "Stranicu $1 projekta {{SITENAME}} {{GENDER:$2|premjestio|premjestila}} je dana $PAGEEDITDATE {{GENDER:$2|suradnik|suradnica}} $2, vidi $3 za trenutačnu inačicu stranice.", "enotif_body_intro_restored": "Stranica $1 projekta {{SITENAME}} {{GENDER:$2|vratio|vratila}} je dana $PAGEEDITDATE {{GENDER:$2|suradnik|suradnica}} $2, vidi $3 za trenutačnu inačicu stranice.", "enotif_body_intro_changed": "Stranica $1 projekta {{SITENAME}} {{GENDER:$2|promijenio|promijenila}} je dana $PAGEEDITDATE {{GENDER:$2|suradnik|suradnica}} $2, vidi $3 za trenutačnu inačicu stranice.", - "enotif_lastvisited": "Pogledaj $1 za promjene od zadnjeg posjeta.", + "enotif_lastvisited": "Za sve izmjene od VaÅ¡ega posljednjeg posjeta, pogledajte $1", "enotif_lastdiff": "Pogledajte $1 kako biste mogli vidjeti tu izmjenu.", "enotif_anon_editor": "neprijavljeni suradnik $1", "enotif_body": "PoÅ¡tovani $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nSažetak uređivača: $PAGESUMMARY $PAGEMINOREDIT\n\nMožete kontaktirati suradnika koji je posljednji uređivao stranicu:\nmail: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nDo VaÅ¡ega ponovnog posjeta stranici ne ćete dobivati nove obavijesti. Postavke za izvješćivanje možete vratiti na prvobitno zadane za sve praćene stranice VaÅ¡ega popisa praćenja.\n\nVaÅ¡ sustav izvješćivanja {{SITENAME}}.\n\n--\nZa promjene postavki izvješćivanja putem e-poÅ¡te, posjetite\n{{canonicalurl:{{#special:Preferences}}}}\n\nZa promjene svog popisa praćenja, posjetite\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nZa brisanje stranica iz svog popisa praćenja, posjetite\n$UNWATCHURL\n\nZa povratne informacije i pomoć posjetite:\n$HELPPAGE", @@ -2009,7 +2046,7 @@ "confirmdeletetext": "Zauvijek ćete izbrisati stranicu ili sliku zajedno s prijaÅ¡njim inačicama.\nMolim potvrdite svoju namjeru, da razumijete posljedice i da ovo radite u skladu s [[{{MediaWiki:Policy-url}}|pravilima]].", "actioncomplete": "Radnja je dovrÅ¡ena", "actionfailed": "Radnja nije uspjela", - "deletedtext": "\"$1\" je izbrisana.\nVidi $2 za evidenciju nedavnih brisanja.", + "deletedtext": "Stranica »$1« je izbrisana.\nVidi pod $2 za zapise nedavnih brisanja.", "dellogpage": "Evidencija brisanja", "dellogpagetext": "Dolje je popis nedavnih brisanja.\nSva vremena su prema poslužiteljevom vremenu.", "deletionlog": "evidencija brisanja", @@ -2027,13 +2064,15 @@ "rollbacklink": "ukloni", "rollbacklinkcount": "ukloni $1 {{PLURAL:$1|uređivanje|uređivanja}}", "rollbacklinkcount-morethan": "ukloni viÅ¡e od $1 {{PLURAL:$1|uređivanje|uređivanja}}", - "rollbackfailed": "Uklanjanje neuspjeÅ¡no", + "rollbackfailed": "Brzo uklanjanje nije uspjelo", + "rollback-missingrevision": "Ne mogu učitati podatke o izmjeni.", "cantrollback": "Ne mogu ukloniti posljednju promjenu, postoji samo jedna promjena.", "alreadyrolled": "Ne mogu ukloniti posljednju promjenu članka [[:$1]] koju je napravio [[User:$2|$2]] ([[User talk:$2|razgovor]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); netko je već promijenio stranicu ili uklonio promjenu.\n\nPosljednju promjenu napravio je [[User:$3|$3]] ([[User talk:$3|razgovor]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).", "editcomment": "Sažetak promjene je bio: <em>$1</em>.", "revertpage": "uklonjena promjena {{GENDER:$2|suradnika|suradnice}} [[Special:Contributions/$2|$2]] ([[User talk:$2|razgovor]]), vraćeno na posljednju inačicu {{GENDER:$1|suradnika|suradnice}} [[User:$1|$1]]", "revertpage-nouser": "Vraćene izmjene suradnika (suradničko ime uklonjeno) na posljednju inačicu suradnika [[User:$1|$1]]", - "rollback-success": "uklonjeno uređivanje {{GENDER:$1|suradnika|suradnice}} $1\nvraćeno na posljednju inačicu {{GENDER:$2|suradnika|suradnice}} $2.", + "rollback-success": "Uklonjeno uređivanje {{GENDER:$3|suradnika|suradnice}} $1; vraćeno na posljednju inačicu {{GENDER:$4|suradnika|suradnice}} $2.", + "rollback-success-notify": "Uklonili ste izmjene suradnika $1;\nvraćeno na posljednju izmjenu suradnika $2. [$3 Prikaži izmjene]", "sessionfailure-title": "Prekid sesije", "sessionfailure": "Izgleda da postoji problem s uspostavom sjednice kod VaÅ¡ega prijavljivanja; ta radnja otkazana je kao način sprječavanja krađe sjednice. Molimo Vas da se u pregledniku vratite natrag na prethodnu stranicu, ponovo ju učitate i zatim pokuÅ¡ate opet.", "changecontentmodel": "Promjena modela sadržaja stranice", @@ -2043,7 +2082,15 @@ "changecontentmodel-reason-label": "Razlog:", "changecontentmodel-submit": "Promijeni", "changecontentmodel-success-title": "Sadržaj modela je promijenjen", + "changecontentmodel-success-text": "Model sadržaja stranice [[:$1]] je promijenjen.", + "changecontentmodel-cannot-convert": "Model sadržaja stranice [[:$1]] se ne može pretvoriti u vrstu $2.", + "changecontentmodel-nodirectediting": "Model sadržaja $1 ne podržava izravno uređivanje", + "changecontentmodel-emptymodels-title": "Nema dostupnih modela sadržaja", + "changecontentmodel-emptymodels-text": "Model sadržaja stranice [[:$1]] ne može se pretvoriti ni u jednu drugu vrstu.", "log-name-contentmodel": "Evidencija promjena modela sadržaja", + "log-description-contentmodel": "Ova stranica navodi izmjene modela sadržaja stranica, kao i stranice napravljene s modelom sadržaja koji se razlikuje od predodređenoga.", + "logentry-contentmodel-new": "$1 {{GENDER:$2|načinio|načinila}} je stranicu $3 s nestandardnim modelom sadržaja »$5«", + "logentry-contentmodel-change": "$1 {{GENDER:$2|promijenio|promijenila}} je model sadržaja stranice $3 iz »$4« u »$5«", "logentry-contentmodel-change-revertlink": "vrati", "logentry-contentmodel-change-revert": "vraćanje modela", "protectlogpage": "Evidencija zaÅ¡tićivanja", @@ -2060,6 +2107,7 @@ "prot_1movedto2": "$1 premjeÅ¡teno na $2", "protect-badnamespace-title": "NezaÅ¡titljiv imenski prostor", "protect-badnamespace-text": "Stranice u ovom imenskom prostoru ne mogu se zaÅ¡tititi.", + "protect-norestrictiontypes-text": "Ova se stranica ne može zaÅ¡tititi jer nema dostupnih oblika ograničenja.", "protect-norestrictiontypes-title": "Stranicu nije moguće zaÅ¡tititi", "protect-legend": "Potvrda zaÅ¡tite", "protectcomment": "Razlog:", @@ -2074,7 +2122,7 @@ "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": "Onemogućeno novim i neprijavljenim suradnicima", + "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)", @@ -2128,6 +2176,7 @@ "undelete-search-title": "Pretraži obrisane stranice", "undelete-search-box": "Pretraži obrisane stranice", "undelete-search-prefix": "Pretraži stranice koje počinju s:", + "undelete-search-full": "Prikaži naslove koji sadržavaju:", "undelete-search-submit": "Pretraži", "undelete-no-results": "Nije pronađena odgovarajuća stranica u arhivu brisanja.", "undelete-filename-mismatch": "Ne mogu vratiti inačicu datoteke s vremenom i datumom $1: imena se ne slažu (''filename mismatch'')", @@ -2159,7 +2208,8 @@ "sp-contributions-newbies-sub": "Za nove suradnike", "sp-contributions-newbies-title": "Doprinosi novih suradnika", "sp-contributions-blocklog": "evidencija blokiranja", - "sp-contributions-deleted": "obrisani suradnički doprinosi", + "sp-contributions-suppresslog": "pobrisani {{GENDER:$1|suradnikovi|suradničini}} doprinosi", + "sp-contributions-deleted": "pobrisani {{GENDER:$1|suradnikovi|suradničini}} doprinosi", "sp-contributions-uploads": "postavljene datoteke", "sp-contributions-logs": "evidencije", "sp-contributions-talk": "razgovor", @@ -2334,7 +2384,7 @@ "articleexists": "Stranica pod tim imenom već postoji ili ime koje ste odabrali nije u skladu s pravilima.\nMolimo odaberite drugo ime.", "cantmove-titleprotected": "Ne možete premjestiti ovu stranicu na ovo mjesto, jer je novi naslov zaÅ¡tićen od kreiranja", "movetalk": "Premjesti i njezinu stranicu za razgovor ako je moguće.", - "move-subpages": "Premjesti podstranice (na $1)", + "move-subpages": "Premjesti podstranice (najviÅ¡e do $1)", "move-talk-subpages": "Premjesti podstranice od stranice za razgovor (na $1)", "movepage-page-exists": "Stranica $1 već postoji i ne može biti automatski prepisana", "movepage-page-moved": "Stranica $1 je premjeÅ¡tena na $2.", @@ -3092,6 +3142,8 @@ "autosumm-blank": "uklonjen cjelokupni sadržaj stranice", "autosumm-replace": "Zamijenjen sadržaj stranice s »$1«", "autoredircomment": "Preusmjeravanje stranice na [[$1]]", + "autosumm-removed-redirect": "Uklonjeno preusmjeravanje na [[$1]]", + "autosumm-changed-redirect-target": "Promijenjeno je odrediÅ¡te preusmjeravanja sa stranice [[$1]] na [[$2]]", "autosumm-new": "Stvorena nova stranica sa sadržajem: »$1«.", "autosumm-newblank": "Stvorena prazna stranica.", "size-bytes": "$1 {{PLURAL:$1|bajt|bajta|bajtova}}", @@ -3110,7 +3162,7 @@ "watchlistedit-raw-done": "VaÅ¡ popis praćenja je snimljen.", "watchlistedit-raw-added": "{{PLURAL:$1|1 stranica je dodana|$1 stranice su dodane}}:", "watchlistedit-raw-removed": "{{PLURAL:$1|1 stranica je uklonjena|$1 stranice su ukonjene}}:", - "watchlistedit-clear-title": "Očišćen popis praćenja", + "watchlistedit-clear-title": "Očisti popis praćenja", "watchlistedit-clear-legend": "ObriÅ¡i popis praćenja", "watchlistedit-clear-explain": "Sve stavke s popisa praćenja će biti izbrisane", "watchlistedit-clear-titles": "Imena stranica:", @@ -3264,6 +3316,18 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Oznaka|Oznake|Oznaka}}]]: $2)", "tag-mw-contentmodelchange": "promjena modela sadržaja", "tag-mw-contentmodelchange-description": "Uređivanja koja [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel mijenjanju model sadržaja] stranice", + "tag-mw-new-redirect": "novo preusmjeravanje", + "tag-mw-new-redirect-description": "Uređivanja kojima se stvara novo preusmjeravanje ili mijenja stranica na koju se preusmjerava", + "tag-mw-removed-redirect": "uklonjeno preusmjeravanje", + "tag-mw-removed-redirect-description": "Uređivanja kojima se mijenja postojeće preusmjeravanje u nepreusmjeravanje", + "tag-mw-changed-redirect-target": "promijenjeno preusmjeravanje", + "tag-mw-changed-redirect-target-description": "Uređivanja koja mijenjaju odrediÅ¡te preusmjeravanja", + "tag-mw-blank": "bjelidba", + "tag-mw-blank-description": "Uređivanje kojim je načinjena bjelidba stranice", + "tag-mw-replace": "preko 90 % zamijenjen tekst", + "tag-mw-replace-description": "Uređivanja kojima se uklanja viÅ¡e nego 90 % sadržaja stranice", + "tag-mw-rollback": "brzo uklanjanje", + "tag-mw-rollback-description": "Uređivanja kojima se brzo uklanjaju prethodne izmjene rabeći poveznicu za brzo uklanjanje", "tags-title": "Oznake", "tags-intro": "Ova stranica sadržava popis oznaka s kojima programska oprema može označivati promjene te njihova značenja.", "tags-tag": "Naziv oznake", @@ -3398,6 +3462,8 @@ "logentry-block-reblock": "$1 {{GENDER:$2|promijenio|promijenila}} je postavke blokiranja {{GENDER:$4|suradnika|suradnice}} {{GENDER:$4|$3}} s krajnjim rokom koji ističe $5 $6", "logentry-suppress-block": "$1 {{GENDER:$2|blokirao|blokirala}} je {{GENDER:$4|$3}} s krajnjim rokom koji ističe $5 $6", "logentry-suppress-reblock": "$1 {{GENDER:$2|promijenio|promijenila}} je postavke blokiranja {{GENDER:$4|suradnika|suradnice}} {{GENDER:$4|$3}} s krajnjim rokom koji ističe $5 $6", + "logentry-import-interwiki": "$1 {{GENDER:$2|uvezao|uvezla}} je $3 s drugog wikija", + "logentry-import-interwiki-details": "$1 {{GENDER:$2|uvezao|uvezla}} je $3 sa $5 ($4 {{PLURAL:$4|izmjena|izmjene|izmjena}})", "logentry-merge-merge": "$1 je {{GENDER:$2|spojio|spojila}} $3 s $4 (izmjene do $5)", "logentry-move-move": "$1 je {{GENDER:$2|premjestio|premjestila}} stranicu $3 na $4", "logentry-move-move-noredirect": "$1 je {{GENDER:$2|premjestio|premjestila}} stranicu $3 na $4 bez preusmjeravanja", @@ -3465,6 +3531,7 @@ "duration-centuries": "$1 {{PLURAL:$1|stoljeće|stoljeća}}", "duration-millennia": "$1 {{PLURAL:$1|milenij|milenija}}", "rotate-comment": "Sliku je $1 zaokrenuo za {{PLURAL:$1|stupanj|stupnja|stupnjeva}} u smjeru kazaljke na satu.", + "limitreport-title": "Podatci profiliranja raščlanjivača:", "limitreport-cputime-value": "$1 {{PLURAL:$1|sekunda|sekunde|sekundi}}", "limitreport-walltime": "Uporaba u realnom vremenu", "limitreport-walltime-value": "$1 {{PLURAL:$1|sekunda|sekunde|sekundi}}", @@ -3487,6 +3554,7 @@ "pagelang-submit": "PoÅ¡alji", "right-pagelang": "Promijeni jezik stranice", "log-name-pagelang": "Evidencija mijenjanja jezika", + "logentry-pagelang-pagelang": "$1 {{GENDER:$2|promijenio|promijenila}} je jezik stranice $3 iz $4 u $5.", "mediastatistics": "Statistika datoteka", "mediastatistics-summary": "Slijede statistike postavljenih datoteka koje pokazuju zadnju inačicu datoteke. Starije ili izbrisane inačice nisu prikazane.", "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajta|$1 bajtova}} ($2; $3 %)", @@ -3595,12 +3663,19 @@ "authmanager-realname-help": "Pravo ime suradnika", "authmanager-provider-password": "Autorizacija zaporkom", "authmanager-provider-temporarypassword": "Privremena zaporka", + "cannotauth-not-allowed-title": "Pristup odbijen", + "cannotauth-not-allowed": "Nije Vam dopuÅ¡teno rabiti ovu stranicu", "changecredentials": "Promjena vjerodajnica", "changecredentials-submit": "Promijeni vjerodajnice", + "changecredentials-invalidsubpage": "$1 nije valjana vrsta vjerodajnice.", + "changecredentials-success": "VaÅ¡e su vjerodajnice promijenjene.", "removecredentials": "Uklanjanje vjerodajnica", "removecredentials-submit": "Ukloni vjerodajnice", "credentialsform-provider": "Vrsta vjerodajnica:", "credentialsform-account": "Suradnički račun:", + "gotointerwiki": "NapuÅ¡tate projekt {{SITENAME}}", + "gotointerwiki-invalid": "Navedeni naslov nije valjan.", + "gotointerwiki-external": "NapuÅ¡tate projekt {{SITENAME}} da biste posjetili zasebno mrežno mjesto [[$2]].\n\n<strong>[$1 Nastavljate na $1]</strong>", "pagedata-title": "Podatci o stranici", "pagedata-bad-title": "Naslov nije valjan: $1." } diff --git a/languages/i18n/hu.json b/languages/i18n/hu.json index abf5268cdd..d37bff11a4 100644 --- a/languages/i18n/hu.json +++ b/languages/i18n/hu.json @@ -1048,6 +1048,7 @@ "timezoneregion-indian": "Indiai-óceán", "timezoneregion-pacific": "Csendes-óceán", "allowemail": "E-mail engedélyezése más szerkesztőktől", + "email-allow-new-users-label": "E-mail engedélyezése frissen regisztrált szerkesztőktől", "email-blacklist-label": "Letiltás ezen felhasználóknak, hogy e-mailt küldhessenek nekem", "prefs-searchoptions": "Keresés", "prefs-namespaces": "Névterek", @@ -1217,6 +1218,7 @@ "right-siteadmin": "adatbázis lezárása, felnyitása", "right-override-export-depth": "Lapok exportálása a hivatkozott lapokkal együtt, legfeljebb 5-ös mélységig", "right-sendemail": "e-mail küldése más felhasználóknak", + "right-sendemail-new-users": "e-mail küldése olyan felhasználóknak, akiknek nincs naplózott művelete", "right-managechangetags": "[[Special:Tags|címkék]] létrehozása és (de)aktiválása", "right-applychangetags": "[[Special:Tags|címkék]] alkalmazása saját változatokra", "right-changetags": "egyedi lapváltozatokon és naplóbejegyzéseken tetszőleges [[Special:Tags|címkék]] hozzáadása és törlése", @@ -1317,6 +1319,8 @@ "recentchanges-summary": "Ezen a lapon a wikiben történt legutóbbi fejleményeket lehet nyomon követni.", "recentchanges-noresult": "A megadott időszakban nincs a feltételeknek megfelelő szerkesztés.", "recentchanges-timeout": "A keresés időtúllépés miatt leállt. Próbálkozz más keresési paraméterekkel.", + "recentchanges-network": "Technikai hiba miatt nem sikerült betölteni semmilyen eredményt. Próbáld frissíteni az oldalt.", + "recentchanges-notargetpage": "Írj be egy lapcímet fentebb a kapcsolódó változások megtekintéséhez.", "recentchanges-feed-description": "Kövesd a wiki friss változtatásait ezzel a hírcsatornával.", "recentchanges-label-newpage": "Ezzel a szerkesztéssel egy új lap jött létre", "recentchanges-label-minor": "Ez egy apró szerkesztés", @@ -1332,7 +1336,9 @@ "rcfilters-group-results-by-page": "Csoportosítás eredményei lapok szerint", "rcfilters-activefilters": "Aktív szűrők", "rcfilters-advancedfilters": "Haladó szűrők", - "rcfilters-limit-title": "Megjelenítendő változtatások", + "rcfilters-limit-title": "Megjelenítendő találatok száma", + "rcfilters-limit-and-date-label": "$1 változtatás, $2", + "rcfilters-date-popup-title": "A keresés időkerete", "rcfilters-days-title": "Legutóbbi napok", "rcfilters-hours-title": "Legutóbbi órák", "rcfilters-days-show-days": "$1 nap", @@ -1352,7 +1358,7 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "Alapértelmezett szűrő készítése", "rcfilters-savedqueries-cancel-label": "Mégse", "rcfilters-savedqueries-add-new-title": "Szűrők mentése gyors hivatkozásként", - "rcfilters-savedqueries-already-saved": "Ezek a szűrők már el lettek mentve", + "rcfilters-savedqueries-already-saved": "Ezek a szűrők már el lettek mentve. Módosítsd a beállításokat egy új mentett szűrő készítéséhez.", "rcfilters-restore-default-filters": "Alapértelmezett szűrők visszaállítása", "rcfilters-clear-all-filters": "Összes szűrő kikapcsolása", "rcfilters-show-new-changes": "Legfrissebb változtatások megtekintése", @@ -1446,6 +1452,9 @@ "rcfilters-watchlist-showupdated": "Az újabb változtatások amiket még nem néztél meg, <strong>vastaggal jelöltük</strong>", "rcfilters-preference-label": "A friss változtatások fejlesztett változatának elrejtése", "rcfilters-preference-help": "A 2017-es felületátdolgozás és minden azóta hozzáadott eszköz visszaállítása.", + "rcfilters-filter-showlinkedfrom-label": "A következő lapra hivatkozó lapok változtatásainak megjelenítése", + "rcfilters-filter-showlinkedto-label": "A következő lapról hivatkozott lapok változtatásainak megjelenítése", + "rcfilters-target-page-placeholder": "Adj meg egy oldal nevét", "rcnotefrom": "Alább a <strong>$3 $4</strong> óta történt változtatások láthatóak (legfeljebb <b>$1</b> db).", "rclistfromreset": "Dátumválasztás visszaállítása", "rclistfrom": "$3, $2 után történt változtatások megtekintése", @@ -1574,7 +1583,7 @@ "uploaded-script-svg": "A feltöltött SVG fájlodban szkriptelemet találtunk: \"$1\".", "uploaded-hostile-svg": "Nem biztonságos CSS kódot találtunk a feltöltött SVG fájlod stíluselemei között.", "uploaded-event-handler-on-svg": "Az alábbi eseménykezelő-attribútum beállítása nem megengedett az SVG fájlokban: <code>$1=$2</code>.", - "uploaded-href-attribute-svg": "href attribútumok SVG fájlokban csak http:// vagy https:// protokollal engedélyezettek, <code><$1 $2=\"$3\"></code> található.", + "uploaded-href-attribute-svg": "<a> elemek hivatkozásai (href) csak data: (beágyazott fájl), http://, https:// protokollú vagy dokumentumon belüli (#, töredék) célpontra mutathatnak. Más elemek (például <image>) csak data: és dokumentumon belüli célpontra mutathatnak. Próbáld beágyazni a képeket az SVG exportálásakor. <code><$1 $2=\"$3\"></code> található.", "uploaded-href-unsafe-target-svg": "Nem biztonságos adatra mutató href-et találtunk a feltöltött SVG-fájlban: URI-cél <code><$1 $2=\"$3\"></code>.", "uploaded-animate-svg": "A feltöltött SVG fájlban \"animate\" taget találtam, ami az alábbi \"from\" attribútumával megváltoztathat egy href-et: <code><$1 $2=\"$3\"></code>.", "uploaded-setting-event-handler-svg": "Eseménykezelő attribútumok beállítása blokkolva van, <code><$1 $2=\"$3\"></code> található a feltöltendő SVG fájlban.", @@ -1686,6 +1695,21 @@ "uploadstash-bad-path": "Útvonal nem létezik", "uploadstash-bad-path-invalid": "Érvénytelen útvonal", "uploadstash-bad-path-unknown-type": "Ismeretlen típus: „$1”", + "uploadstash-bad-path-unrecognized-thumb-name": "Ismeretlen bélyegképnév.", + "uploadstash-bad-path-no-handler": "Nem található kezelő a(z) $1 MIME-típusú $2 fájlhoz.", + "uploadstash-bad-path-bad-format": "A(z) „$1” kulcs nem megfelelő formátumú.", + "uploadstash-file-not-found-no-thumb": "A bélyegkép lekérése sikertelen.", + "uploadstash-file-not-found-no-local-path": "Nincs helyi útvonal az átméretezett elemhez.", + "uploadstash-file-not-found-no-object": "A bélyegkép helyi fájlobjektumának létrehozása sikertelen.", + "uploadstash-file-not-found-no-remote-thumb": "A bélyegkép lekérése sikertelen: $1\nURL: $2", + "uploadstash-file-not-found-missing-content-type": "A Content-Type (tartalomtípus) fejléc hiányzik.", + "uploadstash-file-not-found-not-exists": "Az útvonal nem található, vagy nem egy egyszerű fájl.", + "uploadstash-file-too-large": "$1 bájtnál nagyobb fájlok kiszolgálása nem lehetséges.", + "uploadstash-not-logged-in": "Nincs bejelentkezett felhasználó. A fájloknak felhasználókhoz kell tartozniuk.", + "uploadstash-wrong-owner": "Ez a fájl ($1) nem a jelenlegi felhasználóhoz tartozik.", + "uploadstash-no-such-key": "Nincs ilyen kulcs ($1), nem lehet eltávolítani.", + "uploadstash-no-extension": "Nincs kiterjesztés.", + "uploadstash-zero-length": "A fájl nulla méretű.", "invalid-chunk-offset": "Érvénytelen darab eltolás", "img-auth-accessdenied": "Hozzáférés megtagadva", "img-auth-nopathinfo": "Hiányzó PATH_INFO.\nA szerver nincs beállítva, hogy továbbítsa ezt az információt.\nLehet, hogy CGI-alapú, és nem támogatja az img_auth-ot.\nLásd https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization!", @@ -1831,7 +1855,8 @@ "pageswithprop-legend": "Lap tulajdonsággal rendelkező lapok", "pageswithprop-text": "Ez a lap azokat a lapokat listázza, amelyek egy adott lap tulajdonsággal rendelkeznek.", "pageswithprop-prop": "Tulajdonságnév:", - "pageswithprop-reverse": "Rendezze fordított sorrendbe", + "pageswithprop-reverse": "Rendezés fordított sorrendben", + "pageswithprop-sortbyvalue": "Rendezés a tulajdonság értéke szerint", "pageswithprop-submit": "Ugrás", "pageswithprop-prophidden-long": "hosszú szöveg tulajdonság elrejtve ($1)", "pageswithprop-prophidden-binary": "bináris tulajdonság érték rejtett ($1)", @@ -2089,7 +2114,7 @@ "emailuser-title-target": "E-mail küldése ennek a felhasználónak: $1", "emailuser-title-notarget": "E-mail küldése a felhasználónak", "emailpagetext": "Ezzel az űrlappal tudsz ennek a {{GENDER:$1|felhasználónak}} e-mailt küldeni.\nFeladóként a [[Special:Preferences|beállításaidnál]] megadott e-mail címed fog szerepelni, így a címzett közvetlenül tud majd válaszolni neked.", - "defemailsubject": "{{SITENAME}} e-mail a következő felhasználótól: „$1”", + "defemailsubject": "{{SITENAME}}-e-mail a következő felhasználótól: „$1”", "usermaildisabled": "Email fogadás letiltva", "usermaildisabledtext": "Nem küldhetsz emailt más felhasználóknak ezen a wikin", "noemailtitle": "Nincs e-mail-cím", @@ -2486,6 +2511,7 @@ "ipb_blocked_as_range": "Hiba: a(z) $1 IP-cím nem blokkolható közvetlenül, és nem lehet feloldani. A(z) $2 tartomány részeként van blokkolva, amely feloldható.", "ip_range_invalid": "Érvénytelen IP-tartomány.", "ip_range_toolarge": "Nem engedélyezettek azok a tartományblokkok, melyek nagyobbak mint /$1.", + "ip_range_exceeded": "Az IP-cím-tartomány nagyobb a megengedettnél. Engedélyezett tartomány: /$1.", "proxyblocker": "Proxyblokkoló", "proxyblockreason": "Az IP-címeden ''nyílt proxy'' üzemel. Amennyiben nem használsz proxyt, vedd fel a kapcsolatot egy informatikussal vagy az internetszolgáltatóddal ezen súlyos biztonsági probléma ügyében.", "sorbsreason": "Az IP-címed nyitott proxyként szerepel e webhely által használt DNSBL listán.", @@ -2629,6 +2655,8 @@ "import-mapping-namespace": "Importálás ebbe a névtérbe:", "import-mapping-subpage": "Importálás a következő lap allapjaként:", "import-upload-filename": "Fájlnév:", + "import-upload-username-prefix": "Wikiközi előtag:", + "import-assign-known-users": "A szerkesztések helyi felhasználókhoz rendelése, ha létezik helyileg az adott nevű felhasználó", "import-comment": "Megjegyzés:", "importtext": "Exportáld a fájlt a forráswikiből az [[Special:Export|exportáló eszköz]] segítségével.\nMentsd el a számítógépedre, majd töltsd fel ide.", "importstart": "Lapok importálása...", @@ -2637,6 +2665,7 @@ "imported-log-entries": "Importálva $1 logbejegyzés.", "importfailed": "Az importálás nem sikerült: $1", "importunknownsource": "Ismeretlen import forrástípus", + "importnoprefix": "Nincs megadva nyelvközi előtag", "importcantopen": "Nem nyitható meg az importfájl", "importbadinterwiki": "Rossz wikiközi hivatkozás", "importsuccess": "Az importálás befejeződött!", @@ -3315,6 +3344,8 @@ "autosumm-blank": "Eltávolította a lap teljes tartalmát", "autosumm-replace": "A lap tartalmának cseréje erre: $1", "autoredircomment": "Átirányítás ide: [[$1]]", + "autosumm-removed-redirect": "Átirányítás megszüntetve. Eredeti cél: [[$1]]", + "autosumm-changed-redirect-target": "Az átirányítás célja módosítva: [[$1]]→[[$2]]", "autosumm-new": "Új oldal, tartalma: „$1”", "autosumm-newblank": "Üres oldal létrehozva", "lag-warn-normal": "{{PLURAL:$1|Az egy|A(z) $1}} másodpercnél frissebb szerkesztések nem biztos, hogy megjelennek ezen a listán.", @@ -3440,7 +3471,19 @@ "tag-mw-contentmodelchange": "tartalommodell-változtatás", "tag-mw-contentmodelchange-description": "Szerkesztések, amelyek [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel megváltoztatják egy lap tartalommodelljét].", "tag-mw-new-redirect": "Új átirányítás", + "tag-mw-new-redirect-description": "Szerkesztések, amelyek új átirányítást hoztak létre vagy egy oldalt azzá tettek", + "tag-mw-removed-redirect": "Megszüntetett átirányítás", + "tag-mw-removed-redirect-description": "Szerkesztések, amelyek egy átirányítást megszüntetve tartalmi lapot hoztak létre", + "tag-mw-changed-redirect-target": "Átirányítás célja módosult", + "tag-mw-changed-redirect-target-description": "Szerkesztések, amelyek megváltoztatták az átirányítás célját (ahova irányított a lap)", + "tag-mw-blank": "Oldal egésze eltávolítva", + "tag-mw-blank-description": "Szerkesztések, amelyek eltávolították egy oldal teljes tartalmát", + "tag-mw-replace": "Oldal nagy része eltávolítva", + "tag-mw-replace-description": "Szerkesztések, amelyet egy oldal tartalmának több mint 90%-át törölték", "tag-mw-rollback": "Visszaállítás", + "tag-mw-rollback-description": "Szerkesztések, amelyek visszaállítottak szerkesztéseket a „visszavonás” gombra kattintva", + "tag-mw-undo": "Visszavonás", + "tag-mw-undo-description": "Szerkesztések, amelyek visszaállítottak szerkesztéseket a „visszavonás” linkre kattintva", "tags-title": "Címkék", "tags-intro": "Ez a lap azokat a címkéket és jelentéseiket tartalmazza, amikkel a szoftver megjelölhet egy szerkesztést.", "tags-tag": "Címke neve", @@ -3538,9 +3581,12 @@ "compare-title-not-exists": "A megadott cím nem létezik.", "compare-revision-not-exists": "A megadott lapváltozat nem létezik.", "diff-form": "Eltérések", + "diff-form-oldid": "Régi lapváltozat-azonosító (opcionális)", + "diff-form-revid": "A változtatás változatazonosítója", "diff-form-submit": "Változtatások megjelenítése", "permanentlink": "Állandó hivatkozás", "permanentlink-revid": "Változat azonosító", + "permanentlink-submit": "Ugrás a változatra", "dberr-problems": "Sajnáljuk, de az oldallal technikai problémák vannak.", "dberr-again": "Várj néhány percet, majd frissítsd az oldalt.", "dberr-info": "(Nem sikerült kapcsolódni az adatbázishoz: $1)", diff --git a/languages/i18n/hy.json b/languages/i18n/hy.json index 5d1ad9466c..485fca44e7 100644 --- a/languages/i18n/hy.json +++ b/languages/i18n/hy.json @@ -1790,7 +1790,7 @@ "pageinfo-header-restrictions": "Ô·Õ»Õ« պաշտպանումներ", "pageinfo-header-properties": "Ô·Õ»Õ« հատկանիշեր", "pageinfo-display-title": "Վերնագիր", - "pageinfo-default-sort": "Ô¼Õ¼Õ¥Õ¬Õ¡ÕµÕ¶ տեսակավորման Õ¢Õ¡Õ¶Õ¡Õ¬Õ«", + "pageinfo-default-sort": "Ô¼Õ¼Õ¥Õ¬ÕµÕ¡ÕµÕ¶ տեսակավորման Õ¢Õ¡Õ¶Õ¡Õ¬Õ«", "pageinfo-length": "Ô¾Õ¡Õ¾Õ¡Õ¬ (բայթերով)", "pageinfo-article-id": "Ô·Õ»Õ« N", "pageinfo-language": "Բովանդակության լեզու", @@ -1808,7 +1808,7 @@ "pageinfo-lasttime": "Վերջին խմբագրման Õ¡Õ´Õ½Õ¡Õ©Õ«Õ¾", "pageinfo-edits": "Խմբագրումների քանակ", "pageinfo-authors": "Ô±Õ¼Õ¡Õ¶Õ±Õ«Õ¶ հեղինակների քանակ", - "pageinfo-recent-edits": "Վերջին խմբագրումներ քանակ (վերջին $1)", + "pageinfo-recent-edits": "Վերջին խմբագրումների քանակ (վերջին $1)", "pageinfo-recent-authors": "Վերջին Õ¡Õ¼Õ¡Õ¶Õ±Õ«Õ¶ հեղինակների քանակ", "pageinfo-magic-words": "Մոգական {{PLURAL:$1|Õ¢Õ¡Õ¼|բառեր}} ($1)", "pageinfo-hidden-categories": "Թաքնված {{PLURAL:$1|կաղապար|կաղապարներ}} ($1)", diff --git a/languages/i18n/ia.json b/languages/i18n/ia.json index 9f11a2350d..f71620b098 100644 --- a/languages/i18n/ia.json +++ b/languages/i18n/ia.json @@ -866,7 +866,7 @@ "mergehistory-go": "Revelar modificationes fusionabile", "mergehistory-submit": "Fusionar versiones", "mergehistory-empty": "Nulle versiones pote esser fusionate.", - "mergehistory-done": "$3 {{PLURAL:$3|version|versiones}} de $1 fusionate in [[:$2]] con successo.", + "mergehistory-done": "$3 {{PLURAL:$3|version|versiones}} de $1 ha essite fusionate in [[:$2]].", "mergehistory-fail": "Impossibile executar le fusion del historia. Per favor reverifica le parametros del pagina e del tempore.", "mergehistory-fail-bad-timestamp": "Data e hora non valide.", "mergehistory-fail-invalid-source": "Pagina de origine non valide.", @@ -1014,6 +1014,7 @@ "timezoneregion-indian": "Oceano Indian", "timezoneregion-pacific": "Oceano Pacific", "allowemail": "Permitter que altere usatores me invia e-mail", + "email-allow-new-users-label": "Permitte e-mail de usatores toto nove", "email-blacklist-label": "Prohibir a iste usatores de inviar me e-mail:", "prefs-searchoptions": "Recerca", "prefs-namespaces": "Spatios de nomines", @@ -1183,6 +1184,7 @@ "right-siteadmin": "Blocar e disblocar le base de datos", "right-override-export-depth": "Exportar paginas includente paginas ligate usque a un profunditate de 5", "right-sendemail": "Inviar e-mail a altere usatores", + "right-sendemail-new-users": "Inviar e-mail a usatores sin actiones in registro", "right-managechangetags": "Crear e (de)activar [[Special:Tags|etiquettas]]", "right-applychangetags": "Applicar [[Special:Tags|etiquettas]] al proprie modificationes", "right-changetags": "Adder e remover qualcunque [[Special:Tags|etiquettas]] sur individual versiones e entratas de registro", @@ -1284,6 +1286,7 @@ "recentchanges-noresult": "Nulle modification facite in le periodo specificate que corresponde a iste criterios.", "recentchanges-timeout": "Iste recerca ha expirate. Considera essayar con altere parametros de recerca.", "recentchanges-network": "A causa de un error technic, nulle resultatos poteva esser cargate. Per favor, tenta de recargar le pagina.", + "recentchanges-notargetpage": "Entra le nomine de un pagina pro vider le modificationes associate a illo.", "recentchanges-feed-description": "Seque le modificationes le plus recente al wiki in iste syndication.", "recentchanges-label-newpage": "Iste modification creava un nove pagina", "recentchanges-label-minor": "Isto es un modification minor", @@ -1416,11 +1419,11 @@ "rcfilters-watchlist-showupdated": "Le modificationes del paginas que tu non ha visitate post que illos ha essite apportate se monstra in <strong>grasse</strong>, con marcatores solide.", "rcfilters-preference-label": "Celar le version meliorate del Modificationes recente", "rcfilters-preference-help": "Disface le nove interfacie de 2017 e tote le instrumentos addite alora e posta.", - "rcfilters-filter-showlinkedfrom-label": "Monstrar modificationes sur paginas ligate ab:", - "rcfilters-filter-showlinkedfrom-option-label": "Monstrar modificationes sur paginas ligate AB un pagina", - "rcfilters-filter-showlinkedto-label": "Monstrar modificationes sur paginas que liga a:", - "rcfilters-filter-showlinkedto-option-label": "Monstrar modificationes sur paginas con ligamines A un pagina", - "rcfilters-target-page-placeholder": "Selige un pagina", + "rcfilters-filter-showlinkedfrom-label": "Monstrar modificationes sur paginas ligate ab", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Paginas ligate ab</strong> le pagina seligite", + "rcfilters-filter-showlinkedto-label": "Monstrar modificationes sur paginas que liga a", + "rcfilters-filter-showlinkedto-option-label": "<strong>Paginas que liga verso</strong> le pagina seligite", + "rcfilters-target-page-placeholder": "Entra un nomine de pagina", "rcnotefrom": "Ecce le {{PLURAL:$5|modification|modificationes}} a partir del <strong>$3 a $4</strong> (usque a <strong>$1</strong> entratas monstrate).", "rclistfromreset": "Reinitialisar selection de data", "rclistfrom": "Monstrar nove modificationes a partir del $3 a $2", @@ -1465,7 +1468,7 @@ "recentchangeslinked-feed": "Modificationes ligate", "recentchangeslinked-toolbox": "Modificationes ligate", "recentchangeslinked-title": "Modificationes associate a \"$1\"", - "recentchangeslinked-summary": "Isto es un lista de modificationes facite recentemente in paginas ligate ab un pagina specific (o in membros de un categoria specific).\nLe paginas presente in [[Special:Watchlist|tu observatorio]] appare in litteras '''grasse'''.", + "recentchangeslinked-summary": "Entra le nomine de un pagina pro vider le modificationes facite in paginas ligate ab o verso ille pagina. (Pro vider le membros de un categoria, entra Categoria:Nomine del categoria.)\nLe paginas presente in [[Special:Watchlist|tu observatorio]] appare in litteras <strong>grasse</strong>.", "recentchangeslinked-page": "Nomine del pagina:", "recentchangeslinked-to": "Monstrar modificationes in paginas con ligamines al pagina specificate", "recentchanges-page-added-to-category": "[[:$1]] addite al categoria", @@ -2484,7 +2487,7 @@ "proxyblockreason": "Tu adresse IP ha essite blocate proque illo es un proxy aperte.\nPer favor contacta tu providitor de servicio internet o supporto technic e informa les de iste problema grave de securitate.", "sorbsreason": "Tu adresse IP es listate como proxy aperte in le DNSBL usate per {{SITENAME}}.", "sorbs_create_account_reason": "Tu adresse IP es listate como proxy aperte in le DNSBL usate per {{SITENAME}}.\nTu non pote crear un conto", - "softblockrangesreason": "Contributiones anonime non son permittite de vostre adresse de IP ($1). Si il vos place, identifica vos.", + "softblockrangesreason": "Contributiones anonyme non es permittite de tu adresse IP ($1). Per favor, aperi session.", "xffblockreason": "Un adresse IP presente in le capite X-Forwarded-For, o le tue o illo de un servitor proxy que tu usa, ha essite blocate. Le motivo original pro le blocada es: $1", "cant-see-hidden-user": "Le usator que tu tenta blocar ha ja essite blocate e celate. Post que tu non ha le derecto hideuser, tu non pote vider o modificar le blocada del usator.", "ipbblocked": "Tu non pote blocar o disblocar altere usatores, proque tu mesme es blocate", @@ -2860,7 +2863,7 @@ "file-no-thumb-animation": "'''Note: A causa de limitationes technic, le miniaturas de iste file non essera animate.'''", "file-no-thumb-animation-gif": "'''Nota: A causa de limitationes technic, le miniaturas de imagines GIF in alte resolution como iste non essera animate.'''", "newimages": "Galeria de nove files", - "imagelisttext": "Infra es un lista de '''$1''' {{PLURAL:$1|imagine|imagines}} ordinate $2.", + "imagelisttext": "Infra es un lista de <strong>$1</strong> {{PLURAL:$1|file|files}} ordinate $2.", "newimages-summary": "Iste pagina special detalia le recente files incargate.", "newimages-legend": "Filtro", "newimages-label": "Nomine del file (o un parte de illo):", @@ -3442,6 +3445,8 @@ "tag-mw-replace-description": "Modificationes que elimina plus de 90% del contento de un pagina", "tag-mw-rollback": "Revocation", "tag-mw-rollback-description": "Modificationes que disface previe modificationes usante le ligamine \"revocar\"", + "tag-mw-undo": "Disfacer", + "tag-mw-undo-description": "Modificationes que disface previe modificationes usante le ligamine \"disfacer\"", "tags-title": "Etiquettas", "tags-intro": "Iste pagina lista le etiquettas con le quales le software pote marcar un modification, e lor significato.", "tags-tag": "Nomine del etiquetta", @@ -3731,7 +3736,7 @@ "pagelang-language": "Lingua", "pagelang-use-default": "Usar lingua predefinite", "pagelang-select-lang": "Selige lingua", - "pagelang-reason": "Ration", + "pagelang-reason": "Motivo", "pagelang-submit": "Submitter", "pagelang-nonexistent-page": "Le pagina $1 non existe.", "pagelang-unchanged-language": "Le pagina $1 es jam fixate sur le lingua $2.", @@ -3786,17 +3791,17 @@ "special-characters-group-cyrillic": "Cyrillic", "special-characters-group-arabic": "Arabe", "special-characters-group-arabicextended": "Arabe extendite", - "special-characters-group-persian": "Persiano", + "special-characters-group-persian": "Persa", "special-characters-group-hebrew": "Hebreo", "special-characters-group-bangla": "Bengali", "special-characters-group-tamil": "Tamil", "special-characters-group-telugu": "Telugu", "special-characters-group-sinhala": "Sinhala", "special-characters-group-gujarati": "Gujarati", - "special-characters-group-devanagari": "DevanāgarÄ«", + "special-characters-group-devanagari": "Devanagari", "special-characters-group-thai": "Thailandese", "special-characters-group-lao": "Laotiano", - "special-characters-group-khmer": "Cambodgiano", + "special-characters-group-khmer": "Khmer", "special-characters-group-canadianaboriginal": "Aborigine canadian", "special-characters-title-endash": "lineetta en", "special-characters-title-emdash": "lineetta em", diff --git a/languages/i18n/id.json b/languages/i18n/id.json index cb5cee0bd9..ee3352cb34 100644 --- a/languages/i18n/id.json +++ b/languages/i18n/id.json @@ -1467,6 +1467,7 @@ "rcfilters-watchlist-showupdated": "Perubahan di halaman-halaman yang belum Anda kunjungi sejak perubahan terjadi ditampilkan dalam <strong>huruf terbal</strong>, dan tanda titik tebal di daftar.", "rcfilters-preference-label": "Sembunyikan versi terkini dari Perubahan Terbaru", "rcfilters-preference-help": "Kembalikan perubahan antarmuka 2017 dan semua peralatan yang ditambahkan sejak saat itu.", + "rcfilters-target-page-placeholder": "Masukkan nama halaman", "rcnotefrom": "Di bawah ini adalah {{PLURAL:$5|perubahan}} sejak <strong>$3, $4</strong> (ditampilkan sampai <strong>$1</strong> perubahan).", "rclistfromreset": "Atur ulang pilihan tanggal", "rclistfrom": "Perlihatkan perubahan terbaru sejak $3 $2", diff --git a/languages/i18n/is.json b/languages/i18n/is.json index b34511c53a..f7ec58155d 100644 --- a/languages/i18n/is.json +++ b/languages/i18n/is.json @@ -1228,7 +1228,8 @@ "rcfilters-group-results-by-page": "Flokka niðurstöður eftir síðum", "rcfilters-activefilters": "Virkar síur", "rcfilters-advancedfilters": "Ítarlegar síur", - "rcfilters-limit-title": "Breytingar sem á að sýna", + "rcfilters-limit-title": "Breytingar sem á að birta", + "rcfilters-date-popup-title": "Tímarammi sem á að leita í", "rcfilters-days-title": "Síðustu daga", "rcfilters-hours-title": "Síðustu klukkutíma", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|daga}}", @@ -3444,6 +3445,7 @@ "htmlform-user-not-exists": "<strong>$1</strong> er ekki til.", "htmlform-user-not-valid": "<strong>$1</strong> er ekki gilt notandanafn.", "logentry-delete-delete": "$1 {{GENDER:$2|eyddi}} síðunni $3", + "logentry-delete-delete_redir": "$1 {{GENDER:$2|eyddi}} tilvísun $3 með því að yfirskrifa", "logentry-delete-restore": "$1 {{GENDER:$2|endurvakti}} síðu $3 ($4)", "logentry-delete-event": "$1 {{GENDER:$2|breytti}} sýnileika {{PLURAL:$5|færslu|$5 færslna}} á $3: $4", "logentry-delete-revision": "$1 {{GENDER:$2|breytti}} sýnileika {{PLURAL:$5|útgáfu|$5 útgáfna}} á $3: $4", diff --git a/languages/i18n/it.json b/languages/i18n/it.json index bf7729ab25..90f4e60157 100644 --- a/languages/i18n/it.json +++ b/languages/i18n/it.json @@ -1511,6 +1511,10 @@ "rcfilters-watchlist-showupdated": "Le modifiche alle pagine che non hai ancora visitato da quando le modifiche sono avvenute, vengono evidenziate in <strong>grassetto</strong> e con dei pallini pieni.", "rcfilters-preference-label": "Nascondi la versione migliorata delle ultime modifiche", "rcfilters-preference-help": "Ripristina la riprogettazione dell'interfaccia 2017 e tutti gli strumenti aggiunti allora e da allora.", + "rcfilters-filter-showlinkedfrom-label": "Mostra le modifiche alle pagine collegate da", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pagine con collegamenti da</strong> la pagina selezionata", + "rcfilters-filter-showlinkedto-label": "Mostra le modifiche alle pagine che colegano a", + "rcfilters-filter-showlinkedto-option-label": "<strong>Pagine con collegamenti a</strong> la pagina selezionata", "rcnotefrom": "Di seguito {{PLURAL:$5|è elencata la modifica apportata|sono elencate le modifiche apportate}} a partire da <strong>$3, $4</strong> (mostrate fino a <strong>$1</strong>).", "rclistfromreset": "Reimposta la selezione della data", "rclistfrom": "Mostra le nuove modifiche a partire daː $2, $3", @@ -3531,6 +3535,8 @@ "tag-mw-replace-description": "Modifiche che rimuovono oltre il 90% del contenuto di una pagina", "tag-mw-rollback": "Rollback", "tag-mw-rollback-description": "Modifiche che ripristinano le versioni precedenti utilizzando il collegamento di rollback", + "tag-mw-undo": "Annulla", + "tag-mw-undo-description": "Modifiche che annullano le modifiche precedenti utilizzando il collegamento \"Annulla\"", "tags-title": "Etichette", "tags-intro": "Questa pagina elenca le etichette che il software potrebbe associare a una modifica e il loro significato.", "tags-tag": "Nome dell'etichetta", diff --git a/languages/i18n/ja.json b/languages/i18n/ja.json index e8aa9748b5..a79d9e6dda 100644 --- a/languages/i18n/ja.json +++ b/languages/i18n/ja.json @@ -87,7 +87,9 @@ "Delim", "Hinaloe", "Phantomize", - "Suzukaze-c" + "Suzukaze-c", + "Kkairri", + "Yusuke1109" ] }, "tog-underline": "リンクの下線:", @@ -754,6 +756,7 @@ "yourtext": "編集中の文章", "storedversion": "保存された版", "editingold": "<strong>警告: このページの古い版を編集しています。</strong>\n保存すると、この版以降になされた変更がすべて失われます。", + "unicode-support-fail": "お使いのブラウザはUnicodeをサポートしていないようです。 ページを編集する必要があるため、編集内容は保存されませんでした。", "yourdiff": "差分", "copyrightwarning": "{{SITENAME}}への投稿はすべて、$2 (詳細は$1を参照)のもとで公開したと見なされることにご注意ください。\n自分が書いたものが他の人に容赦なく編集され、自由に配布されるのを望まない場合は、ここに投稿しないでください。<br />\nまた、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください。\n<strong>著作権保護されている作品は、許諾なしに投稿しないでください!</strong>", "copyrightwarning2": "{{SITENAME}}への投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。\n自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。<br />\nまた、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は$1を参照)。\n<strong>著作権保護されている作品は、許諾なしに投稿しないでください!</strong>", @@ -1095,6 +1098,8 @@ "timezoneregion-indian": "インド洋", "timezoneregion-pacific": "太平洋", "allowemail": "他の利用者からのメールを受け取る", + "email-allow-new-users-label": "新しいユーザーからのメールを許可する", + "email-blacklist-label": "次のユーザーからのメールを受け取らない:", "prefs-searchoptions": "検索", "prefs-namespaces": "名前空間", "default": "既定", @@ -1268,6 +1273,7 @@ "right-siteadmin": "データベースをロックおよびロック解除", "right-override-export-depth": "リンク先ページを5階層まで含めて書き出す", "right-sendemail": "他の利用者にメールを送信", + "right-sendemail-new-users": "ログに記録されていないユーザーにメールを送信する", "right-managechangetags": "[[Special:Tags|タグ]]の作成、有効化および無効化", "right-applychangetags": "自分の編集に[[Special:Tags|タグ]]を適用する", "right-changetags": "個々の版と記録項目の任意の[[Special:Tags|タグ]]の追加と削除", @@ -1368,6 +1374,9 @@ "recentchanges-legend": "最近の更新のオプション", "recentchanges-summary": "このページでは、このウィキでの最近の更新を確認できます。", "recentchanges-noresult": "指定した条件に該当する期間の変更はありません。", + "recentchanges-timeout": "この検索はタイムアウトしました。 さまざまな検索パラメータを試してみることもできます。", + "recentchanges-network": "技術的なエラーのため、結果をロードできませんでした。ページをリフレッシュしてみてください。", + "recentchanges-notargetpage": "上記のページ名を入力すると、そのページに関連する変更が表示されます。", "recentchanges-feed-description": "このフィードでこのウィキの最近の更新を追跡できます。", "recentchanges-label-newpage": "ページの新規作成", "recentchanges-label-minor": "細部の編集", @@ -1384,6 +1393,7 @@ "rcfilters-activefilters": "絞り込み", "rcfilters-advancedfilters": "詳細フィルター", "rcfilters-limit-title": "表示件数の変更", + "rcfilters-date-popup-title": "検索期間", "rcfilters-days-title": "日数", "rcfilters-hours-title": "時間", "rcfilters-days-show-days": "$1 {{PLURAL:$1|日}}", @@ -1403,6 +1413,7 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "既定フィルターを作成", "rcfilters-savedqueries-cancel-label": "キャンセル", "rcfilters-savedqueries-add-new-title": "現在のフィルター設定を保存する", + "rcfilters-savedqueries-already-saved": "これらのフィルタは既に保存されています。設定を変更して、新しい保存フィルタを作成します。", "rcfilters-restore-default-filters": "標準設定の絞り込み条件を適用", "rcfilters-clear-all-filters": "すべてのフィルターをクリア", "rcfilters-show-new-changes": "最新の変更を表示", @@ -1410,7 +1421,7 @@ "rcfilters-invalid-filter": "無効なフィルター", "rcfilters-empty-filter": "絞り込みは行われていません。全ての項目が表示さます。", "rcfilters-filterlist-title": "フィルター", - "rcfilters-filterlist-whatsthis": "これは何?", + "rcfilters-filterlist-whatsthis": "これらはどのように機能しますか?", "rcfilters-filterlist-feedbacklink": "(新しい)絞り込み機能に関するフィードバックをお願いします", "rcfilters-highlightbutton-title": "該当項目を強調表示する", "rcfilters-highlightmenu-title": "色を選ぶ", @@ -1457,6 +1468,9 @@ "rcfilters-filter-watchlist-watchednew-description": "ウォッチリストに登録されていて、前回訪れた後に更新があったページ。", "rcfilters-filter-watchlist-notwatched-label": "ウォッチリスト登録外", "rcfilters-filter-watchlist-notwatched-description": "ウォッチリストに登録されているページ以外の全ての変更。", + "rcfilters-filter-watchlistactivity-unseen-label": "保存していません!", + "rcfilters-filter-watchlistactivity-unseen-description": "ウォッチリストに登録されていて、前回訪れた後に更新があったページ。", + "rcfilters-filter-watchlistactivity-seen-label": "最近の更新", "rcfilters-filtergroup-changetype": "変更の種類", "rcfilters-filter-pageedits-label": "ページの編集", "rcfilters-filter-pageedits-description": "ウィキの本文、議論、カテゴリの説明などの編集", @@ -1488,7 +1502,11 @@ "rcfilters-liveupdates-button-title-off": "新しい編集を即座に読み表示する", "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-target-page-placeholder": "ページ名を入力", "rcnotefrom": "以下は<strong>$3 $4</strong>以降の{{PLURAL:$5|更新です}} (最大 <strong>$1</strong> 件)。", "rclistfromreset": "日時指定をリセット", "rclistfrom": "$3の$2以降の更新を表示する", diff --git a/languages/i18n/jv.json b/languages/i18n/jv.json index e32e56ebef..500e25ef78 100644 --- a/languages/i18n/jv.json +++ b/languages/i18n/jv.json @@ -3147,6 +3147,7 @@ "tag-filter-submit": "Penyaring", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tenger|Tenger}}]]: $2)", "tag-mw-contentmodelchange": "owahan modhèl isi", + "tag-mw-blank": "Ngosongaké", "tags-title": "Tag", "tags-intro": "Kaca iki isi pratélan tenger sing dienggo nandhani besutan déning piranti alus, sinartan tegesé.", "tags-tag": "Jeneng tag", diff --git a/languages/i18n/ka.json b/languages/i18n/ka.json index 15cc7101a0..5fb17ba934 100644 --- a/languages/i18n/ka.json +++ b/languages/i18n/ka.json @@ -1299,7 +1299,7 @@ "action-changetags": "თავისუფალი ტეგების დამატება და წაშლა ცალკეულ ცვლილებებსა და ჟურნალების ჩანაწერებში", "action-deletechangetags": "მონაცემთა ბაზიდან ტეგების წაშლა", "action-purge": "ამ გვერდის წაშლა", - "nchanges": "$1 ცვლილება", + "nchanges": "$1 {{PLURAL:$1|ცვლილება|ცვლილება}}", "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ბოლო ვიზიტის შემდეგ}}", "enhancedrc-history": "ისტორია", "recentchanges": "ბოლო ცვლილებები", @@ -1324,6 +1324,8 @@ "rcfilters-activefilters": "აქტიური ფილტრები", "rcfilters-advancedfilters": "გაფართოებული ფილტრები", "rcfilters-limit-title": "ცვლილელების ნახვა", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|ცვლილება|$1 ცვლილება}}, $2", + "rcfilters-date-popup-title": "საძიებო დროის მონაკვეთი", "rcfilters-days-title": "უკანასკნელი დღეები", "rcfilters-hours-title": "ბოლო საათები", "rcfilters-days-show-days": "$1 {{PLURAL:$1|დღე|დღე}}", @@ -3362,6 +3364,7 @@ "autosumm-blank": "გვერდის შიგთავსი დაცარიელდა", "autosumm-replace": "შინაარსი შეიცვალა „$1“-ით", "autoredircomment": "გადამისამართება [[$1]]-ზე", + "autosumm-removed-redirect": "წაშლილი გადამისამართება [[$1]]", "autosumm-new": "ახალი გვერდი: $1", "autosumm-newblank": "ცარიელი გვერდი შეიქმნა", "size-bytes": "$1 ბ", @@ -3526,7 +3529,7 @@ "tags-delete": "წაშლა", "tags-activate": "გააქტიურება", "tags-deactivate": "დეაქტივაცია", - "tags-hitcount": "$1 ცვლილება", + "tags-hitcount": "$1 {{PLURAL:$1|ცვლილება|ცვლილება}}", "tags-manage-no-permission": "თქვენ არ გაქვთ შეცვლილი დასათაურების მართვის უფლება", "tags-manage-blocked": "თქვენ ვერ შეძლებთ ცვლილებების ტეგების მართვას სანამ {{GENDER:$1|თქვენ}} დაბლოკილი ხართ.", "tags-create-heading": "ახალი ტეგის შექმნა", diff --git a/languages/i18n/kk-cyrl.json b/languages/i18n/kk-cyrl.json index 7c4b4f4f79..d83a5ea782 100644 --- a/languages/i18n/kk-cyrl.json +++ b/languages/i18n/kk-cyrl.json @@ -3203,7 +3203,7 @@ "logentry-newusers-byemail": "$1 $3 деген аккаунт {{GENDER:$2|тіркеді}} және құпия сөзі е-пошта арқылы жіберілді", "logentry-newusers-autocreate": "$1 қатысушы аккаунтын автоматты түрде {{GENDER:$2|тіркеді}}", "logentry-protect-move_prot": "$1 protection settings from $4 дегеннен $3 дегенге қорғалу баптауларын {{GENDER:$2|жылжытты}}", - "logentry-protect-unprotect": "$1 $3 бетінің қорғанысын {{GENDER:$2|алыпсады}}", + "logentry-protect-unprotect": "$1 $3 бетінің қорғанысын {{GENDER:$2|алып тастады}}", "logentry-protect-protect": "$1 $3 бетін {{GENDER:$2|қорғады}} $4", "logentry-protect-modify-cascade": "$1 $3 бетінің қорғалу деңгейін $4 мерзіміне {{GENDER:$2|өзгертті}} [баулы]", "logentry-rights-rights": "$1 $3 үшін топ мүшелігін $4 дегеннен $5 дегенге {{GENDER:$2|өзгертті}}", diff --git a/languages/i18n/ko.json b/languages/i18n/ko.json index d04922898f..1ceca05885 100644 --- a/languages/i18n/ko.json +++ b/languages/i18n/ko.json @@ -1044,7 +1044,7 @@ "recentchangesdays-max": "최대 $1{{PLURAL:$1|일}}", "recentchangescount": "기본으로 보여줄 편집 수:", "prefs-help-recentchangescount": "이 설정은 최근 바뀜, 문서 역사와 기록에 적용됩니다.", - "prefs-help-watchlist-token2": "내 주시문서 목록의 웹 피드의 비밀 키입니다.\n이 키를 알고 있는 사람은 내 주시문서 목록을 읽을 수 있으니 이 키를 공유하지 마세요.\n필요하다면 [[Special:ResetTokens|이 키를 재설정할 수 있습니다]].", + "prefs-help-watchlist-token2": "이것은 내 주시문서 목록의 웹 피드의 비밀 키입니다.\n이 키를 알고 있는 사람은 누구든지 내 주시문서 목록을 읽을 수 있으니 이 키를 공유하지 마세요.\n필요하다면 [[Special:ResetTokens|이 키를 재설정할 수 있습니다]].", "savedprefs": "설정을 저장했습니다.", "savedrights": "{{GENDER:$1|$1}}의 사용자 그룹이 저장되었습니다.", "timezonelegend": "시간대:", @@ -1064,6 +1064,7 @@ "timezoneregion-indian": "인도양", "timezoneregion-pacific": "태평양", "allowemail": "다른 사용자가 내게 이메일을 보낼 수 있게 허용", + "email-allow-new-users-label": "처음 온 사용자들로부터 오는 이메일 허용", "email-blacklist-label": "이 사용자들이 내게 이메일을 보내는 것을 금지합니다:", "prefs-searchoptions": "검색", "prefs-namespaces": "이름공간", @@ -1334,6 +1335,7 @@ "recentchanges-noresult": "지정한 ì¡°ê±´ê³¼ 일치하는 주어진 기간 동안 바뀜이 없습니다.", "recentchanges-timeout": "이 검색의 시간이 초과되었습니다. 다른 검색 변수를 사용할 수 있습니다.", "recentchanges-network": "기술적인 문제로 결과를 불러올 수 없습니다. 페이지를 다시 새로 고침해 주십시오.", + "recentchanges-notargetpage": "해당 문서에 관한 변경사항을 보려면 상단에 문서 제목을 입력하십시오.", "recentchanges-feed-description": "이 피드에 위키의 최근 바뀜을 추적합니다.", "recentchanges-label-newpage": "새 문서", "recentchanges-label-minor": "사소한 편집", @@ -1399,10 +1401,10 @@ "rcfilters-filter-user-experience-level-registered-description": "로그인된 편집자.", "rcfilters-filter-user-experience-level-unregistered-label": "등록 안 됨", "rcfilters-filter-user-experience-level-unregistered-description": "로그인하지 않은 편집자.", - "rcfilters-filter-user-experience-level-newcomer-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": "\"신규 사용자\" 보다 경험이 더 많지만 \"능숙한 사용자\" 보다는 적습니다.", + "rcfilters-filter-user-experience-level-learner-description": "\"신입 사용자\"보다 경험이 더 많지만 \"능숙한 사용자\"보다는 적습니다.", "rcfilters-filter-user-experience-level-experienced-label": "능숙한 사용자", "rcfilters-filter-user-experience-level-experienced-description": "30일 이상의 활동 및 500개 이상의 편집.", "rcfilters-filtergroup-automated": "자동으로 된 기여", @@ -1466,7 +1468,7 @@ "rcfilters-watchlist-showupdated": "변경이 발생한 이후로 방문한 적이 없는 문서의 변경사항은 <strong>굵게</strong> 표시됩니다.", "rcfilters-preference-label": "개선된 버전의 최근 바뀜 숨기기", "rcfilters-preference-help": "2017년 인터페이스 재설계 부분과 ê·¸ 당시와 ê·¸ 이후에 추가된 모든 도구를 되돌립니다.", - "rcfilters-target-page-placeholder": "문서 선택", + "rcfilters-target-page-placeholder": "문서 이름을 입력하세요", "rcnotefrom": "아래는 <strong>$3, $4</strong>부터 시작하는 {{PLURAL:$5|바뀜이 있습니다}}. (최대 <strong>$1</strong>개가 표시됨)", "rclistfromreset": "날짜 선택 초기화", "rclistfrom": "$3 $2부터 시작하는 새로 바뀐 문서 보기", @@ -1511,7 +1513,7 @@ "recentchangeslinked-feed": "가리키는 글의 최근 바뀜", "recentchangeslinked-toolbox": "가리키는 글의 최근 바뀜", "recentchangeslinked-title": "\"$1\" 문서에 관련된 문서 바뀜", - "recentchangeslinked-summary": "지정된 문서를 가리키는 문서(또는 지정된 분류에 들어 있는 문서)에 대한 최근에 바뀐 목록입니다.\n[[Special:Watchlist|주시문서 목록]]에 있는 문서는 <strong>굵게</strong> 나타납니다.", + "recentchangeslinked-summary": "해당 문서에 연결된 문서의 변경사항을 확인하려면 문서 이름을 입력하십시오. (분류에 들어있는 문서를 보려면 분류:분류명으로 입력하십시오). [[Special:Watchlist|내 주시문서 목록]]에 있는 문서의 변경사항은 <strong>굵게</strong> 나타납니다.", "recentchangeslinked-page": "문서 이름:", "recentchangeslinked-to": "해당 문서를 가리키는 문서의 최근 바뀜 보기", "recentchanges-page-added-to-category": "[[:$1]]이(가) 분류에 추가되었습니다", @@ -2401,7 +2403,6 @@ "sp-contributions-newonly": "새 글인 기여만 보기", "sp-contributions-hideminor": "사소한 편집 숨기기", "sp-contributions-submit": "검색", - "sp-contributions-explain": "", "sp-contributions-outofrange": "결과를 표시할 수 없습니다. 요청된 IP 대역은 CIDR /$1 한도보다 더 큽니다.", "whatlinkshere": "여기를 가리키는 문서", "whatlinkshere-title": "\"$1\" 문서를 가리키는 문서 목록", @@ -3375,7 +3376,7 @@ "autosumm-replace": "내용을 \"$1\"(으)로 바꿈", "autoredircomment": "[[$1]] 문서로 넘겨주기", "autosumm-removed-redirect": "[[$1]]에 대한 넘겨주기를 제거함", - "autosumm-changed-redirect-target": "넘겨주기 대상을 [[$1]]에서 [[$2]](으)로 변경했습니다", + "autosumm-changed-redirect-target": "넘겨주기 대상을 [[$1]]에서 [[$2]] 문서로 변경했습니다", "autosumm-new": "새 문서: $1", "autosumm-newblank": "빈 문서를 만듦", "size-bytes": "$1 {{PLURAL:$1|바이트}}", @@ -3516,6 +3517,8 @@ "tag-mw-replace-description": "문서 내용 중 90% 보다 많은 내용을 제거한 편집", "tag-mw-rollback": "되돌리기", "tag-mw-rollback-description": "되돌리기 링크를 사용하여 이전 편집을 되돌리는 편집", + "tag-mw-undo": "편집 취소", + "tag-mw-undo-description": "편집 취소 링크를 사용하여 이전 편집을 취소하는 편집", "tags-title": "태그", "tags-intro": "이 문서는 소프트웨어에서 편집에 대해 표시하는 태그와 ê·¸ 의미를 설명하는 목록입니다.", "tags-tag": "태그 이름", diff --git a/languages/i18n/lb.json b/languages/i18n/lb.json index adb240b98d..1374c9fe3e 100644 --- a/languages/i18n/lb.json +++ b/languages/i18n/lb.json @@ -523,11 +523,11 @@ "botpasswords-insert-failed": "De Botnumm \"$1\" konnt net dobäigesat ginn. Gouf e schonn derbäigesat?", "botpasswords-update-failed": "Den Numm vum Bot \"$1\" konnt net aktualiséiert ginn. Gouf e geläscht?", "botpasswords-created-title": "Botpasswuert ugeluecht", - "botpasswords-created-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf ugeluecht.", + "botpasswords-created-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum {{GENDER:$2|Benotzer}} ''$2'' gouf ugeluecht.", "botpasswords-updated-title": "Botpasswuert aktualiséiert", - "botpasswords-updated-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf aktualiséiert.", + "botpasswords-updated-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum {{GENDER:$2|Benotzer}} ''$2'' gouf aktualiséiert.", "botpasswords-deleted-title": "Botpasswuert geläscht", - "botpasswords-deleted-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf geläscht.", + "botpasswords-deleted-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum {{GENDER:$2|Benotzer}} ''$2'' gouf geläscht.", "botpasswords-newpassword": "Dat neit Passwuert fir sech mat <strong>$1</strong> anzeloggen ass <strong>$2</strong>.\n<em>Versuergt dat fir sech spéider dorop ze referéieren.</em><br />(Fir al Botten déi verlaangen datt de Login-Numm d'selwecht ass wéi den spéidere Benotzernumm, kënnt Dir och <strong>$3</strong> als Benotzernumm benotzten a(n) <strong>$4</strong> als Passwuert.)", "botpasswords-no-provider": "BotPasswordsSessionProvider ass net disponibel.", "botpasswords-not-exist": "De Benotzer \"$1\" huet kee Botpasswuert mam Numm \"$2\".", @@ -973,7 +973,7 @@ "recentchangesdays-max": "(Maximal $1 {{PLURAL:$1|Dag|Deeg}})", "recentchangescount": "Zuel vun den Ännerungen déi als Standard gewise ginn:", "prefs-help-recentchangescount": "Inklusiv Rezent Ännerungen, Versiounshistoriquen a Logbicher.", - "prefs-help-watchlist-token2": "Dëst ass de geheime Schlëssel fir de Webfeed vun Ärer Iwwerwaachungslëscht. Jiddwereen deen e kennt kann Är Iwwerwaachungslëscht liesen, dofir sollt Dir en net weider ginn. [[Special:ResetTokens|Klickt hei wann Dir en zrécksetze musst]].", + "prefs-help-watchlist-token2": "Dëst ass de geheime Schlëssel fir de Webfeed vun Ärer Iwwerwaachungslëscht. Jiddwereen deen e kennt kann Är Iwwerwaachungslëscht liesen, dofir sollt Dir en net weider ginn. Wann Dir wëllt [[Special:ResetTokens|kënnt Dir en hei zrécksetze kënnt]].", "savedprefs": "Är Astellunge goufe gespäichert.", "savedrights": "D'Benotzergruppe vum {{GENDER:$1|$1}} goufe gespäichert.", "timezonelegend": "Zäitzon:", @@ -993,6 +993,7 @@ "timezoneregion-indian": "Indeschen Ozean", "timezoneregion-pacific": "Pazifeschen Ozean", "allowemail": "E-Maile vun anere Benotzer kréien.", + "email-allow-new-users-label": "E-Maile vu ganz neie Benotzer erlaben", "prefs-searchoptions": "Sichen", "prefs-namespaces": "Nummraim", "default": "Standard", @@ -1157,6 +1158,7 @@ "right-siteadmin": "Datebank spären an d'Spär ophiewen", "right-override-export-depth": "Säiten exportéieren inklusiv de verlinkte Säite bis zu enger Déift vu 5", "right-sendemail": "Anere Benotzer E-Maile schécken", + "right-sendemail-new-users": "E-Mailen u Benotzer ouni geloggt Aktioune schécken", "grant-group-page-interaction": "Mat Säiten interagéieren", "grant-group-watchlist-interaction": "Mat Ärer Iwwerwaachungslëscht interagéieren", "grant-group-email": "E-Mail schécken", @@ -1370,6 +1372,7 @@ "rcfilters-watchlist-edit-watchlist-button": "Ännert Är Lëscht vun iwwerwaachte Säiten", "rcfilters-watchlist-showupdated": "Ännerungen op Säiten déi Dir net besicht hutt zanter d'Ännerunge gemaach goufen si <strong>fett</strong> geschriwwen.", "rcfilters-preference-label": "Déi verbessert Versioun vun de rezenten Ännerunge verstoppen", + "rcfilters-target-page-placeholder": "Gitt en Numm vun enger Säit an", "rcnotefrom": "Hei drënner {{PLURAL:$5|gëtt d'Ännerung|ginn d'Ännerungen}} zanter <strong>$3, $4</strong> (maximal <strong>$1</strong> Ännerunge gi gewisen).", "rclistfromreset": "Eraussiche vum Datum zrécksetzen", "rclistfrom": "Nei Ännerunge vum $3 $2 u weisen", @@ -1415,7 +1418,7 @@ "recentchangeslinked-feed": "Ännerungen op verlinkt Säiten", "recentchangeslinked-toolbox": "Ännerungen op verlinkt Säiten", "recentchangeslinked-title": "Ännerungen a Verbindung mat \"$1\"", - "recentchangeslinked-summary": "Dëst ass eng Lëscht mat Ännerunge vu verlinkte Säiten op eng bestëmmte Säit (oder vu Membersäite vun der spezifizéierter Kategorie).\nSäite vun [[Special:Watchlist|Ärer Iwwerwaachungslëscht]] si '''fett''' geschriwwen.", + "recentchangeslinked-summary": "Gitt den Numm vun enger Säit a fir Ännerungen Säiten ze gesinn op déi oder vun deene gelinkt gëtt. Ännerungen op Säite vun [[Special:Watchlist|Ärer Iwwerwaachungslëscht]] si <strong>fett</strong> geschriwwen.", "recentchangeslinked-page": "Säitennumm:", "recentchangeslinked-to": "Weis Ännerungen zu de verlinkte Säiten aplaz vun der gefroter Säit", "recentchanges-page-added-to-category": "[[:$1]] an d'Kategorie derbäigesat", @@ -2723,8 +2726,8 @@ "svg-long-desc-animated": "Animéierten SVG-Fichier, Basisgréisst $1 x $2 Pixel, Gréisst vum Fichier: $3", "svg-long-error": "Ongëltegen SVG-Fichier: $1", "show-big-image": "Original Fichier", - "show-big-image-preview": "Gréisst vun dësem Preview: $1.", - "show-big-image-preview-differ": "Gréisst vun dësem $3-Preview vun dësem $2-Fichier: $1.", + "show-big-image-preview": "Gréisst vun dëser Duerstellung: $1.", + "show-big-image-preview-differ": "Gréisst vun dëser $3-Duerstellung vun dësem $2-Fichier: $1.", "show-big-image-other": "Aner {{PLURAL:$2|Opléisung|Opléisungen}}: $1.", "show-big-image-size": "$1 × $2 Pixel", "file-info-gif-looped": "endlos Schleef", diff --git a/languages/i18n/lt.json b/languages/i18n/lt.json index a29f1addcb..f29f31db6d 100644 --- a/languages/i18n/lt.json +++ b/languages/i18n/lt.json @@ -36,7 +36,8 @@ "Zygimantus", "Matma Rex", "Nemo bis", - "Nersip" + "Nersip", + "Manvydasz" ] }, "tog-underline": "Nuorodos pabraukimas:", @@ -472,7 +473,7 @@ "nosuchusershort": "Nėra jokio naudotojo, pavadinto „$1“. Patikrinkite raÅ¡ybą.", "nouserspecified": "Jums reikia nurodyti naudotojo vardą.", "login-userblocked": "Å is naudotojas yra užblokuotas. Prisijungti neleidžiama.", - "wrongpassword": "Ä®vestas neteisingas slaptažodis. Pamėginkite dar kartą.", + "wrongpassword": "Ä®vestas neteisingas vartotojo vardas ar slaptažodis. Pamėginkite dar kartą.", "wrongpasswordempty": "Ä®vestas slaptažodis yra tuščias. Pamėginkite vėl.", "passwordtooshort": "Slaptažodžiai turi bÅ«ti bent $1 {{PLURAL:$1|simbolio|simbolių|simbolių}} ilgio.", "passwordtoolong": "Slaptažodžiai negali bÅ«ti ilgesni nei {{PLURAL:$1|1 simbolis|$1 simboliai}}.", @@ -718,7 +719,7 @@ "permissionserrorstext-withaction": "JÅ«s neturite leidimo $2 dėl {{PLURAL:$1|Å¡ios priežasties|Å¡ių priežasčių}}:", "contentmodelediterror": "JÅ«s negalite redaguoti Å¡ios versijos, nes jos turinio modelis yra <code>$1</code>, kuris skiriasi nuo dabartinio puslapio turinio modelio, kuris yra <code>$2</code>.", "recreate-moveddeleted-warn": "'''Dėmesio: JÅ«s atkuriate puslapį, kuris anksčiau buvo iÅ¡trintas.'''\n\nTurėtumėte nuspręsti, ar reikėtų toliau redaguoti šį puslapį.\nJÅ«sų patogumui čia pateikiamas Å¡io puslapio Å¡alinimų ir perkėlimų sąraÅ¡as:", - "moveddeleted-notice": "Å is puslapis buvo iÅ¡trintas.\nŽemiau pateikiamas puslapio Å¡alinimų ir pervadinimų sąraÅ¡as.", + "moveddeleted-notice": "Å is puslapis buvo iÅ¡trintas.\nŽemiau pateikiamas puslapio Å¡alinimų, apsaugojimų, ir pervadinimų sąraÅ¡as.", "moveddeleted-notice-recent": "AtsipraÅ¡ome, Å¡is puslapis neseniai buvo iÅ¡trintas (per pastarąsias 24 valandas). Žemiau pateikiama detali puslapio iÅ¡trynimo ir perkėlimo istorija.", "log-fulllog": "Rodyti visą istoriją", "edit-hook-aborted": "Keitimas nutrauktas užlūžimo.\nTam nėra paaiÅ¡kinimo.", @@ -1002,7 +1003,7 @@ "prefs-help-recentchangescount": "Ä® tai įeina naujausi keitimai, puslapių istorijos ir specialiųjų veiksmų sąraÅ¡ai.", "prefs-help-watchlist-token2": "Tai yra slaptas jÅ«sų stebimųjų sąraÅ¡o raktas, skirtas žiniatinkliui.\nKiekvienas, kurį jį žino, gali skaityti jÅ«sų stebimųjų puslapių sąrašą, taigi, juo nesidalinkite.\nJei reikia jį anuliuoti, [[Special:ResetTokens|spauskite čia]].", "savedprefs": "Nustatymai sėkmingai iÅ¡saugoti.", - "savedrights": "Naudotojo teisės {{GENDER:$1|$1}} buvo iÅ¡saugotos.", + "savedrights": "Naudotojo {{GENDER:$1|$1}} grupės buvo iÅ¡saugotos.", "timezonelegend": "Laiko juosta:", "localtime": "Vietinis laikas:", "timezoneuseserverdefault": "Naudoti wiki pradinį ($1)", @@ -1020,6 +1021,7 @@ "timezoneregion-indian": "Indijos vandenynas", "timezoneregion-pacific": "Ramusis vandenynas", "allowemail": "Leisti kitiems naudotojams siųsti man el. laiÅ¡kus", + "email-allow-new-users-label": "Leidžia el. laiÅ¡kus iÅ¡ naujų vartotojų", "email-blacklist-label": "Neleisti Å¡iems vartotojams siųsti man el. laiÅ¡kų:", "prefs-searchoptions": "PaieÅ¡ka", "prefs-namespaces": "Vardų sritys", @@ -1252,7 +1254,7 @@ "action-deletedhistory": "žiÅ«rėti puslapio iÅ¡trintą istoriją", "action-browsearchive": "ieÅ¡koti iÅ¡trintų puslapių", "action-undelete": "atkurti puslapius", - "action-suppressrevision": "peržiÅ«rėti ir atkurti Å¡ią paslėptą versiją", + "action-suppressrevision": "peržiÅ«rėti ir atkurti paslėptas versijas", "action-suppressionlog": "peržiÅ«rėti šį privatų registrą", "action-block": "neleisti Å¡iam naudotojui redaguoti", "action-protect": "pakeisti apsaugos lygius Å¡iam puslapiui", @@ -1296,7 +1298,7 @@ "rcfilters-activefilters": "AktyvÅ«s filtrai", "rcfilters-advancedfilters": "DetalÅ«s filtrai", "rcfilters-quickfilters": "IÅ¡saugoti filtrai", - "rcfilters-quickfilters-placeholder-title": "Nėra iÅ¡saugotų nuorodų", + "rcfilters-quickfilters-placeholder-title": "Nėra iÅ¡saugotų filtrų", "rcfilters-savedqueries-defaultlabel": "IÅ¡saugoti filtrai", "rcfilters-savedqueries-rename": "Pervadinti", "rcfilters-savedqueries-setdefault": "Nustatyti kaip numatytą", @@ -1304,7 +1306,7 @@ "rcfilters-savedqueries-remove": "PaÅ¡alinti", "rcfilters-savedqueries-new-name-label": "Pavadinimas", "rcfilters-savedqueries-new-name-placeholder": "ApibÅ«dinkite Å¡io filtro tikslą.", - "rcfilters-savedqueries-apply-label": "IÅ¡saugoti nustatymus", + "rcfilters-savedqueries-apply-label": "Sukurti filtrą", "rcfilters-savedqueries-cancel-label": "AtÅ¡aukti", "rcfilters-savedqueries-add-new-title": "IÅ¡saugoti dabartinius filtro nustatymus", "rcfilters-restore-default-filters": "Atstatyti numatytuosius filtrus", @@ -1313,8 +1315,8 @@ "rcfilters-invalid-filter": "Negalimas filtras", "rcfilters-empty-filter": "Nėra aktyvių filtrų. Rodomi visi indeliai.", "rcfilters-filterlist-title": "Filtrai", - "rcfilters-filterlist-whatsthis": "Kas tai?", - "rcfilters-filterlist-feedbacklink": "Pateikite atsiliepimą apie naujus (beta) filtrus", + "rcfilters-filterlist-whatsthis": "Kaip tai veikia?", + "rcfilters-filterlist-feedbacklink": "Pateikite atsiliepimą apie Å¡iuos (naujus) filtravimo įrankius", "rcfilters-highlightbutton-title": "ParyÅ¡kinti rezultatus", "rcfilters-highlightmenu-title": "Pasirinkite spalvą", "rcfilters-highlightmenu-help": "Pasirinkite spalvą Å¡io elemento paryÅ¡kinimui", @@ -1326,9 +1328,9 @@ "rcfilters-filter-editsbyother-description": "Visi keitimai, iÅ¡skyrus jÅ«sų.", "rcfilters-filtergroup-userExpLevel": "Patirties lygis (tik registruotiems vartotojams)", "rcfilters-filter-user-experience-level-registered-label": "Registruoti", - "rcfilters-filter-user-experience-level-registered-description": "Prisijungę redaktoriai.", + "rcfilters-filter-user-experience-level-registered-description": "Prisijungę naudotojai.", "rcfilters-filter-user-experience-level-unregistered-label": "Neregistruoti", - "rcfilters-filter-user-experience-level-unregistered-description": "Redaktoriai, kurie nėra prisijungę.", + "rcfilters-filter-user-experience-level-unregistered-description": "Naudotojai, kurie nėra prisijungę.", "rcfilters-filter-user-experience-level-newcomer-label": "Naujokai", "rcfilters-filter-user-experience-level-newcomer-description": "Mažiau nei 10 keitimų ir 4 dienų aktyvumo.", "rcfilters-filter-user-experience-level-learner-label": "Mokiniai", @@ -1340,6 +1342,10 @@ "rcfilters-filter-humans-label": "Žmogaus (ne roboto)", "rcfilters-filter-humans-description": "Keitimai atlikti žmonių.", "rcfilters-filtergroup-reviewstatus": "PeržiÅ«rėti statusą", + "rcfilters-filter-patrolled-label": "Stebimas", + "rcfilters-filter-patrolled-description": "Pakeitimai pažymėti kaip stebimi.", + "rcfilters-filter-unpatrolled-label": "Nestebimas", + "rcfilters-filter-unpatrolled-description": "Pakeitimai pažymėti kaip nestebimi.", "rcfilters-filtergroup-significance": "ReikÅ¡mė", "rcfilters-filter-minor-label": "SmulkÅ«s pakeitimai", "rcfilters-filter-minor-description": "Keitimai, kuriuos autorius pažymėjo kaip mažus.", @@ -1350,6 +1356,8 @@ "rcfilters-filter-watchlist-watched-description": "Pakeitimai puslapiuose, jÅ«sų Stebimųjų sąraÅ¡e.", "rcfilters-filter-watchlist-watchednew-label": "Nauji Stebimųjų sąraÅ¡o pakeitimai", "rcfilters-filter-watchlist-notwatched-label": "Nėra Stebimųjų sąraÅ¡e", + "rcfilters-filter-watchlistactivity-unseen-label": "NeperžiÅ«rėti pakeitimai", + "rcfilters-filter-watchlistactivity-seen-label": "PeržiÅ«rėti pakeitimai", "rcfilters-filtergroup-changetype": "Pakeitimo tipas", "rcfilters-filter-pageedits-label": "Puslapių keitimai", "rcfilters-filter-newpages-label": "Puslapių sukÅ«rimai", @@ -1361,6 +1369,14 @@ "rcfilters-filter-previousrevision-description": "Visi keitimai, kurie nėra naujausi puslapio keitimai.", "rcfilters-view-tags": "Pažymėti keitimai", "rcfilters-view-tags-help-icon-tooltip": "Sužinoti daugiau apie Pažymėtus pakeitimus", + "rcfilters-liveupdates-button": "Gyvi atnaujinimai", + "rcfilters-liveupdates-button-title-on": "IÅ¡jungti gyvus atnaujinimus", + "rcfilters-watchlist-markseen-button": "Pažymėti visus pakeitimus kaip peržiÅ«rėtus", + "rcfilters-watchlist-edit-watchlist-button": "Redaguoti stebimųjų sąrašą", + "rcfilters-watchlist-showupdated": "Puslapiai pakeisti nuo tada, kai paskutinį kartą apsilankėte juose, yra <strong>paryÅ¡kinti</strong>.", + "rcfilters-preference-label": "Slėpti patobulintą naujausių pakeitimų versiją", + "rcfilters-filter-showlinkedfrom-label": "Rodyti pakeitimus puslapiuose, iÅ¡ kurių esate nukreipti", + "rcfilters-target-page-placeholder": "Ä®veskite puslapio pavadinimą", "rcnotefrom": "Žemiau yra {{PLURAL:$5|pakeitimas|pakeitimai}} pradedant <strong>$3, $4</strong> (rodoma iki <strong>$1</strong> pakeitimų).", "rclistfromreset": "Nustatyti duomenų pasirinkimą iÅ¡ naujo", "rclistfrom": "Rodyti naujus pakeitimus pradedant $3 $2", @@ -1632,7 +1648,7 @@ "listfiles_size": "Dydis", "listfiles_description": "ApraÅ¡ymas", "listfiles_count": "Versijos", - "listfiles-show-all": "Ä®traukti senesnes paveikslėlių versijas", + "listfiles-show-all": "Ä®traukti senesnes rinkmenų versijas", "listfiles-latestversion": "Dabartinė versija", "listfiles-latestversion-yes": "Taip", "listfiles-latestversion-no": "Ne", @@ -2022,7 +2038,7 @@ "unwatchthispage": "Nustoti stebėti", "notanarticle": "Ne turinio puslapis", "notvisiblerev": "Versija buvo iÅ¡trinta", - "watchlist-details": "Stebima {{PLURAL:$1|$1 puslapis|$1 puslapiai|$1 puslapių}}, neskaičiuojant aptarimų puslapių.", + "watchlist-details": "Stebima {{PLURAL:$1|$1 puslapis|$1 puslapiai|$1 puslapių}}, (įskaičiuojant aptarimų puslapius).", "wlheader-enotif": "El. paÅ¡to praneÅ¡imai yra įjungti.", "wlheader-showupdated": "Puslapiai pakeisti nuo tada, kai paskutinį kartą apsilankėte juose, yra '''paryÅ¡kinti'''.", "wlnote": "{{PLURAL:$1|Rodomas '''$1''' paskutinis pakeitimas, atliktas|Rodomi '''$1''' paskutiniai pakeitimai, atlikti|Rodoma '''$1''' paskutinių pakeitimų, atliktų}} per '''$2''' {{PLURAL:$2|paskutinę valandą|paskutines valandas|paskutinių valandų}}, nuo $3 $4.", @@ -2057,6 +2073,7 @@ "enotif_lastdiff": "Užeikite į $1, jei norite pamatyti šį pakeitimą.", "enotif_anon_editor": "anoniminis naudotojas $1", "enotif_body": "$WATCHINGUSERNAME,\n\n\n$PAGEEDITDATE {{SITENAME}} projekte $PAGEEDITOR $CHANGEDORCREATED puslapį „$PAGETITLE“, dabartinę versiją rasite adresu $PAGETITLE_URL.\n\n$NEWPAGE\n\nRedaguotojo komentaras: $PAGESUMMARY $PAGEMINOREDIT\n\nSusisiekti su redaguotoju:\nel. paÅ¡tu: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nDaugiau praneÅ¡imų apie vėlesnius pakeitimus nebus siunčiama, jei neapsilankysite puslapyje.\nJÅ«s taip pat galite iÅ¡jungti praneÅ¡imo žymę visiems jÅ«sų stebimiems puslapiams savo stebimųjų sąraÅ¡e.\n\n JÅ«sų draugiÅ¡koji projekto {{SITENAME}} praneÅ¡imų sistema\n\n--\nNorėdami pakeisti e-paÅ¡tu siunčiamų praneÅ¡imų nustatymus, užeikite į\n{{canonicalurl:{{#special:Preferences}}}}\n\nNorėdami pakeisti stebimųjų puslapių nustatymus, užeikite į\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nNorėdami puslapį iÅ¡ stebimųjų puslapių sąraÅ¡o, užeikite į\n$UNWATCHURL\n\nAtsiliepimai ir pagalba:\n$HELPPAGE", + "enotif_minoredit": "Tai smulkus pakeitimas", "created": "sukurė", "changed": "pakeitė", "deletepage": "Trinti puslapį", @@ -2620,7 +2637,7 @@ "anonymous": "{{SITENAME}} {{PLURAL:$1|anoniminis naudotojas|anoniminiai naudotojai}}", "siteuser": "{{SITENAME}} {{GENDER:$2|naudotojas|naudotoja}} $1", "anonuser": "{{SITENAME}} anoniminis naudotojas $1", - "lastmodifiedatby": "Šį puslapį paskutinį kartą redagavo $3 $2, $1.", + "lastmodifiedatby": "Šį puslapį paskutinį kartą redagavo $2, $1, $3.", "othercontribs": "Paremta $1 darbu.", "others": "kiti", "siteusers": "{{SITENAME}} {{PLURAL:$2|naudotojas|naudotojai}} $1", @@ -3393,7 +3410,7 @@ "compare-invalid-title": "JÅ«sų nurodytas pavadinimas neleistinas.", "compare-title-not-exists": "Pavadinimas, kurį nurodėte, neegzistuoja.", "compare-revision-not-exists": "Keitimas, kurį nurodėte, neegzistuoja.", - "diff-form": "'''forma'''", + "diff-form": "Skirtumai", "dberr-problems": "AtsipraÅ¡ome! Svetainei iÅ¡kilo techninių problemų.", "dberr-again": "Palaukite kelias minutes ir perkraukite puslapį.", "dberr-info": "(Nepavyksta pasiekti duomenų bazės: $1)", diff --git a/languages/i18n/lv.json b/languages/i18n/lv.json index dcecadc327..083141b171 100644 --- a/languages/i18n/lv.json +++ b/languages/i18n/lv.json @@ -366,6 +366,8 @@ "mypreferencesprotected": "Jums nav tiesÄ«bu rediģēt savus iestatÄ«jumus.", "ns-specialprotected": "Nevar izmainÄ«t Ä«pašās lapas.", "titleprotected": "Å Ä« lapa ir aizsargāta pret izveidoÅ¡anu. To aizsargāja [[User:$1|$1]].\nNorādÄ«tais iemesls bija <em>$2</em>.", + "invalidtitle-knownnamespace": "NederÄ«gs nosaukums ar vārdtelpu \"$2\" un tekstu \"$3\"", + "invalidtitle-unknownnamespace": "NederÄ«gs nosaukums ar nezināmu vārdtelpas numuru \"$1\" un tekstu \"$2\"", "exception-nologin": "Neesat pieslēdzies", "virus-badscanner": "Nekorekta konfigurācija: nezināms vÄ«rusu skeneris: ''$1''", "virus-scanfailed": "skenēšana neizdevās (kods $1)", @@ -556,6 +558,7 @@ "preview": "Pirmskats", "showpreview": "RādÄ«t pirmskatu", "showdiff": "RādÄ«t izmaiņas", + "blankarticle": "<strong>BrÄ«dinājums:</strong> Lapa, ko tu veido, ir tukÅ¡a.\nJa tu vēlreiz nospiedÄ«si uz \"$1\", tiks izveidota lapa bez jebkāda satura.", "anoneditwarning": "<strong>UzmanÄ«bu:</strong> tu neesi pieslēdzies. Ja veiksi labojumus, publiski bÅ«s redzama tava IP adrese. Ja tu <strong>[$1 pieslēgsies]</strong> vai <strong>[$2 izveidosi kontu]</strong>, visi labojumi tiks piesaistÄ«ti tavam kontam; bÅ«s arÄ« citi ieguvumi.", "anonpreviewwarning": "''Tu neesi ienācis. Saglabājot lapu, Tava IP adrese tiks ierakstÄ«ta Å¡Ä«s lapas hronoloÄ£ijā.''", "missingsummary": "'''Atgādinājums''': Tu neesi norādÄ«jis izmaiņu kopsavilkumu. Vēlreiz klikÅ¡Ä·inot uz \"Saglabāt lapu\", Tavas izmaiņas tiks saglabātas bez kopsavilkuma.", @@ -1039,6 +1042,7 @@ "grant-highvolume": "Liela apjoma laboÅ¡ana", "grant-patrol": "Patrulēt lapu izmaiņas", "grant-privateinfo": "Piekļūt privātai informācijai", + "grant-rollback": "Atcelt izmaiņas lapās", "grant-sendemail": "SÅ«tÄ«t e-pastu citiem dalÄ«bniekiem", "grant-uploadeditmovefile": "AugÅ¡upielādēt, aizvietot un pārvietot failus", "grant-uploadfile": "AugÅ¡upielādēt jaunus failus", @@ -1109,7 +1113,8 @@ "rcfilters-group-results-by-page": "Grupēt rezultātus pēc lapas", "rcfilters-activefilters": "AktÄ«vie filtri", "rcfilters-advancedfilters": "PaplaÅ¡inātie filtri", - "rcfilters-limit-title": "Rādāmās izmaiņas", + "rcfilters-limit-title": "Rādāmie rezultāti", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|$1 izmaiņas|$1 izmaiņa|$1 izmaiņas}}, $2", "rcfilters-days-title": "Pēdējās dienas", "rcfilters-hours-title": "Pēdējās stundas", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dienas|diena|dienas}}", @@ -1128,7 +1133,7 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "Izveidot noklusēto filtru", "rcfilters-savedqueries-cancel-label": "Atcelt", "rcfilters-savedqueries-add-new-title": "Saglabāt esoÅ¡os filtra iestatÄ«jumus", - "rcfilters-savedqueries-already-saved": "Å ie filtri jau ir saglabāti", + "rcfilters-savedqueries-already-saved": "Å ie filtri jau ir saglabāti. Izmaini savus iestatÄ«jumus, lai izveidotu jaunu saglabāto filtru.", "rcfilters-restore-default-filters": "Atjaunot noklusētos filtrus", "rcfilters-clear-all-filters": "Noņemt visus filtrus", "rcfilters-show-new-changes": "SkatÄ«t jaunās izmaiņas", @@ -1187,14 +1192,15 @@ "rcfilters-filter-logactions-description": "AdministratÄ«vās darbÄ«bas, kontu veidoÅ¡ana, lapu dzēšana, augÅ¡upielādes...", "rcfilters-filtergroup-lastRevision": "PaÅ¡reizējās versijas", "rcfilters-filter-lastrevision-label": "PaÅ¡reizējā versija", - "rcfilters-filter-lastrevision-description": "Nesenākā lapas izmaiņa.", + "rcfilters-filter-lastrevision-description": "Tikai nesenākā lapas izmaiņa.", "rcfilters-filter-previousrevision-label": "Agrākas versijas", - "rcfilters-filter-previousrevision-description": "Visas izmaiņas, kuras nav pēdējā lapas izmaiņa.", + "rcfilters-filter-previousrevision-description": "Visas izmaiņas, kas nav lapas pēdējā izmaiņa.", "rcfilters-view-tags": "IezÄ«mētie labojumi", "rcfilters-view-namespaces-tooltip": "Filtrēt rezultātus pēc vārdtelpas", "rcfilters-view-tags-help-icon-tooltip": "Uzzināt vairāk par iezÄ«mētajiem labojumiem", "rcfilters-liveupdates-button": "TÅ«lÄ«tēji atjauninājumi", "rcfilters-liveupdates-button-title-on": "Atslēgt tÅ«lÄ«tējos atjauninājumus", + "rcfilters-liveupdates-button-title-off": "RādÄ«t jaunās izmaiņas, tiklÄ«dz tās tiek veiktas", "rcfilters-watchlist-markseen-button": "AtzÄ«mēt visas izmaiņas kā apskatÄ«tas", "rcfilters-watchlist-edit-watchlist-button": "Labot manu uzraugāmo lapu sarakstu", "rcfilters-preference-label": "Paslēpt uzlaboto pēdējo izmaiņu versiju", @@ -1722,7 +1728,7 @@ "enotif_reset": "AtzÄ«mēt visas lapas kā apskatÄ«tas", "enotif_impersonal_salutation": "{{SITENAME}} lietotājs", "enotif_lastvisited": "$1 lai apskatÄ«tos visas izmaiņas kopÅ¡ tava pēdējā apmeklējuma.", - "enotif_lastdiff": "$1 lai apskatÄ«tos Å¡o izmaiņu.", + "enotif_lastdiff": "Lai apskatÄ«tu Å¡o izmaiņu, skatÄ«t $1", "enotif_anon_editor": "anonÄ«ms dalÄ«bnieks $1", "enotif_body": "$WATCHINGUSERNAME,\n\n\n{{grammar:Ä£enitÄ«vs|{{SITENAME}}}} lapu $PAGETITLE $CHANGEDORCREATED $PAGEEDITOR, $PAGEEDITDATE, paÅ¡reizējā versja ir $PAGETITLE_URL.\n\n$NEWPAGE\n\nIzmaiņu kopsavilkums bija: $PAGESUMMARY $PAGEMINOREDIT\n\nSazināties ar attiecÄ«go lietotāju:\ne-pasts: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nJa Å¡o uzraugāmo lapu izmainÄ«s vēl, turpmāku paziņojumu par to nebÅ«s, kamēr tu to neatvērsi.\nTu arÄ« vari atstatÄ«t visu uzraugāmo lapu paziņojumu statusus uzraugāmo lapu sarakstā.\n\n {{grammar:Ä£enitÄ«vs|{{SITENAME}}}} paziņojumu sistēma\n\n--\nLai izmainÄ«tu uzraugāmo lapu saraksta uzstādÄ«jumus:\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nLai dzēstu lapu no uzraugāmo lapu saraksta:\n$UNWATCHURL\n\nPapildinformācija:\n$HELPPAGE", "enotif_minoredit": "Å is ir maznozÄ«mÄ«gs labojums", @@ -1874,6 +1880,7 @@ "year": "No gada (un senāki):", "sp-contributions-newbies": "RādÄ«t jauno lietotāju devumu", "sp-contributions-newbies-sub": "Jaunie lietotāji", + "sp-contributions-newbies-title": "Jauno dalÄ«bnieku devums", "sp-contributions-blocklog": "bloķēšanas reÄ£istrs", "sp-contributions-suppresslog": "cenzēja {{GENDER:$1|dalÄ«bnieka|dalÄ«bnieces}} devumu", "sp-contributions-deleted": "dzēstais {{GENDER:$1|dalÄ«bnieka|dalÄ«bnieces}} devums", @@ -1991,6 +1998,7 @@ "ipb_blocked_as_range": "Kļūda: IP $1 nav bloķēta tieÅ¡i, tāpēc to nevar atbloķēt.\nTā ir bloķēta kā daļa no IP adreÅ¡u diapazona $2, kuru var atbloķēt.", "ip_range_invalid": "NederÄ«gs IP diapazons", "proxyblocker": "Starpniekservera bloķētājs", + "softblockrangesreason": "No tavas IP adreses ($1) nav atļauts anonÄ«ms devums. LÅ«dzu, pieslēdzies.", "ipbblocked": "Tu nevar bloķēt vai atbloķēt lietotājus, jo Tu pats esi bloķēts", "ipbnounblockself": "Tev nav atļauts sevi atbloķēt", "lockdb": "Bloķēt datubāzi", @@ -2222,6 +2230,7 @@ "pageinfo-length": "Lapas garums (baitos)", "pageinfo-article-id": "Lapas ID", "pageinfo-language": "Lappuses satura valoda", + "pageinfo-language-change": "izmainÄ«t", "pageinfo-content-model": "Lapas satura modelis", "pageinfo-content-model-change": "mainÄ«t", "pageinfo-robot-policy": "Indeksācija ar robotiem", @@ -2263,6 +2272,7 @@ "markedaspatrollederror": "Nevar atzÄ«mēt kā pārbaudÄ«tu", "markedaspatrollederrortext": "Jums jānorāda versija, ko atzÄ«mēt kā pārbaudÄ«tu.", "markedaspatrollederror-noautopatrol": "Jums nav atļaujas atzÄ«mēt savas izmaiņas kā pārbaudÄ«tas.", + "markedaspatrollednotify": "Å Ä« izmaiņa lapā \"$1\" ir atzÄ«mēta kā patrulēta.", "patrol-log-page": "Pārbaudes reÄ£istrs", "patrol-log-header": "Å is ir pārbaudÄ«to versiju reÄ£istrs.", "log-show-hide-patrol": "$1 pārbaudes reÄ£istrs", @@ -2299,6 +2309,7 @@ "newimages-legend": "Filtrs", "newimages-label": "Faila nosaukums (vai tā daļa):", "newimages-user": "IP adrese vai lietotājvārds", + "newimages-newbies": "RādÄ«t tikai jaunu dalÄ«bnieku devumu", "newimages-showbots": "ParādÄ«t botu augÅ¡upielādētos failus", "newimages-hidepatrolled": "Paslēpt pārbaudÄ«tās augÅ¡upielādes", "noimages": "Nav nekā ko redzēt.", @@ -2639,6 +2650,8 @@ "autosumm-blank": "Nodzēsa lapu", "autosumm-replace": "Aizvieto lapas saturu ar '$1'", "autoredircomment": "Pāradresē uz [[$1]]", + "autosumm-removed-redirect": "Noņēma pāradresāciju uz [[$1]]", + "autosumm-changed-redirect-target": "Pāradresācija nomainÄ«ta no [[$1]] uz [[$2]]", "autosumm-new": "Jauna lapa: $1", "autosumm-newblank": "Izveidota tukÅ¡a lapa", "lag-warn-normal": "Izmaiņas, kas ir jaunākas par $1 {{PLURAL:$1|sekundēm|sekundi|sekundēm}}, var neparādÄ«ties Å¡ajā sarakstā.", @@ -2737,6 +2750,16 @@ "tag-filter-submit": "Filtrs", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|IezÄ«mes|IezÄ«me|IezÄ«mes}}]]: $2)", "tag-mw-contentmodelchange": "satura modeļa izmaiņa", + "tag-mw-contentmodelchange-description": "Labojumi, kas [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel maina lapas satura modeli]", + "tag-mw-new-redirect": "Jauna pāradresācija", + "tag-mw-new-redirect-description": "Labojumi, kas izveido jaunu pāradresāciju, vai pārveido lapu par pāradresāciju", + "tag-mw-removed-redirect": "Noņēma pāradresāciju", + "tag-mw-changed-redirect-target": "Pāradresācijas mērÄ·is nomainÄ«ts", + "tag-mw-changed-redirect-target-description": "Labojumi, kas maina pāradresācijas mērÄ·i", + "tag-mw-blank": "Nodzēsta lapa", + "tag-mw-blank-description": "Labojumi, kas nodzēš lapas saturu", + "tag-mw-replace": "Aizvietots", + "tag-mw-replace-description": "Labojumi, kas izņem vairāk kā 90% no lapas satura", "tags-title": "IezÄ«mes", "tags-intro": "Å ajā lapā uzskaitÄ«tas iezÄ«mes, ar kurām programmatÅ«ra var atzÄ«mēt labojumus, un to nozÄ«me.", "tags-tag": "IezÄ«mes nosaukums", @@ -2857,7 +2880,7 @@ "feedback-useragent": "Lietotāja aÄ£ents:", "searchsuggest-search": "Meklēt {{SITENAME}}", "searchsuggest-containing": "Meklējamā frāze:", - "api-error-unknown-warning": "Nezināms brÄ«dinājums: $1", + "api-error-unknown-warning": "Nezināms brÄ«dinājums: \"$1\".", "api-error-unknownerror": "Nezināma kļūda: \"$1\"", "duration-seconds": "$1 {{PLURAL:$1|sekundes|sekunde|sekundes}}", "duration-minutes": "$1 {{PLURAL:$1|minÅ«tes|minÅ«te|minÅ«tes}}", @@ -2879,6 +2902,7 @@ "expand_templates_ok": "Labi", "expand_templates_remove_nowiki": "Cenzēt <nowiki> iezÄ«mes rezultātā", "expand_templates_preview": "Pirmskats", + "pagelanguage": "MainÄ«t lapas valodu", "pagelang-name": "Lapa", "pagelang-language": "Valoda", "pagelang-select-lang": "Izvēlieties valodu", diff --git a/languages/i18n/lzh.json b/languages/i18n/lzh.json index 4d3ce93890..0b8cc8c32b 100644 --- a/languages/i18n/lzh.json +++ b/languages/i18n/lzh.json @@ -1181,7 +1181,7 @@ "imgfile": "檔", "listfiles": "見檔", "listfiles_thumb": "縮", - "listfiles_date": "時", + "listfiles_date": "時日", "listfiles_name": "名", "listfiles_user": "ç°¿", "listfiles_size": "幅(位元組)", @@ -1197,7 +1197,7 @@ "filehist-deleteone": "刪", "filehist-revert": "還", "filehist-current": "今", - "filehist-datetime": "時", + "filehist-datetime": "日時", "filehist-thumb": "縮", "filehist-thumbtext": "於$1之縮", "filehist-nothumb": "無縮", @@ -1448,7 +1448,7 @@ "wlheader-enotif": "*准報信。", "wlheader-showupdated": "*易者'''粗體'''。", "wlnote": "近<b>$2</b>時有$1者易。", - "wlshowlast": "見近$1時、$2天易", + "wlshowlast": "凡最近$1小時、$2日所更易皆示之", "watchlist-options": "哨項", "watching": "出陣…", "unwatching": "收兵…", @@ -1529,7 +1529,7 @@ "protect-otherreason-op": "它理", "protect-dropdown": "*通錮之理\n** 多破\n** 多灌\n** 反產之戰纂\n** 高量之頁", "protect-edit-reasonlist": "纂護之理", - "protect-expiry-options": "半時:1 hour,一日:1 day,一週:1 week,兩週:2 weeks,一月:1 month,三月:3 months,六月:6 months,一年:1 year,永久:infinite", + "protect-expiry-options": "半小時:1 hour,一日:1 day,一週:1 week,兩週:2 weeks,一月:1 month,三月:3 months,六月:6 months,一年:1 year,恆久:infinite", "restriction-type": "准", "restriction-level": "緘捆", "minimum-size": "幅越", @@ -1632,7 +1632,7 @@ "ipbenableautoblock": "屢禁此簿,新IP址、後繼亦如也。", "ipbsubmit": "禁簿", "ipbother": "別期", - "ipboptions": "二時:2 hours,一日:1 day,三日:3 days,一週:1 week,二週:2 weeks,一月:1 month,三月:3 months,六月:6 months,一年:1 year,永如:infinite", + "ipboptions": "二小時:2 hours,一日:1 day,三日:3 days,一週:1 week,二週:2 weeks,一月:1 month,三月:3 months,六月:6 months,一年:1 year,恆久:infinite", "ipbhidename": "簿名隱乎纂與表", "ipbwatchuser": "哨該簿之齋與議", "ipb-change-block": "用此設重禁此簿", @@ -1655,7 +1655,7 @@ "ipblocklist-submit": "尋", "ipblocklist-localblock": "本禁", "ipblocklist-otherblocks": "他{{PLURAL:$1|禁|禁}}", - "infiniteblock": "永如", + "infiniteblock": "恆久", "expiringblock": "屆$1 $2", "anononlyblock": "惟匿者", "noautoblockblock": "止自禁", diff --git a/languages/i18n/map-bms.json b/languages/i18n/map-bms.json index aa348f529c..e07c525e4e 100644 --- a/languages/i18n/map-bms.json +++ b/languages/i18n/map-bms.json @@ -356,7 +356,7 @@ "logouttext": "'''Rika uwis metu log sekang sistem.'''\n\nDigatekna ya, cokan ana kaca sing esih terus nidokna nek rika esih mlebu log nganti Rika mbusak singgahan nang panjelajah web-e Rika.", "cannotlogoutnow-title": "Ora teyeng metu siki", "cannotlogoutnow-text": "Metu ora dadi angger nganggo:$1", - "welcomeuser": "Sugeng teka,$1", + "welcomeuser": "Sugeng rawuh,$1", "welcomecreation-msg": "Akune Rika uwis digawe. \nAja kelalen ngowaih [[Special:Preferences|preferensi {{SITENAME}}]] Rika.", "yourname": "Jeneng panganggo:", "userlogin-yourname": "Jenengpanganggo", diff --git a/languages/i18n/mk.json b/languages/i18n/mk.json index 6eb6d58ace..97745adef2 100644 --- a/languages/i18n/mk.json +++ b/languages/i18n/mk.json @@ -45,7 +45,7 @@ "tog-previewontop": "Прикажи го прегледот пред кутијата за уредување", "tog-previewonfirst": "Прикажи преглед на првото уредување", "tog-enotifwatchlistpages": "Испраќај ми е-пошта при промена на страница или податотека од мојот список на набљудувања", - "tog-enotifusertalkpages": "Испраќај ми е-пошта при промена на мојата страница за разговор", + "tog-enotifusertalkpages": "Испраќај ми е-пошта при промена на мојата разговорна страница", "tog-enotifminoredits": "Испраќај ми е-пошта и за ситни промени во страниците и податотеките", "tog-enotifrevealaddr": "Откриј ја мојата е-поштенска адреса во пораките за известување", "tog-shownumberswatching": "Прикажи го бројот на корисници кои набљудуваат", @@ -348,7 +348,7 @@ "title-invalid-empty": "Бараниот наслов е празен или го содржи само називот на именскиот простор.", "title-invalid-utf8": "Бараниот наслов содржи неважечка UTF-8-низа.", "title-invalid-interwiki": "Бараниот наслов содржи меѓујазична врска што не може да се користи во наслови.", - "title-invalid-talk-namespace": "Бараниот наслов се однесува на страница за разговор што не може да постои.", + "title-invalid-talk-namespace": "Бараниот наслов се однесува на разговорна страница што не може да постои.", "title-invalid-characters": "Бараниот наслов содржи неважечки знаци: „$1“.", "title-invalid-relative": "Насловот има односна патека. Односните наслови (./, ../) се неважечки бидејќи честопати се недостапни за прелистувачот.", "title-invalid-magic-tilde": "Побараниот наслов содржи низа неважечки тилди (<nowiki>~~~</nowiki>).", @@ -652,7 +652,7 @@ "accmailtext": "На $2 е спратена е случајно создадена лозинка за [[User talk:$1|$1]] е испратена. Истата може да се смени на страницата ''[[Special:ChangePassword|Менување на лозинка]]'' откако ќе се најавите.", "newarticle": "(нова)", "newarticletext": "Дојдовте на врска до страница што не постои.\nЗа да ја создадете страницата, напишете текст во полето подолу ([$1 помош]). Ако сте овде по грешка, само систнете на копчето '''назад''' во вашиот прелистувач.", - "anontalkpagetext": "----\n<em>Ова е страница за разговор со анонимен корисник кој сè уште не регистрирал корисничка сметка или не ја користи.<em>\nЗатоа мораме да ја користиме неговата бројчена IP-адреса за да го препознаеме.\nЕдна ваква IP-адреса може да ја делат повеќе корисници.\nАко сте анонимен корисник и сметате дека кон вас се упатени нерелевантни коментари, тогаш [[Special:CreateAccount|создајте корисничка сметка]] или [[Special:UserLogin|најавете се]] за да избегнете поистоветување со други анонимни корисници во иднина.''", + "anontalkpagetext": "----\n<em>Ова е разговорна страница со анонимен корисник кој сè уште не регистрирал корисничка сметка или не ја користи.<em>\nЗатоа мораме да ја користиме неговата бројчена IP-адреса за да го препознаеме.\nЕдна ваква IP-адреса може да ја делат повеќе корисници.\nАко сте анонимен корисник и сметате дека кон вас се упатени нерелевантни коментари, тогаш [[Special:CreateAccount|создајте корисничка сметка]] или [[Special:UserLogin|најавете се]] за да избегнете поистоветување со други анонимни корисници во иднина.''", "noarticletext": "Таква страница сè уште не постои.\nМожете да проверите [[Special:Search/{{PAGENAME}}|дали насловот се споменува]] во други статии,\nда ги <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={{FULLPAGENAMEE}}}} пребарате поврзаните дневници]</span>, но немате дозвола да ја создадете страницата.", "missing-revision": "Не ја пронајдов преработката бр. $1 на страницата со наслов „{{FULLPAGENAME}}“.\n\nОва обично се должи на застарена врска за разлики што води кон избришана страница.\nПовеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].", @@ -934,7 +934,7 @@ "searchprofile-advanced": "Напредно", "searchprofile-articles-tooltip": "Пребарување во $1", "searchprofile-images-tooltip": "Пребарување на податотеки", - "searchprofile-everything-tooltip": "Пребарување по сета содржина (вклучувајќи страници за разговор)", + "searchprofile-everything-tooltip": "Пребарување по сета содржина (вклучувајќи разговорни страници)", "searchprofile-advanced-tooltip": "Пребарување во именски простори по избор", "search-result-size": "$1 ({{PLURAL:$2|еден збор|$2 збора}})", "search-result-category-size": "{{PLURAL:$1|1 член|$1 членови}} ({{PLURAL:$2|1 поткатегорија|$2 поткатегории}}, {{PLURAL:$3|1 податотека|$3 податотеки}})", @@ -1025,6 +1025,7 @@ "timezoneregion-indian": "Индиски Океан", "timezoneregion-pacific": "Тихи Океан", "allowemail": "Дозволи е-пошта од други корисници", + "email-allow-new-users-label": "Дозволи е-пошта од сосем нови корисници", "email-blacklist-label": "Забрани е-пошта од следниве корисници:", "prefs-searchoptions": "Пребарување", "prefs-namespaces": "Именски простори", @@ -1045,7 +1046,7 @@ "yourvariant": "Јазична варијанта на содржината:", "prefs-help-variant": "Вашата претпочитана варијанта или правопис за приказ на содржинските страници на ова вики.", "yournick": "Потпис:", - "prefs-help-signature": "Коментарите на страниците за разговор треба да се потпишуваат со „<nowiki>~~~~</nowiki>“ кое ќе се претвори во вашиот потпис и време.", + "prefs-help-signature": "Коментарите на разговорните страници треба да се потпишуваат со „<nowiki>~~~~</nowiki>“ кое ќе се претвори во вашиот потпис и време.", "badsig": "Грешка во потписот.\nПроверете ги HTML ознаките.", "badsiglength": "Вашиот потпис е премногу долг.\nМора да биде со помалку од $1 {{PLURAL:$1|знак|знаци}}.", "yourgender": "Пол", @@ -1128,8 +1129,8 @@ "grouppage-suppress": "{{ns:project}}:Притајување", "right-read": "Читање страници", "right-edit": "Уредување страници", - "right-createpage": "Создавање на страници (кои не се страници за разговор)", - "right-createtalk": "Создавање на страници за разговор", + "right-createpage": "Создавање на страници (кои не се разговорни страници)", + "right-createtalk": "Создавање на разговорни страници", "right-createaccount": "Создавање на нови кориснички сметки", "right-autocreateaccount": "Автоматска најава со надворешна корисничка сметка", "right-minoredit": "Означување на уредувањата како ситни", @@ -1195,6 +1196,7 @@ "right-siteadmin": "Заклучување и отклучување на базата на податоци", "right-override-export-depth": "Извезување на страници вклучувајќи поврзани страници со продорност до 5", "right-sendemail": "Испраќање на е-пошта до други корисници", + "right-sendemail-new-users": "Испраќање е-пошта на корисници без заведени дејства", "right-managechangetags": "Создавање и (де)активирање на [[Special:Tags|ознаки]]", "right-applychangetags": "Задавање на [[Special:Tags|ознаки]] заедно со направените измени", "right-changetags": "Додавате и отстранување на произволни [[Special:Tags|ознаки]] во поединечни преработки и дневнички записи", @@ -1239,7 +1241,7 @@ "action-read": "читање на оваа страница", "action-edit": "уредување на оваа страница", "action-createpage": "создавање страници", - "action-createtalk": "создавање на оваа страница за разговор", + "action-createtalk": "создавање на оваа разговорна страница", "action-createaccount": "создај ја оваа корисничка сметка", "action-autocreateaccount": "автоматско создавање на оваа надворешна корисничка сметка", "action-history": "преглед на историјата на оваа страница", @@ -1296,6 +1298,7 @@ "recentchanges-noresult": "Нема промени од дадениот период што одговараат на бараното.", "recentchanges-timeout": "Ова пребарување истече. Пробајте со поинакви параметри.", "recentchanges-network": "Поради техничка грешка, не можев да го вчитам исходот. Превчитајте ја страницата.", + "recentchanges-notargetpage": "Погоре внесете страница за да ги видите промените поврзани со неа.", "recentchanges-feed-description": "Следење на најскорешните промени на викито во овој тековник.", "recentchanges-label-newpage": "Нова страница", "recentchanges-label-minor": "Ова е ситна промена", @@ -1428,11 +1431,11 @@ "rcfilters-watchlist-showupdated": "Промени во страниците што ги немате посетено откако се случиле се <strong>задебелени</strong>.", "rcfilters-preference-label": "Скриј ја подобрената верзија во Скорешните промени", "rcfilters-preference-help": "Го отповикува преуредувањето на околината од 2017 г. и сите алатки додадени оттогаш.", - "rcfilters-filter-showlinkedfrom-label": "Прикажи промени во страници кои водат од:", - "rcfilters-filter-showlinkedfrom-option-label": "Прикажи промени во страници кои водат ОД страница", - "rcfilters-filter-showlinkedto-label": "Прикажи промени во страници кои водат кон:", - "rcfilters-filter-showlinkedto-option-label": "Прикажи промени во страници кои водат КОН страница", - "rcfilters-target-page-placeholder": "Изберете страница", + "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": "Подолу {{PLURAL:$5|е прикажана промената|се прикажани промените}} почнувајќи од <strong>$3, $4</strong> (се прикажуваат до <b>$1</b>).", "rclistfromreset": "Нов избор на датуми", "rclistfrom": "Прикажи нови промени почнувајќи од $3 $2", @@ -1478,7 +1481,7 @@ "recentchangeslinked-feed": "Поврзани промени", "recentchangeslinked-toolbox": "Поврзани промени", "recentchangeslinked-title": "Промени поврзани со „$1“", - "recentchangeslinked-summary": "Ова е список на промени направени на страниците поврзани преку назначената страница (или до членови на назначената категорија).\nСтраниците на [[Special:Watchlist|вашиот список на набљудувања]] се прикажани '''задебелено'''.", + "recentchangeslinked-summary": "Внесете страница за да ги видите промените кои водат кон или од неа. (За да ги видите членовите на категорија, внесете Категорија:Име на категоријата). Промените во страниците во [[Special:Watchlist|набљудувањата]] се <strong>задебелени</strong>.", "recentchangeslinked-page": "Име на страницата:", "recentchangeslinked-to": "Прикажи ги промените на страниците поврзани со дадената страница", "recentchanges-page-added-to-category": "[[:$1]] ставена во категорија", @@ -1825,7 +1828,7 @@ "statistics-header-hooks": "Други статистики", "statistics-articles": "Статии", "statistics-pages": "Страници", - "statistics-pages-desc": "Сите страници на викито, вклучувајќи страници за разговор, пренасочувања и тн.", + "statistics-pages-desc": "Сите страници на викито, вклучувајќи разговорни страници, пренасочувања и тн.", "statistics-files": "Подигнати податотеки", "statistics-edits": "Број на уредувања од започнувањето на {{SITENAME}}", "statistics-edits-average": "Просечен број на уредувања по страница", @@ -2128,11 +2131,11 @@ "watchlistanontext": "Најавете се за да можете да го прегледувате и уредувате набљудуваните.", "watchnologin": "Не сте најавени", "addwatch": "Додај во набљудувани", - "addedwatchtext": "„[[:$1]]“ и нејзината страница за разговор се додадени во вашите [[Special:Watchlist|набљудувања]].", + "addedwatchtext": "„[[:$1]]“ и нејзината разговорна страница се додадени во вашите [[Special:Watchlist|набљудувања]].", "addedwatchtext-talk": "„[[:$1]]“ и нејзината поврзана страница се додадени во вашите [[Special:Watchlist|набљудувања]].", "addedwatchtext-short": "Страницата „$1“ е додадена во вашите набљудувања.", "removewatch": "Отстрани набљудуваните", - "removedwatchtext": "„[[:$1]]“ и нејзината страница за разговор се отстранети од вашите [[Special:Watchlist|набљудувања]].", + "removedwatchtext": "„[[:$1]]“ и нејзината разговорна страница се отстранети од вашите [[Special:Watchlist|набљудувања]].", "removedwatchtext-talk": "„[[:$1]]“ и нејзината поврзана страница се отстранети од вашите [[Special:Watchlist|набљудувања]].", "removedwatchtext-short": "Страницата „$1“ е отстранета од вашите набљудувања.", "watch": "Набљудувај", @@ -2141,7 +2144,7 @@ "unwatchthispage": "Престани набљудување", "notanarticle": "Не е статија", "notvisiblerev": "Преработката била избришана", - "watchlist-details": "Во вашите набљудувани имате {{PLURAL:$1|$1 страница|$1 страници}} (не броејќи ги страниците за разговор).", + "watchlist-details": "Во вашите набљудувани имате {{PLURAL:$1|$1 страница|$1 страници}} (не броејќи ги разговорните страници).", "wlheader-enotif": "Известувањето по е-пошта е вклучено.", "wlheader-showupdated": "Страниците што се изменети од вашата последна посета се прикажани со '''задебелени''' букви", "wlnote": "Подолу {{PLURAL:$1|е прикажана последната промена|се прикажани последните <strong>$1</strong> промени}} во {{PLURAL:$2|последниов час|последниве <strong>$2</strong> часа}}, заклучно со $3, $4 ч.", @@ -2407,8 +2410,8 @@ "ipbother": "Друг рок:", "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", "ipbhidename": "Скриј го корисничкото име во уредувањата и списоците", - "ipbwatchuser": "Набљудувај ја корисничката страница и страницата за разговор на овој корисник", - "ipb-disableusertalk": "Спречи го корисников да ја уредува неговата страница за разговор додека е блокиран", + "ipbwatchuser": "Набљудувај ја корисничката страница и разговорната страница на овој корисник", + "ipb-disableusertalk": "Спречи го корисников да ја уредува неговата разговорна страница додека е блокиран", "ipb-change-block": "Преблокирај го корисникот со овие нагодувања", "ipb-confirm": "Потврди блок", "badipaddress": "Неважечка IP-адреса", @@ -2459,7 +2462,7 @@ "noautoblockblock": "оневозможено автоблокирање", "createaccountblock": "создавањето на кориснички сметки е блокирано", "emailblock": "блокирана е-пошта", - "blocklist-nousertalk": "без можност за уредување на својата страница за разговор", + "blocklist-nousertalk": "без можност за уредување на својата разговорна страница", "ipblocklist-empty": "Списокот на блокирања е празен.", "ipblocklist-no-results": "Бараната IP-адреса или корисничка сметка не е блокирана.", "blocklink": "блокирај", @@ -2479,7 +2482,7 @@ "block-log-flags-nocreate": "оневозможено создавање кориснички сметки", "block-log-flags-noautoblock": "автоблокирање исклучено", "block-log-flags-noemail": "блокирана е-поштенска адреса", - "block-log-flags-nousertalk": "без можност за уредување на својата страница за разговор", + "block-log-flags-nousertalk": "без можност за уредување на својата разговорна страница", "block-log-flags-angry-autoblock": "овозможено проширено автоблокирање", "block-log-flags-hiddenname": "сокриено корисничко име", "range_block_disabled": "Администраторската можност да блокираат IP групи е исклучена.", @@ -2528,7 +2531,7 @@ "move-page-legend": "Премести страница", "movepagetext": "Со користењето на овој образец можете да преименувате страница, преместувајќи ја целата нејзина историја под ново име.\nСтариот наслов ќе стане пренасочувачка страница кон новиот наслов.\nАвтоматски можете да ги подновите пренасочувањата кои покажуваат кон првобитниот наслов.\nАко не изберете автоматско подновување, проверете на [[Special:DoubleRedirects|двојни]] или [[Special:BrokenRedirects|прекинати пренасочувања]].\nНа вас е одговорноста да се осигурате дека врските ќе продолжат да насочуваат таму за каде се предвидени.\n\nИмајте предвид дека страницата '''нема''' да биде преместена ако веќе постои страница со новиот наслов, освен ако е не е пренасочување и нема историја на минати уредувања. Тоа значи дека можете да ја преименувате страницата како што била претходно доколку сте направиле грешка без да ја прекриете постоечката страница.\n\n'''Напомена:'''\nОва може да биде драстична и неочекувана промена за популарна страница;\nосигурајте се дека сте ги разбрале последиците од ова пред да продолжите.", "movepagetext-noredirectfixer": "Со користењето на овој образец можете да преименувате страница, преместувајќи ја целата нејзина историја под ново име.\nСтариот наслов ќе стане пренасочувачка страница кон новиот наслов.\nАвтоматски можете да ги подновите пренасочувањата кои покажуваат кон првобитниот наслов.\nНе заборавајте да проверите [[Special:DoubleRedirects|двојни]] и [[Special:BrokenRedirects|прекинати пренасочувања]].\nНа вас е одговорноста да се осигурате дека врските ќе продолжат да насочуваат таму за каде се предвидени.\n\nИмајте предвид дека страницата '''НЕМА''' да биде преместена ако веќе постои страница со новиот наслов, освен ако е празна или ако е пренасочување и нема историја на минати уредувања. Тоа значи дека можете да ја преименувате страницата како што била претходно доколку сте направиле грешка без да ја прекриете постоечката страница.\n\n'''Напомена:'''\nОва може да биде драстична и неочекувана промена за популарна страница;\nосигурајте се дека сте ги разбрале последиците од ова пред да продолжите.", - "movepagetalktext": "Ако го штиклирате кутивчево, соодветната страница за разговор ќе биде автоматски преместена на нов наслов, освен ако таму веќе постои страница за разговор што не е празна.\n\nВо тој случај, ќе треба да ја преместите или споите страницата рачно, доколку сакате.", + "movepagetalktext": "Ако го штиклирате кутивчево, соодветната разговорна страница ќе биде автоматски преместена на нов наслов, освен ако таму веќе постои разговорна страница што не е празна.\n\nВо тој случај, ќе треба да ја преместите или споите страницата рачно, доколку сакате.", "moveuserpage-warning": "'''Предупредување:''' На пат сте да преместите корисничка страница. Имајте предвид дека само страницата ќе биде преместена, а самиот корисник ''нема'' да биде преименуван.", "movecategorypage-warning": "<strong>Предупредување:</strong> Преместувате категориска страница. Имајте предвид дека ќе се премести само страницата, а страниците во старата категорија <em>нема</em> да се прекатегоризираат во новата.", "movenologintext": "Мора да бидете регистриран корисник и да сте [[Special:UserLogin|најавени]] за да можете да преместувате страници.", @@ -2549,9 +2552,9 @@ "movepage-moved-noredirect": "Создавањето на пренасочување е оневозможено.", "articleexists": "Веќе постои страница со тоа име, или името што го одбравте е неважечко.\nИзберете друго име.", "cantmove-titleprotected": "Не може да ја преместите страницата на тоа место бидејќи саканиот наслов е заштитен од создавање.", - "movetalk": "Премести ја и страницата за разговор, ако е возможно.", + "movetalk": "Премести ја и разговорната страница, ако е возможно.", "move-subpages": "Премести ги и потстраниците (највеќе до $1)", - "move-talk-subpages": "Премести потстраници на страници за разговор (највеќе до $1)", + "move-talk-subpages": "Премести потстраници на разговорни страници (највеќе до $1)", "movepage-page-exists": "Страницата $1 веќе постои и не може автоматски да биде заменета.", "movepage-page-moved": "Страницата $1 е преместена на $2.", "movepage-page-unmoved": "Страницата $1 не може да биде преместена во $2.", @@ -2686,7 +2689,7 @@ "javascripttest-qunit-intro": "Вид. [$1 документација на испробувањето] на mediawiki.org.", "tooltip-pt-userpage": "{{GENDER:|Вашата}} корисничка страница", "tooltip-pt-anonuserpage": "Корисничка страница за IP-адресата од која уредувате", - "tooltip-pt-mytalk": "{{GENDER:|Вашата}} страница за разговор", + "tooltip-pt-mytalk": "{{GENDER:|Вашата}} разговорна страница", "tooltip-pt-anontalk": "Разговор за уредувањата од оваа IP-адреса", "tooltip-pt-preferences": "{{GENDER:|Ваши}} нагодувања", "tooltip-pt-watchlist": "Список на страници кои сте избрале да ги набљудувате.", @@ -2696,7 +2699,7 @@ "tooltip-pt-login-private": "Ќе треба да се најавите за да го користите викито", "tooltip-pt-logout": "Одјавување", "tooltip-pt-createaccount": "Ви препорачуваме да направите сметка и да се најавите, иако тоа не е задолжително", - "tooltip-ca-talk": "Разговор за страницата", + "tooltip-ca-talk": "Разговор за содржинската страница", "tooltip-ca-edit": "Уредете ја страницава", "tooltip-ca-addsection": "Започни нов пасус", "tooltip-ca-viewsource": "Оваа страница е заштитена. Можете да го видите изворниот код.", @@ -2992,7 +2995,7 @@ "exif-model": "Модел", "exif-software": "Користен програм", "exif-artist": "Автор", - "exif-copyright": "Носител на авторските права", + "exif-copyright": "Праводржец", "exif-exifversion": "Exif-верзија", "exif-flashpixversion": "Поддржана верзија на Flashpix", "exif-colorspace": "Боен простор", @@ -3123,7 +3126,7 @@ "exif-rating": "Оценка (од 5)", "exif-rightscertificate": "Уверение за раководство со права", "exif-copyrighted": "Авторскоправен статус:", - "exif-copyrightowner": "Носител на авторските права", + "exif-copyrightowner": "Праводржец", "exif-usageterms": "Услови на употреба", "exif-webstatement": "Изјава за авторското право", "exif-originaldocumentid": "Единствена назнака на изворниот документ", @@ -3613,6 +3616,8 @@ "tag-mw-replace-description": "Уредувања што отстрануваат преку 90% од содржината на една страница", "tag-mw-rollback": "Отповикување", "tag-mw-rollback-description": "Уредувања што отповикуваат претходни уредувања користејќи ја соодветната врска", + "tag-mw-undo": "Отповикај", + "tag-mw-undo-description": "Уредувања што ги отповикуваат претходните уредувања користејќи ја врската за таа намена", "tags-title": "Ознаки", "tags-intro": "На оваа страница е даден список на ознаки со кои програмската опрема може да ги означи измените и нивното значење.", "tags-tag": "Име на ознака", diff --git a/languages/i18n/ml.json b/languages/i18n/ml.json index 515f4cca29..51db50df1e 100644 --- a/languages/i18n/ml.json +++ b/languages/i18n/ml.json @@ -1226,13 +1226,15 @@ "rcfilters-activefilters": "സജീവ അരിപ്പകൾ", "rcfilters-advancedfilters": "വിപുല അരിപ്പകൾ", "rcfilters-limit-title": "പ്രദർശിപ്പിക്കേണ്ട മാറ്റങ്ങൾ", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|ഒരു മാറ്റം|$1 മാറ്റങ്ങൾ}}, $2", + "rcfilters-date-popup-title": "തിരയേണ്ട കാലം", "rcfilters-days-title": "സമീപദിവസങ്ങൾ", "rcfilters-hours-title": "സമീപമണിക്കൂറുകൾ", "rcfilters-days-show-days": "{{PLURAL:$1|ഒരു ദിവസം|$1 ദിവസം}}", "rcfilters-days-show-hours": "{{PLURAL:$1|ഒരു മണിക്കൂർ|$1 മണിക്കൂർ}}", "rcfilters-highlighted-filters-list": "പ്രമുഖമാക്കിയവ: $1", "rcfilters-quickfilters": "സേവ് ചെയ്തിട്ടുള്ള അരിപ്പകൾ", - "rcfilters-quickfilters-placeholder-title": "കണ്ണികളൊന്നും ഇതുവരെ സേവ് ചെയ്തിട്ടില്ല", + "rcfilters-quickfilters-placeholder-title": "അരിപ്പകളൊന്നും ഇതുവരെ സേവ് ചെയ്തിട്ടില്ല", "rcfilters-quickfilters-placeholder-description": "താങ്കളുടെ അരിപ്പകളുടെ സജ്ജീകരണം സേവ് ചെയ്ത് പിന്നീടുപയോഗിക്കാനായി, താഴെയുള്ള സജീവ അരിപ്പകളുടെ ഭാഗത്തെ ബുക്ക്‌മാർക്ക് ഐകോണിൽ അമർത്തുക.", "rcfilters-savedqueries-defaultlabel": "സേവ് ചെയ്തിട്ടുള്ള അരിപ്പകൾ", "rcfilters-savedqueries-rename": "പേരുമാറ്റുക", @@ -1324,7 +1326,13 @@ "rcfilters-exclude-button-on": "തിരഞ്ഞെടുത്തവ ഒഴിവാക്കുക", "rcfilters-view-tags": "ടാഗ് ചെയ്തിട്ടുള്ള തിരുത്തുകൾ", "rcfilters-view-namespaces-tooltip": "നാമമേഖല അനുസരിച്ച് ഫലങ്ങൾ അരിച്ചെടുക്കുക", + "rcfilters-view-tags-tooltip": "തിരുത്തൽ ടാഗുകൾ ഉപയോഗിച്ച് ഫലങ്ങൾ അരിച്ചെടുക്കുക", "rcfilters-liveupdates-button": "നിലവിലുള്ള പുതുക്കലുകൾ", + "rcfilters-liveupdates-button-title-on": "തത്സമയ പുതുക്കലുകൾ നിർത്തിവെക്കുക", + "rcfilters-liveupdates-button-title-off": "പുതിയ മാറ്റങ്ങൾ ഉണ്ടാകുന്ന മുറയ്ക്ക് പ്രദർശിപ്പിക്കുക", + "rcfilters-watchlist-markseen-button": "എല്ലാ മാറ്റങ്ങളും കണ്ടതായി അടയാളപ്പെടുത്തുക", + "rcfilters-watchlist-edit-watchlist-button": "താങ്കൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടിക തിരുത്തുക", + "rcfilters-watchlist-showupdated": "മാറ്റങ്ങൾ ഉണ്ടായ ശേഷം താങ്കൾ സന്ദർശിക്കാത്ത താളുകളിലെ മാറ്റങ്ങൾ, തളിക അടയാളത്തോടൊപ്പം <strong>കടുപ്പിച്ച്</strong> കാണിച്ചിരിക്കുന്നു.", "rcnotefrom": "<strong>$3, $4</strong> മുതലുള്ള {{PLURAL:$5|മാറ്റം|മാറ്റങ്ങൾ}} ആണ് താഴെയുള്ളത് (<strong>$1</strong> എണ്ണം വരെ കൊടുക്കുന്നതാണ്).", "rclistfromreset": "തീയതി എടുത്തത് പുനഃസജ്ജീകരിക്കുക", "rclistfrom": "$3 $2 മുതലുള്ള മാറ്റങ്ങൾ പ്രദർശിപ്പിക്കുക", diff --git a/languages/i18n/mr.json b/languages/i18n/mr.json index 1a28f2fd2c..32788bedf8 100644 --- a/languages/i18n/mr.json +++ b/languages/i18n/mr.json @@ -476,7 +476,7 @@ "nosuchusershort": "\"$1\" या नावाचा सदस्य नाही. लिहीताना आपली चूक तर नाही ना झाली?", "nouserspecified": "तुम्हाला सदस्यनाव नमूद करावे लागेल.", "login-userblocked": "हा सदस्य ’प्रतिबंधित’ आहे. त्यास सनोंद-प्रवेशाची परवानगी नाही.", - "wrongpassword": "आपण परवलीचा शब्द चुकीचा टाकला आहे, पुन्हा एकदा प्रयत्न करा.", + "wrongpassword": "सदस्यनाव अथवा परवलीचा शब्द चुकीचा टाकण्यात आला आहे. पुन्हा एकदा प्रयत्न करा.", "wrongpasswordempty": "परवलीचा शब्द कोरा आहे; पुन्हा प्रयत्न करा.", "passwordtooshort": "तुमच्या परवलीच्या शब्दात किमान {{PLURAL:$1|१ अक्षर |$1 अक्षरे}} हवीत.", "passwordtoolong": "परवलीचा शब्द हा {{PLURAL:$1|१ वर्ण पेक्षा|$1 वर्णांपेक्षा}} लांबीचा नको.", @@ -636,10 +636,10 @@ "anonpreviewwarning": "\"'''सावधान:''' तुम्ही विकिपीडियाचे सदस्य म्हणून सनोंद-प्रवेश (लॉग-इन) केलेला नाही. या पानाच्या संपादन इतिहासात तुमचा अंकपत्ता (आय.पी. अॅड्रेस) नोंदला जाईल.\"", "missingsummary": "'''आठवण:''' आपण संपादन सारांश पुरवलेला नाही.आपण 'जतन करा' वर पुन्हा टिचकी मारली तर, ते त्याशिवायच जतन होईल.", "selfredirect": "<strong>ईशारा:</strong>आपण या पानास, त्याच पानावर पुनर्निर्देशित करीता आहात.\nआपण पुनर्निर्देशनासाठी चूकिचे लक्ष्य नमूद केले आहे किंवा आपण चूकिच्या पानाचे संपादन करीत आहात.\nजर आपण पुन्हा \"$1\" टिचकले तर, कसेहीकरुन ते पुनर्निर्देशन तयार होईल.", - "missingcommenttext": "कृपया खाली प्रतिक्रिया भरा.", + "missingcommenttext": "कृपया प्रतिक्रिया टाका.", "missingcommentheader": "<strong>आठवण:<strong> आपण या लेखनाकरिता विषय दिलेला नाही. आपण पुन्हा \"$1\" वर टिचकले तर, तुमचे संपादन त्याशिवायच जतन होईल.", - "summary-preview": "आढाव्याची झलक:", - "subject-preview": "विषय झलक:", + "summary-preview": "संपादन सारांशाची झलक:", + "subject-preview": "विषयाची झलक:", "previewerrortext": "आपल्या बदलांची झलक बघण्याचे प्रयत्नादरम्यान त्रुटी उद्भवली.", "blockedtitle": "हा सदस्य प्रतिबंधित आहे", "blockedtext": "'''तुमचे सदस्यनाव अथवा IP पत्ता ब्लॉक केलेला आहे.'''\n\nहा ब्लॉक $1 यांनी केलेला आहे.\nयासाठी ''$2'' हे कारण दिलेले आहे.\n\n* ब्लॉकची सुरूवात: $8\n* ब्लॉकचा शेवट: $6\n* कुणाला ब्लॉक करायचे आहे: $7\n\nतुम्ही ह्या ब्लॉक संदर्भातील चर्चेसाठी $1 अथवा [[{{MediaWiki:Grouppage-sysop}}|प्रबंधकांशी]] संपर्क करू शकता.\nतुम्ही जोवर वैध ई-मेल पत्ता आपल्या [[Special:Preferences|'माझ्या पसंती']] पानावर देत नाही तोवर तुम्ही ’सदस्याला ई-मेल पाठवा’ हा दुवा वापरू शकत नाही. तसेच असे करण्यापासून आपल्याला ब्लॉक केलेले नाही.\nतुमचा सध्याचा IP पत्ता $3 हा आहे, व तुमचा ब्लॉक क्रमांक #$5 हा आहे.\nकृपया या संदर्भातील चर्चेमध्ये वरील सर्व तपशिल उद्घृत करा.", @@ -782,8 +782,8 @@ "page_first": "प्रथम", "page_last": "अंतिम", "histlegend": "फरक निवडणे: जुन्या आवृत्तींमधील फरक पाहण्यासाठी रेडियो बॉक्स मध्ये खूण करा व एन्टर कळ दाबा अथवा खाली दिलेल्या कळीवर टिचकी द्या.<br />\nविवरण: '''({{int:cur}})''' = चालू आवृत्तीशी फरक,\n(मागील) = पूर्वीच्या आवृत्तीशी फरक, छो = किरकोळ संपादन", - "history-fieldset-title": "इतिहास विंचरण करा", - "history-show-deleted": "फक्त काढून टाकलेले", + "history-fieldset-title": "आवृत्त्यांसाठी शोधा", + "history-show-deleted": "फक्त वगळलेल्या आवृत्त्या", "histfirst": "सर्वात प्राचिन", "histlast": "नविनतम", "historysize": "({{PLURAL:$1|1 बाइट|$1 बाइट्स}})", @@ -840,9 +840,9 @@ "revdelete-unsuppress": "पुर्नस्थापीत आवृत्त्यांवरील बंधने ऊठवा", "revdelete-log": "कारण:", "revdelete-submit": "निवडलेल्या {{PLURAL:$1|आवृत्तीला|आवृत्त्यांना}} लागू करा", - "revdelete-success": "'''आवृत्त्यांची दृश्यता यशस्वीपणे अद्ययावत केली.'''", + "revdelete-success": "आवृत्त्यांची दृश्यता अद्ययावत केली.", "revdelete-failure": "'''आवर्तन दृश्यता अद्ययावत करता येत नाही:'''\n$1", - "logdelete-success": "'''नोंदींची दृश्यता यशस्वी पणे स्थापिली.'''", + "logdelete-success": "नोंदींची दृश्यता स्थापिली.", "logdelete-failure": "'''नोंदींची दृश्यता स्थापिल्या गेली नाही.'''\n$1", "revdel-restore": "दृश्यता बदला", "pagehist": "पानाचा इतिहास", @@ -873,6 +873,9 @@ "mergehistory-empty": "कोणतेही आवर्तन एकत्रित करता येत नाही.", "mergehistory-done": "$1 {{PLURAL:$3|चे|ची}} $3 {{PLURAL:$3|आवर्तन|आवर्तने}} [[:$2]] मध्ये यशस्वीरीत्या एकत्रित केली.", "mergehistory-fail": "इतिहासाचे एकत्रीकरण कार्य करू शकत नाही आहे, कृपया पान आणि वेळ प्राचलांची पुनर्तपासणी करा.", + "mergehistory-fail-bad-timestamp": "वेळठसा अवैध आहे.", + "mergehistory-fail-invalid-source": "स्रोत पान अवैध आहे.", + "mergehistory-fail-invalid-dest": "लक्ष्य पान अवैध आहे.", "mergehistory-fail-toobig": "इतिहास एकत्रिकरण करणे शक्य झाले नाही कारण $1 मर्यादेपेक्षा अधिक {{PLURAL:$1|आवृत्ती|आवृत्त्या}} स्थानांतरीत केल्या जातील.", "mergehistory-no-source": "स्रोत पान $1 अस्तित्वात नाही.", "mergehistory-no-destination": "लक्ष्य पान $1 अस्तित्वात नाही.", @@ -929,9 +932,10 @@ "search-file-match": "(संचिका आशयाशी अनुरुपते)", "search-suggest": "तुम्हाला हेच म्हणायचे का: $1", "search-rewritten": "$1 साठीचे निकाल दाखवित आहे.त्याऐवजी $2 चा शोध घ्या.", - "search-interwiki-caption": "सह प्रकल्प", + "search-interwiki-caption": "सह-प्रकल्पांपासून प्राप्त निकाल", "search-interwiki-default": "$1चे निकाल:", "search-interwiki-more": "(आणखी)", + "search-interwiki-more-results": "अधिक निकाल", "search-relatedarticle": "जवळील", "searchrelated": "संबंधित", "searchall": "सर्व", @@ -1058,13 +1062,13 @@ "prefs-help-prefershttps": "हा पसंतीक्रम आपल्या पुढील सनोंद प्रवेशानंतर कार्यान्वित होईल.", "prefswarning-warning": "आपण आपल्या पसंतीक्रमात केलेला बदल अद्याप जतन झाला नाही.जर आपण \"$1\" न टिचकता, या पानावरुन दुसरीकडे गेलात तर आपला पसंतीक्रम अद्यतन होणार नाही.", "prefs-tabs-navigation-hint": "उपयुक्त सूचना:आपण कळींच्या यादीत, कळींदरम्यानच्या सुचालनास डावी व उजवी बाण-कळ वापरु शकता.", - "userrights": "सदस्य अधिकार व्यवस्थापन", - "userrights-lookup-user": "सदस्य गटांचे(ग्रूप्स) व्यवस्थापन करा.", + "userrights": "सदस्य अधिकार", + "userrights-lookup-user": "सदस्याची निवड करा", "userrights-user-editname": "सदस्य नाव टाका:", - "editusergroup": "सदस्याचे गट संपादित करा", + "editusergroup": "सदस्य गटांचे भारण करा", "editinguser": "या {{GENDER:$1|सदस्या}}चे सदस्य-अधिकारात बदल केला जात आहे<strong>[[User:$1|$1]]</strong> $2", - "userrights-editusergroup": "सदस्याचे गट संपादित करा", - "saveusergroups": "सदस्य गट जतन करा", + "userrights-editusergroup": "{{GENDER:$1|सदस्य}} गट संपादित करा", + "saveusergroups": "{{GENDER:$1|सदस्य}} गट जतन करा", "userrights-groupsmember": "याचा सभासद:", "userrights-groupsmember-auto": "याचा अव्यक्त सदस्य:", "userrights-groups-help": "तुम्ही एखाद्या सदस्याचे गट सदस्यत्व बदलू शकता:\n* निवडलेला चौकोन म्हणजे सदस्य त्या गटात आहे.\n* न निवडलेला चौकोन म्हणजे सदस्य त्या गटात नाही.\n* एक * चा अर्थ तुम्ही एकदा समावेश केल्यानंतर तो गट बदलू शकत नाही, किंवा काढल्यानंतर समावेश करू शकत नाही.", @@ -1268,7 +1272,9 @@ "rcfilters-group-results-by-page": "पानानुसार गट निकाल", "rcfilters-activefilters": "सक्रिय गाळण्या", "rcfilters-advancedfilters": "प्रगत गाळण्या", - "rcfilters-limit-title": "दाखविण्यासाठीचे बदल", + "rcfilters-limit-title": "दाखविण्यासाठीचे निकाल", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|बदल}}, $2", + "rcfilters-date-popup-title": "शोधावयाचा कालावधी", "rcfilters-days-title": "अलीकडील दिवस", "rcfilters-hours-title": "अलीकडील तास", "rcfilters-days-show-days": "$1 {{PLURAL:$1|दिवस}}", @@ -1283,29 +1289,31 @@ "rcfilters-savedqueries-apply-label": "गाळणी तयार करा", "rcfilters-savedqueries-cancel-label": "रद्द करा", "rcfilters-savedqueries-add-new-title": "सध्या असलेल्या गाळण्यांच्या मांडण्या जतन करा", + "rcfilters-savedqueries-already-saved": "या गाळण्या पूर्वीच जतन केल्या आहेत.नवीन जतन केलेली गाळणी तयार करण्यासाठी आपल्या मांडण्या बदलवा.", "rcfilters-restore-default-filters": "अविचल गाळण्या पुनर्स्थापा", "rcfilters-clear-all-filters": "सर्व गाळण्या हटवा", "rcfilters-show-new-changes": "नवीनतम बदल बघा", - "rcfilters-search-placeholder": "अलीकडील बदल गाळा (न्याहाळा किंवा टंकन सुरू करा)", + "rcfilters-search-placeholder": "बदल गाळा (गाळण्यांच्या नावासाठी मेन्यू अथवा शोध वापरा)", "rcfilters-invalid-filter": "अवैध गाळणी", "rcfilters-empty-filter": "कोणत्याच गाळण्या सक्रिय नाहीत. सर्व योगदाने दाखविण्यात येत आहेत.", "rcfilters-filterlist-title": "गाळण्या", + "rcfilters-filterlist-whatsthis": "हे कसे काम करते?", "rcfilters-filterlist-feedbacklink": "या (नवीन) गाळणी साधनांबद्दल आपले काय म्हणणे/विचार आहेत ते आम्हास सांगा", "rcfilters-highlightbutton-title": "निकालांवर झोत टाका", "rcfilters-highlightmenu-help": "या गुणधर्मासाठी झोताचा रंग निवडा", "rcfilters-filterlist-noresults": "कोणतीच गाळणी सापडली नाही", "rcfilters-filtergroup-authorship": "योगदानांचे लेखक", - "rcfilters-filter-editsbyself-label": "आपली स्वत:ची संपादने", + "rcfilters-filter-editsbyself-label": "आपले स्वतःचे बदल", "rcfilters-filter-editsbyself-description": "आपली संपादने", - "rcfilters-filter-editsbyother-label": "इतरांची संपादने", + "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-registered-description": "प्रवेशलेले संपादक.", "rcfilters-filter-user-experience-level-unregistered-label": "अ-नोंदणीकृत", "rcfilters-filter-user-experience-level-unregistered-description": "संपादक जे प्रवेशित नाहीत.", "rcfilters-filter-user-experience-level-newcomer-label": "नवागत", - "rcfilters-filter-user-experience-level-newcomer-description": "१० संपादनांपेक्षा कमी व ४ दिवसांची सक्रियता असणारे नोंदणीकृत सदस्य.", + "rcfilters-filter-user-experience-level-newcomer-description": "१० संपादनांपेक्षा कमी संपादने केलेले व ४ दिवसांची सक्रियता असणारे नोंदणीकृत सदस्य.", "rcfilters-filter-user-experience-level-learner-label": "शिकाऊ", "rcfilters-filter-user-experience-level-learner-description": "\"शिकाऊ\" व \"नोंदणीकृत संपादक\" या दरम्यानचा अनुभव असणारे संपादक", "rcfilters-filter-user-experience-level-experienced-label": "अनुभवी सदस्य", @@ -1322,10 +1330,13 @@ "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-notwatched-label": "निरीक्षणसूचीत नसलेली", "rcfilters-filter-watchlist-notwatched-description": "आपल्या निरीक्षणसूचीतील बदलांशिवाय इतर सर्वकाही.", + "rcfilters-filter-watchlistactivity-unseen-label": "न-बघितलेले बदल", + "rcfilters-filter-watchlistactivity-seen-label": "बघितलेले बदल", "rcfilters-filtergroup-changetype": "बदलाचा प्रकार", "rcfilters-filter-pageedits-label": "पृष्ठ संपादने", "rcfilters-filter-newpages-label": "नवीन पान-निर्माण", @@ -1344,6 +1355,8 @@ "rcfilters-view-namespaces-tooltip": "नामविश्वांनुसार गाळण्यांचे निकाल", "rcfilters-view-tags-tooltip": "संपादन खूण वापरुन गाळण्यांचे निकाल", "rcfilters-view-tags-help-icon-tooltip": "खूण केलेल्या संपादनांबाबत अधिक जाणून घ्या", + "rcfilters-liveupdates-button": "सजीव अद्यतने", + "rcfilters-liveupdates-button-title-on": "सजीव अद्यतने बंद करा", "rcnotefrom": "खाली {{PLURAL:$5|हा बदल आहे|हे बदल आहेत}} <strong>$3, $4</strong>पासून ते(<strong>$1</strong>पर्यंतचे बदल दाखविले आहेत).", "rclistfrom": "$2,$3 पासून सुरुवात करुन, नविन केल्या गेलेले बदल दाखवा.", "rcshowhideminor": "छोटे बदल $1", @@ -2018,6 +2031,7 @@ "changecontentmodel-title-label": "लेखपान शीर्ष", "changecontentmodel-reason-label": "कारण:", "changecontentmodel-submit": "बदला", + "changecontentmodel-success-title": "आशय नमूना बदलल्या गेला", "log-name-contentmodel": "आशय नमूना बदल नोंदी", "logentry-contentmodel-change-revertlink": "उलटवा", "logentry-contentmodel-change-revert": "उलटवा", @@ -2540,6 +2554,7 @@ "pageinfo-length": "पानाचा आकार (बाइट्समध्ये)", "pageinfo-article-id": "पृष्ठ-ओळखण", "pageinfo-language": "पान-आशय भाषा", + "pageinfo-language-change": "बदल", "pageinfo-content-model": "पान-आशय नमूना", "pageinfo-content-model-change": "बदला", "pageinfo-robot-policy": "यंत्रमानवांद्वारे अनुक्रमण", @@ -3023,6 +3038,7 @@ "confirmemail_body_set": "{{SITENAME}} वर कुणीतरी, बहुतेक आपणच, $1 या अंकपत्त्यावरून, \"$2\" या खात्याकरिताचा विपत्रपत्ता (ई-मेल), या पत्त्यास स्थापिलेला आहे.\n\nहे खाते खरोखर आपलेच आहे याची खात्री करण्यासाठी आणि {{SITENAME}} वर विपत्रपत्ता प्रारुप सक्रिय(उपलब्ध) करण्यासाठी, हा दुवा आपल्या न्याहाळकात(ब्राउजर) उघडा:\n\n$3\n\nजर हे खाते आपले *नसेल* तर, ही विपत्रपत्याचे निश्चितीकरण वगळण्यासाठी,खालील दुव्यास अनुसरा:\n\n$5\n\nहा निश्चितीकरण संकेत(कन्फर्मेशन कोड) $4 ला कालबाह्य होईल.", "confirmemail_invalidated": "इ-मेल पत्ता तपासणी रद्द करण्यात आलेली आहे", "invalidateemail": "इ-मेल तपासणी रद्द करा", + "notificationemail_subject_changed": "{{SITENAME}} वर नोंदविलेला विपत्रपत्ता बदलवल्या गेला", "scarytranscludedisabled": "[आंतरविकि आंतरन्यास अनुपलब्ध केले आहे]", "scarytranscludefailed": "[क्षमस्व;$1करिताची साचा ओढी फसली]", "scarytranscludetoolong": "[आंतरजालपत्ता खूप लांब आहे]", @@ -3221,6 +3237,7 @@ "tags-apply-blocked": "आपण प्रतिबंधित असतांना आपल्या बदलांसह, बदल खूणपताकांना लागू करु शकत नाही.", "tags-update-blocked": "आपण प्रतिबंधित असतांना बदल खूणपताकांना जोडू अथवा हटवू शकत नाही.", "tags-edit-reason": "कारण:", + "tags-edit-success": "बदल लागू केल्या गेलेत.", "tags-edit-none-selected": "जोडण्यास किंवा हटविण्यास किमान एक खूणपताका निवडा.", "comparepages": "पानांची तुलना करा", "compare-page1": "पान १", @@ -3355,7 +3372,7 @@ "api-error-emptypage": "नवीन आणि मोकळी पाने बनवायला परवानगी नाही", "api-error-publishfailed": "अंतर्गत त्रुटी:विदादात्यास, या तात्पुरत्या संचिकेच्या प्रकाशनास अपयश आले.", "api-error-stashfailed": "इन्तरिक त्रुटी : विदादाता तात्पुरत्या स्वरूपाच्या संचिका जमा करण्यात अयशस्वी", - "api-error-unknown-warning": "अज्ञात इशारा : $1", + "api-error-unknown-warning": "अज्ञात इशारा : \"$1\"", "api-error-unknownerror": "अज्ञात चूक $1", "duration-seconds": "$1 {{PLURAL:$1|सेकंदापूर्वी|सेकंदांपूर्वी}}", "duration-minutes": "$1 {{PLURAL:$1|मिनिटापूर्वी|मिनिटांपूर्वी}}", @@ -3392,7 +3409,7 @@ "pagelang-submit": "सादर करा", "right-pagelang": "पानाची भाषा बदला", "action-pagelang": "पानाची असलेली भाषा बदला", - "log-name-pagelang": "भाषा बदल नोंदवही", + "log-name-pagelang": "भाषा बदल नोंदी", "log-description-pagelang": "या पानातील भाषाबदलाच्या नोंदी आहेत.", "logentry-pagelang-pagelang": "$1 ने $3 करीता पानाची भाषा $4 वरुन $5 ला {{GENDER:$2|बदलली}}", "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (सक्षम केला)", @@ -3437,6 +3454,7 @@ "sessionprovider-nocookies": "कुकिज अक्षम असू शकतात. याची खात्री करा कि कुकिज सक्षम केल्या आहेत व पुन्हा सुरुवात करा.", "randomrootpage": "अविशिष्ट मूळ पान", "log-action-filter-contentmodel": "आशय नमूना बदलाचा प्रकार", + "log-action-filter-rights-rights": "मानवी बदल", "log-action-filter-suppress-block": "रोधामार्फत सदस्य दाबणे", "changecredentials": "अधिकारपत्रे (क्रेडेंटियल्स)बदला", "removecredentials": "अधिकारपत्रे (क्रेडेंटियल्स) हटवा" diff --git a/languages/i18n/mt.json b/languages/i18n/mt.json index 35e35ee673..5202c2e0d8 100644 --- a/languages/i18n/mt.json +++ b/languages/i18n/mt.json @@ -531,6 +531,7 @@ "minoredit": "Din hija modifika minuri", "watchthis": "Segwi din il-paÄ¡na", "savearticle": "Salva l-paÄ¡na", + "publishpage": "Ippubblika l-paÄ¡na", "publishchanges": "Ippubblika l-modifiki", "preview": "Dehra proviżorja", "showpreview": "Dehra proviżorja", diff --git a/languages/i18n/mwl.json b/languages/i18n/mwl.json index da1104344c..cbaa2e5314 100644 --- a/languages/i18n/mwl.json +++ b/languages/i18n/mwl.json @@ -8,7 +8,8 @@ "Romaine", "Urhixidur", "아라", - "MokaAkashiyaPT" + "MokaAkashiyaPT", + "Athena in Wonderland" ] }, "tog-underline": "Sublinhar lhigaçones:", @@ -151,6 +152,7 @@ "navigation": "Nabegaçon", "and": " i", "faq": "FAQ", + "actions": "Açones", "namespaces": "Domínios", "variants": "Bariadades", "navigation-heading": "Menu de nabegaçon", @@ -164,6 +166,7 @@ "searcharticle": "Bota", "history": "Stórico de la páigina", "history_short": "Stórico", + "history_small": "stórico", "printableversion": "Berson pa ampremir", "permalink": "Lhigaçon pa siempre", "print": "Ampremir", @@ -313,7 +316,9 @@ "createacct-emailoptional": "Andereço de correio eiletrónico (oupcional)", "createacct-email-ph": "Poner l sou andereço de correio eiletrónico", "createaccountmail": "Outelizar ua palabra-chabe aleatória i temporária i ambiar eilha pa l'andereço de correio eiletrónico specificado", + "createacct-reason": "Rezon", "createacct-submit": "Crie la sue cuonta", + "createacct-another-submit": "Criar cuonta", "createacct-benefit-heading": "{{SITENAME}} ye feita por pessonas cumo bós.", "createacct-benefit-body1": "{{PLURAL:$1|eidiçon|eidiçones}}", "createacct-benefit-body2": "{{PLURAL:$1|páigina|páiginas}}", @@ -324,7 +329,7 @@ "nosuchuser": "Num eisiste nanhun outelizador cul nome \"$1\".\nLs nomes de outelizador son defrentes an lhetra grande ó pequeinha.\nBei cumo screbiste, ó [[Special:CreateAccount|cria ua nuoba cuonta]].", "nosuchusershort": "Nun eisiste nanhun outelizador cul nome \"$1\".\nBei se l screbiste bien.", "nouserspecified": "Tenes que dezir un nome de outelizador.", - "wrongpassword": "La palabra chabe ye ambálida.\nPor fabor, spurmenta outra beç.", + "wrongpassword": "L nome de outelizador ó la palabra chabe son ambálidos.\nPor fabor, spurmenta outra beç.", "wrongpasswordempty": "Tenes que poner la palabra chabe.\nPor fabor, spurmenta outra beç.", "passwordtooshort": "La tue palabra-chabe debe de tener pul menos $1 {{PLURAL:$1|carátele|caráteles}}.", "password-name-match": "La tue palabra-chabe ten que ser defrente de l tou nome de outelizador.", @@ -346,10 +351,16 @@ "resetpass_header": "Demudar palabra-chabe de la cuonta", "retypenew": "Pon outra beç la nuoba palabra chabe:", "botpasswords": "Palabras-chabe de robós", + "botpasswords-label-delete": "Botar fuora", "botpasswords-label-resetpassword": "Redefenir palabra-chabe", "resetpass-submit-loggedin": "Demudar palabra-chabe", + "resetpass-submit-cancel": "Çfazer", "passwordreset": "Redefenir palabra-chabe", + "passwordreset-username": "Nome de outelizador(a):", + "passwordreset-domain": "Domínio:", + "passwordreset-email": "Morada de correio eiletrónico:", "changeemail": "Altarar ó zarredar l andereço de correio eiletrónico", + "changeemail-none": "(nanhun)", "resettokens": "Redefenir chabes", "bold_sample": "Testo a negrito", "bold_tip": "Testo a negrito", @@ -394,6 +405,7 @@ "noarticletext-nopermission": "Ne l momiento, nun hai cuntenido nesta páigina.\nPuode [[Special:Search/{{PAGENAME}}|percurar l títalo desta páigina]] noutras páiginas, ó <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} percurar registros relacionados]</span>, mas nun ten permisson pa criar esta páigina.", "userpage-userdoesnotexist-view": "La cuonta de outelizador \"$1\" nun stá registrada.", "clearyourcache": "<strong>Ousserbaçon:</strong> Açpuis grabar, terá de lhimpar la mimória ''cache'' de l sou nabegador pa ber las altaraçones.\n* <strong>Firefox / Safari:</strong> Pressione <em>Shift</em> anquanto clica an <em>Recargar</em>, ó pressione <em>Ctrl-F5</em> ó <em>Ctrl-R</em> (<em>⌘-R</em> ne l Mac)\n* <strong>Google Chrome:</strong> Pressione <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> ne l Mac)\n* <strong>Internet Explorer:</strong> Pressione <em>Ctrl</em> anquanto clica an <em>Recargar</em>, ó pressione <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Nabegar pa <em>Menu → Cunfiguraçones</em> (<em>Opera → Perfréncias</em> ne l Mac) i, an seguida, <em>Pribacidade i sigurança → Lhimpar dados de nabegaçon → Eimaiges i fexeiros an cache</em>.", + "updated": "(Atualizado)", "note": "'''Abiso:'''", "previewnote": "<strong>Lhembra-te qu'esto ye solo ua amostra.</strong>\nLas altaraçones inda nun fúrun grabadas!", "continue-editing": "Nabegar pa la ária d'eidiçon", @@ -447,6 +459,7 @@ "history-feed-description": "Stórico de eidiçones pa esta páigina nesta wiki", "history-feed-item-nocomment": "$1 a $2", "rev-delundel": "amostrar/scunder", + "revdelete-show-file-submit": "Si", "revdel-restore": "Demudar besiblidade", "revdelete-edit-reasonlist": "Eiditar rezones de botar fuora", "mergehistory": "Fundir ls stóricos de las páiginas", @@ -598,6 +611,8 @@ "group-bot": "Rubós", "group-sysop": "Admenistradores", "group-bureaucrat": "Burocratas", + "group-all": "(todos)", + "group-user-member": "{{GENDER:$1|outelizador|outelizadora}}", "group-bot-member": "{{GENDER:$1|rubó}}", "group-sysop-member": "{{GENDER:$1|admenistrador|admenistradora|admenistrador(a)}}", "group-bureaucrat-member": "{{GENDER:$1|burocrata}}", @@ -620,7 +635,9 @@ "action-createaccount": "criar esta cunta de outelizador", "action-move": "arrastrar esta páigina", "action-movefile": "arrastrar este fexeiro", + "action-upload": "cargar este fexeiro", "action-delete": "apagar esta páigina", + "action-deleterevision": "botar fuora rebisones", "action-purge": "recargar esta páigina", "nchanges": "$1 {{PLURAL:$1|altaraçon|altaraçones}}", "enhancedrc-history": "stórico", @@ -637,6 +654,10 @@ "recentchanges-legend-heading": "<strong>Lhegenda:</strong>", "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ber tamien la [[Special:NewPages|lhista de páiginas nuobas]])", "recentchanges-submit": "Amostrar", + "rcfilters-savedqueries-rename": "Altarar l nome", + "rcfilters-savedqueries-remove": "Botar fuora", + "rcfilters-savedqueries-new-name-label": "Nome", + "rcfilters-savedqueries-cancel-label": "Çfazer", "rcfilters-filtergroup-watchlist": "Páiginas begiadas", "rcfilters-watchlist-markseen-button": "Marcar todas las altaraçones cumo yá bistas", "rcnotefrom": "Ambaixo {{PLURAL:$5|stá la altaraçon feita|stan las alteraçones feitas}} zde <strong>$2</strong> (amostradas até <strong>$1</strong>).", @@ -727,7 +748,7 @@ "uploadnewversion-linktext": "Cargar ua nuoba berson deste fexeiro", "shared-repo-from": "de $1", "upload-disallowed-here": "Nun puode sustituir este fexeiro.", - "filerevert-comment": "Motibo:", + "filerevert-comment": "Rezon:", "filedelete": "Apagar $1", "filedelete-legend": "Apagar fexeiro", "filedelete-submit": "Apagar", diff --git a/languages/i18n/my.json b/languages/i18n/my.json index 9775537d0c..ff40d9d822 100644 --- a/languages/i18n/my.json +++ b/languages/i18n/my.json @@ -366,10 +366,10 @@ "logout": "ထွက်ရန်", "userlogout": "ထွက်ရန်", "notloggedin": "log in ဝင်မထားပါ", - "userlogin-noaccount": "အကောင့် မရှိဘူးလား?", + "userlogin-noaccount": "အကောင့် မရှိဘူးလား။", "userlogin-joinproject": "{{SITENAME}} ကို ချိတ်ဆက်ရန်", "createaccount": "အကောင့် ဖန်တီးရန်", - "userlogin-resetpassword-link": "စကားဝှက် မေ့နေသလား?", + "userlogin-resetpassword-link": "စကားဝှက် မေ့နေသလား။", "userlogin-helplink2": "log in အကူအညီ", "userlogin-loggedin": "သင်သည် {{GENDER:$1|$1}} အနေဖြင့် လော့အင်ဝင်ထားပြီး ဖြစ်သည်။ အခြားအသုံးပြုသူ အနေဖြင့် ဝင်ရောက်ရန် အောက်ပါပုံစံကို အသုံးပြုပါ။", "userlogin-createanother": "အခြားအကောင့် ဖန်တီးရန်", @@ -446,11 +446,11 @@ "botpasswords-bad-appid": "ဘော့အမည် \"$1\" သည် မရေရာပါ။", "botpasswords-insert-failed": "ဘော့အမည် \"$1\" ကို ထည့်သွင်းရန် မဖြစ်ပါ။ ထည့်ပြီးသားလား?", "botpasswords-created-title": "ဘော့စကားဝှက် ဖန်တီးပြီးပါပြီ", - "botpasswords-created-body": "အသုံးပြုသူ \"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက် ဖန်တီးပြီးပါပြီ။", + "botpasswords-created-body": "{{GENDER:$2|အသုံးပြုသူ}} \"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက် ဖန်တီးပြီးပါပြီ။", "botpasswords-updated-title": "ဘော့စကားဝှက် မွမ်းမံပြီးပါပြီ", - "botpasswords-updated-body": "အသုံးပြုသူ \"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက်ကို မွမ်းမံပြီးပါပြီ။", + "botpasswords-updated-body": "{{GENDER:$2|အသုံးပြုသူ}}\"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက်ကို မွမ်းမံပြီးပါပြီ။", "botpasswords-deleted-title": "ဘော့စကားဝှက် ဖျက်ပြီးပါပြီ", - "botpasswords-deleted-body": "အသုံးပြုသူ \"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက်ကို ဖျက်ပြီးပါပြီ။", + "botpasswords-deleted-body": "{{GENDER:$2|အသုံးပြုသူ}} \"$2\" ၏ ဘော့အမည် \"$1\" အတွက် ဘော့စကားဝှက်ကို ဖျက်ပြီးပါပြီ။", "resetpass_forbidden": "စကားဝှက် ပြောင်းမရနိုင်ပါ", "resetpass-no-info": "ဤစာမျက်နှာကို တိုက်ရိုက်အသုံးပြုနိုင်ရန်အတွက် Log in ဝင်ထားရပါမည်။", "resetpass-submit-loggedin": "စကားဝှက်ပြောင်းရန်", @@ -914,7 +914,7 @@ "rcfilters-group-results-by-page": "စာမျက်နှာအလိုက် ရလဒ်များ အုပ်စုဖွဲ့ရန်", "rcfilters-activefilters": "သက်ဝင်နေသာ filter များ", "rcfilters-advancedfilters": "အဆင့်မြင့် filter များ", - "rcfilters-limit-title": "ပြသမည့် ပြောင်းလဲမှုများ", + "rcfilters-limit-title": "ပြသမည့် ရလဒ်များ", "rcfilters-days-title": "မကြာသေးမီက ရက်များ", "rcfilters-hours-title": "မကြာသေးမီက နာရီများ", "rcfilters-days-show-days": "$1 {{PLURAL:$1|ရက်|ရက်}}", @@ -1048,7 +1048,7 @@ "recentchangeslinked-feed": "ဆက်စပ်သော ​အ​ပြောင်း​အ​လဲ​များ​", "recentchangeslinked-toolbox": "ဆက်စပ်သော အပြောင်းအလဲများ", "recentchangeslinked-title": "\"$1\" နှင့် ဆက်စပ်သော အပြောင်းအလဲများ", - "recentchangeslinked-summary": "ဤသည်မှာ သီးသန့်ပြထားသော စာမျက်နှာ (သို့ သီးသန့်ကဏ္ဍများ) မှ ညွှန်းထားသော စာမျက်နှာများ၏ လတ်တလော ပြောင်းလဲမှုများ၏ စာရင်းဖြစ်သည်။ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ စာမျက်နှာများကို စာလုံးမည်းဖြင့် ပြထားသည်။", + "recentchangeslinked-summary": "ဤစာမျက်နှာမှ သို့မဟုတ် ဤစာမျက်နှာသို့ ချိတ်ဆက်ထားသော စာမျက်နှာများ၏ ပြောင်းလဲမှုများကို ကြည့်ရှုနိုင်ရန် စာမျက်နှာအမည်တစ်ခုကို ထည့်သွင်းပါ။ (ကဏ္ဍတစ်ခု၏ အဖွဲ့ဝင်များကို ကြည့်ရှုရန် Category:ကဏ္ဍအမည် ကို ရိုက်ထည့်ပါ။) [[Special:Watchlist|သင့်စောင့်ကြည့်စာရင်း]]ရှိ စာမျက်နှာများ၏ ပြောင်းလဲမှုများကို <strong>စာလုံးအထူ</strong>ဖြင့် ပြထားသည်။", "recentchangeslinked-page": "စာမျက်နှာ အမည် -", "recentchangeslinked-to": "ပေးထားသော စာမျက်နှာများအစား လင့်များနှင့် ဆက်စပ်နေသာ စာမျက်နှာများ၏ အပြောင်းအလဲများကို ပြရန်", "recentchanges-page-added-to-category": "ကဏ္ဍထဲသို့ [[:$1]] ကို ပေါင်းထည့်ခဲ့သည်", @@ -1801,6 +1801,7 @@ "table_pager_empty": "မည်သည့်ရလဒ်မှ မရှိပါ", "autosumm-blank": "စာမျက်နှာကို ဗလာလုပ်လိုက်သည်", "autoredircomment": "စာမျက်နှာကို [[$1]] သို့ ပြန်ညွှန်းလိုက်သည်", + "autosumm-changed-redirect-target": "ပြန်ညွှန်းကို [[$1]] မှ [[$2]] သို့ ပြောင်းလဲခဲ့သည်", "autosumm-new": "\"$1\" အစချီသော စာလုံးတို့နှင့် စာမျက်နှာကို ဖန်တီးလိုက်သည်", "size-bytes": "$1 {{PLURAL:$1|ဘိုက်|ဘိုက်}}", "watchlistedit-normal-title": "စောင့်ကြည့်စာရင်းကို တည်းဖြတ်ရန်", @@ -1855,6 +1856,12 @@ "tag-filter": "[[Special:Tags|Tag]] သီးသန့်စစ်ထုတ်ရန် -", "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-replace": "အစားထိုးခဲ့သည်", + "tag-mw-rollback": "နောက်ပြန် ပြန်ပြင်ခြင်း", "tags-title": "အမည်တွဲ", "tags-tag": "အမည်တွဲ အမည်", "tags-active-yes": "မှန်", diff --git a/languages/i18n/nb.json b/languages/i18n/nb.json index 241b9c8344..895a91acf5 100644 --- a/languages/i18n/nb.json +++ b/languages/i18n/nb.json @@ -1049,6 +1049,7 @@ "timezoneregion-indian": "Indiahavet", "timezoneregion-pacific": "Stillehavet", "allowemail": "Tillat andre Ã¥ sende meg e-post", + "email-allow-new-users-label": "Tillat e-poster fra helt nyregistrerte brukere", "email-blacklist-label": "Forhindre disse brukerne fra Ã¥ sende meg e-post:", "prefs-searchoptions": "Søk", "prefs-namespaces": "Navnerom", @@ -1219,6 +1220,7 @@ "right-siteadmin": "LÃ¥se og lÃ¥se opp databasen", "right-override-export-depth": "Eksporter sider inkludert lenkede sider til en dypde pÃ¥ 5", "right-sendemail": "Sende e-post til andre brukere", + "right-sendemail-new-users": "Sende epost til brukere som ikke har loggførte handlinger", "right-managechangetags": "Opprette og (de)aktivere [[Special:Tags|tagger]]", "right-applychangetags": "Legge til [[Special:Tags|tagger]] sammen med ens endringer", "right-changetags": "Legge til og fjerne vilkÃ¥rlige [[Special:Tags|tagger]] pÃ¥ individuelle revisjoner og loggoppføringer", @@ -1320,6 +1322,7 @@ "recentchanges-noresult": "Ingen endringer i perioden passer med kriteriene.", "recentchanges-timeout": "Tidsfristen for søket har løpt ut. Du bør prøve andre søkeparametere.", "recentchanges-network": "PÃ¥ grunn av en teknisk feil kunne ikke resultatene lastes. Prøv Ã¥ oppdatere siden.", + "recentchanges-notargetpage": "Skriv inn et sidenavn ovenfor for Ã¥ se endringer som er relatert til den siden.", "recentchanges-feed-description": "Følg med pÃ¥ siste endringer i denne wikien med denne matingen.", "recentchanges-label-newpage": "Denne redigeringen opprettet en ny side", "recentchanges-label-minor": "Dette er en mindre endring", @@ -1452,11 +1455,11 @@ "rcfilters-watchlist-showupdated": "Endringer til sider du ikke har besøkt siden endringene ble gjort vises med <strong>fet</strong> skrift.", "rcfilters-preference-label": "Skjul den forbedrede versjonen av siste endringer", "rcfilters-preference-help": "Fjerner grensesnittendringen fra 2017 og alle verktøyene som ble lagt fra og med da.", - "rcfilters-filter-showlinkedfrom-label": "Vis endringer pÃ¥ sider som lenkes fra:", - "rcfilters-filter-showlinkedfrom-option-label": "Vis endringer pÃ¥ sider som lenkes FRA en side", - "rcfilters-filter-showlinkedto-label": "Vis endringer pÃ¥ sider som lenker til:", - "rcfilters-filter-showlinkedto-option-label": "Vis endringer pÃ¥ sider som lenker TIL en side", - "rcfilters-target-page-placeholder": "Velg en side", + "rcfilters-filter-showlinkedfrom-label": "Vis endringer pÃ¥ sider som lenkes fra", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Sider som lenkes fra</strong> den valgte siden", + "rcfilters-filter-showlinkedto-label": "Vis endringer pÃ¥ sider som lenker til", + "rcfilters-filter-showlinkedto-option-label": "<strong>Sider som lenker til</strong> den valgte siden", + "rcfilters-target-page-placeholder": "Skriv inn et sidenavn", "rcnotefrom": "Nedenfor er vist {{PLURAL:$5|endringen|endringene}} som er gjort siden <strong>$3, $4</strong> (frem til <strong>$1</strong>).", "rclistfromreset": "Nullstill datovalg", "rclistfrom": "Vis nye endringer fra og med $3 $2", @@ -1501,7 +1504,7 @@ "recentchangeslinked-feed": "Relaterte endringer", "recentchangeslinked-toolbox": "Relaterte endringer", "recentchangeslinked-title": "Endringer relatert til «$1»", - "recentchangeslinked-summary": "Dette er en liste over de siste endringene pÃ¥ sidene som eer lenket fra en angitt side (eller til medlemmer av en angitt kategori).\nSider pÃ¥ [[Special:Watchlist|overvÃ¥kningslisten din]] er i '''fet skrift'''.", + "recentchangeslinked-summary": "Skriv inn et sidenavn for Ã¥ se endringer pÃ¥ sider som lenker til eller lenkes fra den siden. (For Ã¥ se medlemmene av en kategori, skriv inn Kategori:Kategorinavn.) Endringer pÃ¥ sider som er pÃ¥ [[Special:Watchlist|overvÃ¥kningslista di]] er i <strong>fet skrift</strong>.", "recentchangeslinked-page": "Sidenavn:", "recentchangeslinked-to": "Vis endringer pÃ¥ sider som lenker til den gitte siden istedet", "recentchanges-page-added-to-category": "[[:$1]] ble lagt til i kategorien", @@ -2930,7 +2933,7 @@ "weeks": "{{PLURAL:$1|$1 uke|$1 uker}}", "months": "{{PLURAL:$1|$1 mÃ¥ned|$1 mÃ¥neder}}", "years": "{{PLURAL:$1|$1 Ã¥r}}", - "ago": "$1 siden", + "ago": "for $1 siden", "just-now": "nettopp", "hours-ago": "$1 {{PLURAL:$1|time|timer}} siden", "minutes-ago": "$1 {{PLURAL:$1|minutt|minutter}} siden", @@ -3531,6 +3534,8 @@ "tag-mw-replace-description": "Redigeringer som fjerner mer enn 90 % av innholdet pÃ¥ en side", "tag-mw-rollback": "Tilbakestilling", "tag-mw-rollback-description": "Redigeringer som tilbakestiller redigeringer med tilbakestillingsknappen", + "tag-mw-undo": "Endringsomgjøring", + "tag-mw-undo-description": "Redigeringer som fjerner tidligere redigeringer med lenka «{{int:editundo}}»", "tags-title": "Tagger", "tags-intro": "Denne siden lister opp taggene programvaren kan merke en endring med, og hva de betyr.", "tags-tag": "Taggnavn", diff --git a/languages/i18n/ne.json b/languages/i18n/ne.json index 70717dd01f..268ff300a9 100644 --- a/languages/i18n/ne.json +++ b/languages/i18n/ne.json @@ -1479,7 +1479,7 @@ "uploadnewversion-linktext": "यो फाइलको नयाँ संस्करण उर्ध्वभरण गर्नुहोस् ।", "shared-repo-from": " $1 बाट", "shared-repo": "एल साझा भण्डार", - "shared-repo-name-wikimediacommons": "विकिमीडिया कमन्स", + "shared-repo-name-wikimediacommons": "विकिमिडिया कमन्स", "upload-disallowed-here": "तपाईंले यो फाइल अधिलेखन गर्न सक्नुहुन्न ।", "filerevert": "पूर्वस्थिति $1 मा फर्काउने", "filerevert-legend": " फाइल पूर्वस्थितीमा फर्काउने", diff --git a/languages/i18n/nl.json b/languages/i18n/nl.json index e8bec16bec..75d2fefa97 100644 --- a/languages/i18n/nl.json +++ b/languages/i18n/nl.json @@ -97,17 +97,17 @@ "tog-hidepatrolled": "Gemarkeerde wijzigingen verbergen in recente wijzigingen", "tog-newpageshidepatrolled": "Gemarkeerde pagina's verbergen in de lijst met nieuwe pagina's", "tog-hidecategorization": "Categorisatie van pagina's verbergen", - "tog-extendwatchlist": "Uitgebreide volglijst gebruiken om alle wijzigingen te bekijken, en niet alleen de laatste", + "tog-extendwatchlist": "Volglijst uitbreiden om alle wijzigingen te tonen, en niet alleen de recentste", "tog-usenewrc": "Wijzigingen per pagina weergeven in recente wijzigingen en volglijst", "tog-numberheadings": "Koppen automatisch nummeren", "tog-showtoolbar": "Bewerkingswerkbalk weergeven", "tog-editondblclick": "Dubbelklikken voor bewerken", "tog-editsectiononrightclick": "Bewerken van deelpagina’s mogelijk maken met een rechtermuisklik op een tussenkop", - "tog-watchcreations": "Pagina's die ik aanmaak en bestanden die ik upload automatisch volgen", - "tog-watchdefault": "Pagina’s en bestanden die ik bewerk automatisch volgen", + "tog-watchcreations": "Pagina's die ik aanmaak en bestanden die ik upload aan mijn volglijst toevoegen", + "tog-watchdefault": "Pagina’s en bestanden die ik bewerk aan mijn volglijst toevoegen", "tog-watchmoves": "Pagina’s en bestanden die ik hernoem automatisch volgen", "tog-watchdeletion": "Pagina’s en bestanden die ik verwijder automatisch volgen", - "tog-watchuploads": "Nieuwe bestanden die ik upload toevoegen aan mijn volglijst", + "tog-watchuploads": "Nieuwe bestanden die ik upload aan mijn volglijst toevoegen", "tog-watchrollback": "Pagina's waarop ik heb teruggedraaid automatisch volgen", "tog-minordefault": "Mijn bewerkingen standaard als kleine bewerking markeren", "tog-previewontop": "Voorvertoning boven bewerkingsveld weergeven", @@ -125,8 +125,8 @@ "tog-watchlisthidebots": "Botbewerkingen op mijn volglijst verbergen", "tog-watchlisthideminor": "Kleine bewerkingen op mijn volglijst verbergen", "tog-watchlisthideliu": "Bewerkingen van aangemelde gebruikers op mijn volglijst verbergen", - "tog-watchlistreloadautomatically": "Herlaad de volglijst automatisch wanneer er een filter is veranderd (JavaScript vereist)", - "tog-watchlistunwatchlinks": "Voeg volgen/niet volgen-links toe aan regels in de volglijst (JavaScript vereist voor deze functionaliteit)", + "tog-watchlistreloadautomatically": "De volglijst automatisch herladen wanneer er een filter wordt veranderd (JavaScript vereist)", + "tog-watchlistunwatchlinks": "Volgen/niet volgen-links toevoegen aan regels in de volglijst (JavaScript vereist voor schakelfunctionaliteit)", "tog-watchlisthideanons": "Bewerkingen van anonieme gebruikers op mijn volglijst verbergen", "tog-watchlisthidepatrolled": "Gemarkeerde wijzigingen op mijn volglijst verbergen", "tog-watchlisthidecategorization": "Categorisatie van pagina's verbergen", @@ -496,21 +496,21 @@ "createacct-email-ph": "Geef uw e-mailadres op", "createacct-another-email-ph": "Geef een e-mailadres op", "createaccountmail": "Gebruik een tijdelijk willekeurig wachtwoord en stuur het naar het opgegeven e-mailadres", - "createaccountmail-help": "Kan worden gebruikt voor het aanmaken van een account voor een andere persoon zonder het wachtwoord te leren.", + "createaccountmail-help": "Kan worden gebruikt voor het aanmaken van een account voor een andere persoon zonder het wachtwoord te vernemen.", "createacct-realname": "Echte naam (optioneel)", "createacct-reason": "Reden", "createacct-reason-ph": "Waarom u een ander account aanmaakt", "createacct-reason-help": "Weergegeven bericht in het logbestand van aangemaakte gebruikers", - "createacct-submit": "Account aanmaken", + "createacct-submit": "Uw account aanmaken", "createacct-another-submit": "Account aanmaken", - "createacct-continue-submit": "Doorgaan met het maken van een account", - "createacct-another-continue-submit": "Doorgaan met het maken van een account", + "createacct-continue-submit": "Doorgaan met het aanmaken van een account", + "createacct-another-continue-submit": "Doorgaan met het aanmaken van een account", "createacct-benefit-heading": "{{SITENAME}} wordt gemaakt door mensen zoals u.", "createacct-benefit-body1": "bewerking{{PLURAL:$1||en}}", "createacct-benefit-body2": "pagina{{PLURAL:$1||'s}}", "createacct-benefit-body3": "recente bijdrager{{PLURAL:$1||s}}", "badretype": "De ingevoerde wachtwoorden verschillen van elkaar.", - "usernameinprogress": "Het aanmaken van een account met die naam is al bezig.\nEven geduld alstublieft.", + "usernameinprogress": "Het aanmaken van een account met die naam is al in behandeling.\nEven geduld alstublieft.", "userexists": "De gekozen gebruikersnaam is al in gebruik.\nKies een andere naam.", "loginerror": "Aanmeldfout", "createacct-error": "Fout tijdens aanmaken account", @@ -543,7 +543,7 @@ "eauthentsent": "Er is ter bevestiging een e-mail naar het opgegeven e-mailadres gezonden.\nVolg de aanwijzingen in de e-mail om te bevestigen dat het uw account is.\nTot die tijd wordt er geen andere e-mail naar het account gezonden.", "throttled-mailpassword": "In {{PLURAL:$1|het laatste uur|de laatste $1 uur}} is al een wachtwoordherinnering verzonden.\nOm misbruik te voorkomen wordt er slechts één wachtwoordherinnering per {{PLURAL:$1|uur|$1 uur}} verzonden.", "mailerror": "Fout bij het verzenden van e-mail: $1", - "acct_creation_throttle_hit": "Bezoekers van deze wiki hebben vanaf uw IP-adres de afgelopen $2 al {{PLURAL:$1|1 account|$1 accounts}} aangemaakt, wat het maximale toegestane aantal is voor deze periode.\nDaarom kunt u momenteel vanaf dit IP-adres geen nieuwe accounts aanmaken.", + "acct_creation_throttle_hit": "Bezoekers van deze wiki hebben vanaf uw IP-adres de afgelopen $2 al {{PLURAL:$1|een account|$1 accounts}} aangemaakt, wat het maximaal toegestane aantal is voor deze periode.\nDaarom kunt u momenteel vanaf dit IP-adres geen nieuwe accounts aanmaken.", "emailauthenticated": "Uw e-mailadres is bevestigd op $2 om $3.", "emailnotauthenticated": "Uw e-mailadres is niet bevestigd.\nDe volgende functies verzenden nog geen e-mail.", "noemailprefs": "Geef een e-mailadres op in uw voorkeuren om deze functies te gebruiken.", @@ -557,7 +557,7 @@ "createaccount-text": "Iemand heeft een account voor uw e-mailadres op {{SITENAME}} ($4) aangemaakt genaamd \"$2\", met wachtwoord \"$3\".\nMeld u aan en wijzig uw wachtwoord.\n\nU kunt dit bericht negeren als dit account zonder uw medeweten is aangemaakt.", "login-throttled": "U heeft recentelijk te veel aanmeldpogingen gedaan.\nWacht alstublieft $1 voordat u het opnieuw probeert.", "login-abort-generic": "Uw aanmelding is mislukt - Afgebroken", - "login-migrated-generic": "Uw gebruikersnaam is hernoemd, en uw gebruikersnaam bestaat niet langer op deze wiki.", + "login-migrated-generic": "Uw account is hernoemd, en uw gebruikersnaam bestaat niet langer op deze wiki.", "loginlanguagelabel": "Taal: $1", "suspicious-userlogout": "Uw verzoek om af te melden is genegeerd, omdat het lijkt alsof het verzoek is verzonden door een browser of cacheproxy die stuk is.", "createacct-another-realname-tip": "Een echte naam is optioneel.\nAls u een naam opgeeft, wordt deze gebruikt ter erkenning voor diens werk.", @@ -580,9 +580,9 @@ "changepassword-success": "Uw wachtwoord is gewijzigd!", "changepassword-throttled": "U heeft recentelijk te veel mislukte aanmeldpogingen gedaan.\nWacht alstublieft $1 voordat u het opnieuw probeert.", "botpasswords": "Botwachtwoorden", - "botpasswords-summary": "<em>Botwachtwoorden</em> zorgen voor toegang tot de API via een gebruikersaccount zonder gebruik te maken van de aanmeldgegevens van dat account. De gebruikersrechten die beschikbaar zijn kunnen afwijken indien er aangemeld is met een botwachtwoord.\n\nAls u niet weet wat de gevolgen hiervan zijn, is het handiger om dit ook dan niet te doen. Niemand hoort u te vragen om een botwachtwoord aan te maken en deze vervolgens aan hem of haar te geven.", + "botpasswords-summary": "<em>Botwachtwoorden</em> zorgen voor toegang tot een gebruikersaccount via de API, zonder gebruik te maken van de aanmeldgegevens van dat account. De beschikbare gebruikersrechten zijn mogelijk beperkt wanneer er met een botwachtwoord is aangemeld.\n\nAls u niet weet waarom u een botwachtwoord zou willen aanmaken, is het raadzaam het niet te doen. Niemand hoort u te vragen om een botwachtwoord aan te maken en dit aan hem of haar te geven.", "botpasswords-disabled": "Botwachtwoorden zijn uitgeschakeld.", - "botpasswords-no-central-id": "Om botwachtwoorden te gebruiken, moet u ingelogd zijn met een gecentraliseerd account", + "botpasswords-no-central-id": "Om botwachtwoorden te gebruiken, moet u ingelogd zijn met een gecentraliseerd account.", "botpasswords-existing": "Bestaande botwachtwoorden", "botpasswords-createnew": "Een nieuw botwachtwoord aanmaken", "botpasswords-editexisting": "Een bestaand botwachtwoord bewerken", @@ -593,7 +593,7 @@ "botpasswords-label-delete": "Verwijderen", "botpasswords-label-resetpassword": "Het wachtwoord opnieuw instellen", "botpasswords-label-grants": "Van toepassing zijnde rechten:", - "botpasswords-help-grants": "Toestemmingen geven toegang tot gebruikersrechten die u al heeft. Het geven van een toestemming op deze plek geeft u geen toegang tot gebruikersrechten die u anders niet zou hebben. Zie het [[Special:ListGrants|overzicht van toestemmingen]] voor meer informatie.", + "botpasswords-help-grants": "Toestemmingen geven toegang tot rechten die uw gebruikersaccount al heeft. Het geven van een toestemming op deze plek geeft geen toegang tot rechten die uw gebruikersaccount anders niet zou hebben. Zie het [[Special:ListGrants|overzicht van toestemmingen]] voor meer informatie.", "botpasswords-label-grants-column": "Toegewezen", "botpasswords-bad-appid": "De botnaam \"$1\" is niet geldig.", "botpasswords-insert-failed": "Toevoegen van botnaam \"$1\" mislukt. Is deze misschien al toegevoegd?", @@ -631,7 +631,7 @@ "passwordreset-domain": "Domein:", "passwordreset-email": "E-mailadres:", "passwordreset-emailtitle": "Accountgegevens op {{SITENAME}}", - "passwordreset-emailtext-ip": "Iemand (waarschijnlijk u, vanaf IP-adres $1) heeft een aanvraag gedaan om uw wachtwoord voor {{SITENAME}} ($4) opnieuw in te stellen. {{PLURAL:$3|Het volgende gebruikersaccount is|De volgende gebruikersaccounts zijn}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tijdelijke wachtwoord vervalt|Deze tijdelijke wachtwoorden vervallen}} over {{PLURAL:$5|een dag|$5 dagen}}. Meld u aan en wijzig het wachtwoord nu. Als u dit verzoek niet zelf heeft gedaan, of als u het oorspronkelijke wachtwoord nog kent en het niet wilt wijzigen, negeer dit bericht dan en blijf uw oude wachtwoord gebruiken.", + "passwordreset-emailtext-ip": "Iemand (waarschijnlijk u, vanaf IP-adres $1) heeft een aanvraag gedaan om uw wachtwoord voor {{SITENAME}} ($4) opnieuw in te stellen. {{PLURAL:$3|Het volgende gebruikersaccount is|De volgende gebruikersaccounts zijn}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tijdelijke wachtwoord vervalt|Deze tijdelijke wachtwoorden vervallen}} over {{PLURAL:$5|een dag|$5 dagen}}. Meld u aan en wijzig het wachtwoord nu. Als u dit verzoek niet zelf heeft gedaan, of als u het oorspronkelijke wachtwoord herinnert en het niet wilt wijzigen, negeer dit bericht dan en blijf uw oude wachtwoord gebruiken.", "passwordreset-emailtext-user": "Gebruiker $1 op {{SITENAME}} heeft een aanvraag gedaan om uw wachtwoord voor {{SITENAME}} ($4) opnieuw in te stellen. {{PLURAL:$3|Het volgende gebruikersaccount is|De volgende gebruikersaccounts zijn}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tijdelijke wachtwoord vervalt|Deze tijdelijke wachtwoorden vervallen}} over {{PLURAL:$5|een dag|$5 dagen}}.\nMeld u aan en wijzig het wachtwoord nu. Als u dit verzoek niet zelf heeft gedaan, of als u het oorspronkelijke wachtwoord nog kent en het niet wilt wijzigen, negeer dit bericht dan en blijf uw oude wachtwoord gebruiken.", "passwordreset-emailelement": "Gebruikersnaam: \n$1\n\nTijdelijk wachtwoord: \n$2", "passwordreset-emailsentemail": "Als dit e-mailadres aan uw account gekoppeld is, dan wordt er een e-mail verzonden om uw wachtwoord opnieuw in te stellen.", @@ -642,18 +642,18 @@ "passwordreset-invalidemail": "Ongeldig e-mailadres", "passwordreset-nodata": "Er is geen gebruikersnaam of e-mailadres opgegeven", "changeemail": "E-mailadres wijzigen of verwijderen", - "changeemail-header": "Vul dit formulier in om uw e-mailadres te wijzigen. Als u het e-mailadres wilt ontkoppelen van uw account, laat het e-mailadres dan leeg als u het formulier opslaat.", + "changeemail-header": "Vul dit formulier in om uw e-mailadres te wijzigen. Als u geen enkel e-mailadres aan uw account gekoppeld wilt hebben, laat het veld voor het nieuwe e-mailadres dan leeg.", "changeemail-no-info": "U moet aangemeld zijn om rechtstreeks toegang te hebben tot deze pagina.", "changeemail-oldemail": "Huidig e-mailadres:", "changeemail-newemail": "Nieuw e-mailadres:", - "changeemail-newemail-help": "Laat dit veld leeg als u uw e-mailadres wilt verwijderen. Na het verwijderen kunt u niet langer een vergeten wachtwoord opnieuw instellen en u ontvangt geen e-mails van deze wiki meer.", + "changeemail-newemail-help": "Laat dit veld leeg als u uw e-mailadres wilt verwijderen. Zonder gekoppeld e-mailadres kunt u een vergeten wachtwoord niet opnieuw instellen en ontvangt u geen e-mails van deze wiki meer.", "changeemail-none": "(geen)", "changeemail-password": "Uw wachtwoord voor {{SITENAME}}:", "changeemail-submit": "E-mailadres wijzigen", "changeemail-throttled": "U heeft recentelijk te veel mislukte aanmeldpogingen gedaan.\nWacht alstublieft $1 voordat u het opnieuw probeert.", "changeemail-nochange": "Geef een ander e-mailadres op.", "resettokens": "Tokens opnieuw instellen", - "resettokens-text": "U kunt tokens opnieuw instellen die toegang geven tot bepaalde persoonlijke gegevens die aan uw account zijn verbonden.\n\nU zou dit moeten doen als u ze per ongeluk gedeeld heeft met anderen of als onbevoegden toegang tot uw account hebben gehad.", + "resettokens-text": "U kunt hier tokens die toegang geven tot bepaalde aan uw account gekoppelde privégegevens opnieuw instellen.\n\nU zou dit moeten doen als u ze per ongeluk met anderen hebt gedeeld of als onbevoegden toegang tot uw account hebben gehad.", "resettokens-no-tokens": "Er zijn geen tokens om opnieuw in te stellen.", "resettokens-tokens": "Tokens:", "resettokens-token-label": "$1 (huidige waarde: $2)", @@ -690,7 +690,7 @@ "showpreview": "Bewerking ter controle bekijken", "showdiff": "Wijzigingen bekijken", "blankarticle": "<strong>Waarschuwing:</strong> de pagina die u wilt aanmaken is leeg.\nAls u opnieuw op \"$1\" klikt, wordt de pagina aangemaakt zonder enige inhoud.", - "anoneditwarning": "<strong>Waarschuwing:</strong> U bent niet aangemeld.\nUw IP-adres zal voor iedereen zichtbaar zijn als u wijzigingen op deze pagina maakt. Wanneer u <strong>[$1 zich aanmeldt]</strong> of <strong>[$2 een account aanmaakt]</strong>, verschijnen uw bewerkingen onder uw gebruikersnaam, naast andere voordelen.", + "anoneditwarning": "<strong>Waarschuwing:</strong> U bent niet aangemeld.\nUw IP-adres zal voor iedereen zichtbaar zijn als u wijzigingen op deze pagina maakt. Wanneer u <strong>[$1 zich aanmeldt]</strong> of <strong>[$2 een account aanmaakt]</strong>, worden uw bewerkingen aan uw gebruikersnaam toegeschreven, naast andere voordelen.", "anonpreviewwarning": "<em>U bent niet aangemeld. Door uw bewerking op te slaan wordt uw IP-adres in de paginageschiedenis opgenomen.</em>", "missingsummary": "'''Let op:''' u hebt geen bewerkingssamenvatting opgegeven.\nAls u nogmaals op \"$1\" klikt wordt de bewerking zonder samenvatting opgeslagen.", "selfredirect": "<strong>Waarschuwing:</strong> U heeft een doorverwijzing gemaakt naar deze pagina. Mogelijk heeft u de verkeerde bestemming voor de doorverwijzing gebruikt, of bewerkt u de verkeerde pagina. Door nogmaals op \"$1\" te klikken word de doorverwijzing alsnog aangemaakt.", @@ -720,7 +720,7 @@ "noarticletext-nopermission": "Deze pagina bevat geen tekst.\nU kunt [[Special:Search/{{PAGENAME}}|naar deze term zoeken]] in andere pagina's of\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} de logboeken doorzoeken]</span>, maar u mag de pagina niet aanmaken.", "missing-revision": "De versie #$1 van de pagina \"{{FULLPAGENAME}}\" bestaat niet.\n\nDit wordt meestal veroorzaakt door het volgen van een verouderde koppeling naar een pagina die is verwijderd.\nMeer gegevens zijn mogelijk te vinden in het [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwijderingslogboek].", "userpage-userdoesnotexist": "Gebruikersaccount \"$1\" bestaat niet.\nControleer of u deze pagina wel wilt aanmaken/bewerken.", - "userpage-userdoesnotexist-view": "Gebruikersaccount \"$1\" is niet geregistreerd.", + "userpage-userdoesnotexist-view": "Gebruikersaccount \"$1\" bestaat niet.", "blocked-notice-logextract": "Deze gebruiker is momenteel geblokkeerd.\nDe laatste regel uit het blokkeerlogboek wordt hieronder ter referentie weergegeven:", "clearyourcache": "<strong>Opmerking:</strong> nadat u de wijzigingen hebt opgeslagen is het wellicht nodig uw browsercache te legen.\n* <strong>Firefox / Safari:</strong> houd <em>Shift</em> ingedrukt terwijl u op <em>Vernieuwen</em> klikt of druk op <em>Ctrl-F5</em> of <em>Ctrl-R</em> (<em>⌘-Shift-R</em> op een Mac)\n* <strong>Google Chrome:</strong> druk op <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> op een Mac)\n* <strong>Internet Explorer:</strong> houd <em>Ctrl</em> ingedrukt terwijl u op <em>Vernieuwen</em> klikt of druk op <em>Ctrl-F5</em>\n* '''Opera:''' ga naar <em>Menu → Instellingen</em> (<em>Opera → Voorkeuren</em> op een Mac) en daarna naar <em>Privacy & beveiliging → Browsegegevens wissen... → Tijdelijk opgeslgen afbeeldingen en bestanden</em>.", "usercssyoucanpreview": "'''Tip:''' gebruik de knop \"{{int:showpreview}}\" om uw nieuwe CSS te testen alvorens op te slaan.", @@ -1049,7 +1049,7 @@ "prefs-watchlist-days-max": "Maximaal $1 {{PLURAL:$1|dag|dagen}}", "prefs-watchlist-edits": "Maximaal aantal bewerkingen in de volglijst:", "prefs-watchlist-edits-max": "Maximale aantal: 1000", - "prefs-watchlist-token": "Volglijstsleutel:", + "prefs-watchlist-token": "Volglijsttoken:", "prefs-misc": "Diversen", "prefs-resetpass": "Wachtwoord wijzigen", "prefs-changeemail": "E-mailadres wijzigen of verwijderen", @@ -1067,7 +1067,7 @@ "recentchangesdays-max": "(maximaal $1 {{PLURAL:$1|dag|dagen}})", "recentchangescount": "Standaard aantal weer te geven bewerkingen:", "prefs-help-recentchangescount": "Dit geldt voor recente wijzigingen, paginageschiedenis en logboekpagina's.", - "prefs-help-watchlist-token2": "Dit is de geheime sleutel voor de webfeed van uw volglijst.\nIedereen die het token kent, kan uw volglijst bekijken, dus deel dit token niet.\nU kunt de [[Special:ResetTokens|tokens opnieuw instellen]] als u dat wilt.", + "prefs-help-watchlist-token2": "Dit is de geheime sleutel voor de webfeed van uw volglijst.\nIedereen die het token kent, kan uw volglijst bekijken, dus deel dit token niet.\nIndien nodig kunt u [[Special:ResetTokens|tokens opnieuw instellen]].", "savedprefs": "Uw voorkeuren zijn opgeslagen.", "savedrights": "De gebruikergroepen van {{GENDER:$1|$1}} zijn opgeslagen.", "timezonelegend": "Tijdzone:", @@ -1087,6 +1087,7 @@ "timezoneregion-indian": "Indische Oceaan", "timezoneregion-pacific": "Stille Oceaan", "allowemail": "Andere gebruikers toestaan mij e-mails te sturen", + "email-allow-new-users-label": "E-mails van gloednieuwe gebruikers toestaan", "email-blacklist-label": "Voorkom dat deze gebruikers e-mails naar mij kunnen sturen:", "prefs-searchoptions": "Zoeken", "prefs-namespaces": "Naamruimten", @@ -1256,6 +1257,7 @@ "right-siteadmin": "De database blokkeren en weer vrijgeven", "right-override-export-depth": "Pagina's exporteren inclusief pagina's waarnaar verwezen wordt tot een diepte van vijf", "right-sendemail": "E-mail versturen aan andere gebruikers", + "right-sendemail-new-users": "Emails versturen naar gebruikers zonder geregistreerde handelingen", "right-managechangetags": "[[Special:Tags|Labels]] aanmaken en (de)activeren", "right-applychangetags": "[[Special:Tags|Labels]] aan bewerkingen toewijzen", "right-changetags": "Willekeurige [[Special:Tags|labels]] toevoegen aan en verwijderen van versies en logboekregels", @@ -1357,6 +1359,7 @@ "recentchanges-noresult": "Er zijn in deze periode geen wijzigingen gemaakt die aan de criteria voldoen.", "recentchanges-timeout": "Deze zoekopdracht kost te veel tijd. U kunt het opnieuw proberen met andere zoekparameters.", "recentchanges-network": "Vanwege een technische fout kunnen er geen resultaten worden geladen. U kunt proberen de pagina te verversen.", + "recentchanges-notargetpage": "Voer hierboven een paginanaam in om wijzigingen gerelateerd aan die pagina te zien.", "recentchanges-feed-description": "Met deze feed kunt u de recentste wijzigingen in deze wiki bekijken.", "recentchanges-label-newpage": "Met deze bewerking is een nieuwe pagina aangemaakt", "recentchanges-label-minor": "Dit is een kleine bewerking", @@ -1375,6 +1378,7 @@ "rcfilters-advancedfilters": "Geavanceerde filters", "rcfilters-limit-title": "Resultaten om te tonen", "rcfilters-limit-and-date-label": "{{PLURAL:$1|wijziging|$1 wijzigingen}}, $2", + "rcfilters-date-popup-title": "Tijdsperiode om te doorzoeken", "rcfilters-days-title": "Afgelopen dagen", "rcfilters-hours-title": "Afgelopen uren", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagen}}", @@ -1488,6 +1492,11 @@ "rcfilters-watchlist-showupdated": "Wijzigingen aan pagina's die u nog niet heeft bezocht worden <strong>vet</strong> en met gevulde opsommingstekens weergeven.", "rcfilters-preference-label": "Verberg de verbeterde versie van recente wijzigingen", "rcfilters-preference-help": "Zet het oude uiterlijk van de recente wijzigingen-pagina terug, inclusief alle hulpmiddelen die sindsdien zijn toegevoegd.", + "rcfilters-filter-showlinkedfrom-label": "Toon wijzigingen op pagina's gekoppeld aan", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Paginas gekoppeld aan</strong> de geselecteerde pagina", + "rcfilters-filter-showlinkedto-label": "Toon wijzigingen op pagina's gekoppeld naar", + "rcfilters-filter-showlinkedto-option-label": "<strong>Pagina's gekoppeld naar</strong> de geselecteerde pagina", + "rcfilters-target-page-placeholder": "Voer een paginanaam in", "rcnotefrom": "Wijzigingen sinds <strong>$3 om $4</strong> (maximaal <strong>$1</strong> {{PLURAL:$1|wijziging|wijzigingen}}).", "rclistfromreset": "Datum selectie opnieuw instellen", "rclistfrom": "Wijzigingen bekijken vanaf $3 $2", @@ -1532,7 +1541,7 @@ "recentchangeslinked-feed": "Verwante wijzigingen", "recentchangeslinked-toolbox": "Verwante wijzigingen", "recentchangeslinked-title": "Wijzigingen verwant aan \"$1\"", - "recentchangeslinked-summary": "Deze speciale pagina geeft de laatste bewerkingen weer op pagina's waarheen verwezen wordt vanaf een opgegeven pagina of op pagina's in een opgegeven categorie.\nPagina's die op [[Special:Watchlist|uw volglijst]] staan worden '''vet''' weergegeven.", + "recentchangeslinked-summary": "Voer een paginanaam in om bewerkingen te zien van pagina's waarheen vanaf die pagina verwezen wordt of die ernaar verwijzen. (Om leden van een categorie te zien, voert u <kbd>Categorie:''Naam van categorie''</kbd> in.) Bewerkingen van pagina's op [[Special:Watchlist|uw volglijst]] worden <strong>vet</strong> weergegeven.", "recentchangeslinked-page": "Paginanaam:", "recentchangeslinked-to": "Wijzigingen aan pagina's met koppelingen naar deze pagina bekijken", "recentchanges-page-added-to-category": "[[:$1]] aan categorie toegevoegd", @@ -1729,11 +1738,14 @@ "uploadstash-bad-path-invalid": "Pad is ongeldig.", "uploadstash-bad-path-unknown-type": "Onbekend type \"$1\".", "uploadstash-bad-path-unrecognized-thumb-name": "Miniatuurnaam onbekend.", + "uploadstash-bad-path-no-handler": "Geen handler gevonden voor mime $1 van bestand $2.", "uploadstash-bad-path-bad-format": "Sleutel \"$1\" is niet in het juiste formaat.", "uploadstash-file-not-found": "Sleutel \"$1\" niet gevonden in de opslag.", "uploadstash-file-not-found-no-thumb": "Kon geen miniatuur verkrijgen.", + "uploadstash-file-not-found-no-local-path": "Geen lokaal pad voor geschaalde afbeelding.", "uploadstash-file-not-found-no-object": "Kan geen lokaal bestandsobject voor het miniatuur aanmaken.", "uploadstash-file-not-found-no-remote-thumb": "Ophalen van het miniatuur mislukt: $1\nurl = $2", + "uploadstash-file-not-found-missing-content-type": "content-type koptekst ontbreekt.", "uploadstash-file-not-found-not-exists": "Kan het pad niet vinden, of het bestand is geen platte tekst.", "uploadstash-file-too-large": "Kan geen bestand versturen dat groter is dan $1 bytes.", "uploadstash-not-logged-in": "Er is geen gebruiker aangemeld, bestanden moeten van een gebruiker zijn.", @@ -1773,7 +1785,7 @@ "listfiles-delete": "verwijderen", "listfiles-summary": "Op deze speciale pagina zijn alle toegevoegde bestanden te bekijken.", "listfiles_search_for": "Zoeken naar bestand:", - "listfiles-userdoesnotexist": "Het gebruikersaccount \"$1\" bestaat niet.", + "listfiles-userdoesnotexist": "Gebruikersaccount \"$1\" bestaat niet.", "imgfile": "bestand", "listfiles": "Bestandslijst", "listfiles_thumb": "Miniatuur", @@ -2114,7 +2126,7 @@ "listgrouprights-removegroup": "Gebruikers uit de volgende {{PLURAL:$2|groep|groepen}} verwijderen: $1", "listgrouprights-addgroup-all": "Gebruikers aan alle groepen toevoegen", "listgrouprights-removegroup-all": "Gebruikers uit alle groepen verwijderen", - "listgrouprights-addgroup-self": "De volgende {{PLURAL:$2|groep|groepen}} toevoegen aan eigen gebruiker: $1", + "listgrouprights-addgroup-self": "De volgende {{PLURAL:$2|groep|groepen}} toevoegen aan eigen account: $1", "listgrouprights-removegroup-self": "De volgende {{PLURAL:$2|groep|groepen}} verwijderen van eigen gebruiker: $1", "listgrouprights-addgroup-self-all": "Alle groepen toevoegen aan eigen account", "listgrouprights-removegroup-self-all": "Alle groepen verwijderen van eigen account", @@ -2122,7 +2134,7 @@ "listgrouprights-namespaceprotection-namespace": "Naamruimte", "listgrouprights-namespaceprotection-restrictedto": "Recht(en) waardoor gebruiker kan bewerken", "listgrants": "Toestemmingen", - "listgrants-summary": "Hieronder staat een lijst met toestemmingen en de bijbehorende gebruikersrechten. Gebruikers kunnen toepassingen machtigen voor toegang tot hun account, maar met beperkte rechten gebaseerd op de toestemmingen die de gebruiker aan de toepassing heeft gegeven. Een toepassing die namens een gebruiker handelt, kan nooit rechten gebruiken die een gebruiker niet heeft.\nEr zijn mogelijk [[{{MediaWiki:Listgrouprights-helppage}}|aanvullende gegevens]] over individuele rechten.", + "listgrants-summary": "Hieronder staat een lijst met toestemmingen en de bijbehorende gebruikersrechten. Gebruikers kunnen toepassingen machtigen hun account te gebruiken, maar met beperkte rechten gebaseerd op de toestemmingen die de gebruiker aan de toepassing heeft gegeven. Een toepassing die namens een gebruiker handelt, kan echter geen rechten gebruiken die de gebruiker niet heeft.\nEr is mogelijk [[{{MediaWiki:Listgrouprights-helppage}}|aanvullende informatie]] over individuele rechten.", "listgrants-grant": "Toestemming", "listgrants-rights": "Rechten", "trackingcategories": "Volgcategorieën", @@ -2314,7 +2326,7 @@ "protect-text": "Hier kunt u het beveiligingsniveau voor de pagina '''$1''' bekijken en wijzigen.", "protect-locked-blocked": "U kunt het beveiligingsniveau niet wijzigen terwijl u geblokkeerd bent.\nDit zijn de huidige instellingen voor de pagina '''$1''':", "protect-locked-dblock": "Het beveiligingsniveau kan niet worden gewijzigd, omdat de database gesloten is.\nHier zijn de huidige instellingen voor de pagina '''$1''':", - "protect-locked-access": "U hebt geen rechten om het beveiligingsniveau te wijzigen.\nDit zijn de huidige instellingen voor de pagina '''$1''':", + "protect-locked-access": "Uw account heeft geen rechten om het beveiligingsniveau van pagina's te wijzigen.\nDit zijn de huidige instellingen voor de pagina <strong>$1</strong>:", "protect-cascadeon": "Deze pagina is beveiligd, omdat die in de volgende {{PLURAL:$1|pagina|pagina's}} is opgenomen, die beveiligd {{PLURAL:$1|is|zijn}} met de cascade-optie.\nWijzigingen aan beveiligingsniveau hebben geen invloed op de cascadebeveiliging.", "protect-default": "Toestaan voor alle gebruikers", "protect-fallback": "Alleen gebruikers met het recht \"$1\" toestaan", @@ -2397,14 +2409,14 @@ "mycontris": "Bijdragen", "anoncontribs": "Bijdragen", "contribsub2": "Voor {{GENDER:$3|$1}} ($2)", - "contributions-userdoesnotexist": "De account \"$1\" is niet geregistreerd.", + "contributions-userdoesnotexist": "Gebruikersaccount \"$1\" bestaat niet.", "nocontribs": "Geen wijzigingen gevonden die aan de gestelde criteria voldoen.", "uctop": "(laatste wijziging)", "month": "Van maand (en eerder):", "year": "Van jaar (en eerder):", - "sp-contributions-newbies": "Alleen de bijdragen van nieuwe gebruikers bekijken", + "sp-contributions-newbies": "Alleen bijdragen van nieuwe accounts bekijken", "sp-contributions-newbies-sub": "Voor nieuwelingen", - "sp-contributions-newbies-title": "Bijdragen van nieuwe gebruikers", + "sp-contributions-newbies-title": "Gebruikersbijdragen van nieuwe accounts", "sp-contributions-blocklog": "blokkeerlogboek", "sp-contributions-suppresslog": "onderdrukte {{GENDER:$1|gebruikersbijdragen}}", "sp-contributions-deleted": "verwijderde {{GENDER:$1|gebruiker}}sbijdragen", @@ -2447,7 +2459,7 @@ "ipaddressorusername": "IP-adres of gebruikersnaam:", "ipbexpiry": "Vervalt (maak een keuze):", "ipbreason": "Reden:", - "ipbreason-dropdown": "*Veelvoorkomende redenen voor blokkades\n** Foutieve informatie invoeren\n** Verwijderen van informatie uit pagina's\n** Spamkoppeling naar externe websites\n** Invoegen van nonsens in pagina's\n** Intimiderend gedrag\n** Misbruik door meerdere gebruikers\n** Onaanvaardbare gebruikersnaam", + "ipbreason-dropdown": "*Veelvoorkomende redenen voor blokkades\n** Foutieve informatie invoeren\n** Informatie uit pagina's verwijderen\n** Veelvuldig koppelingen naar externe websites plaatsen\n** Nonsens/gebrabbel in pagina's opnemen\n** Intimiderend gedragen/anderen lastigvallen\n** Van meerdere accounts misbruik maken\n** Een onaanvaardbare gebruikersnaam kiezen", "ipb-hardblock": "Aangemelde gebruikers de mogelijkheid ontnemen om vanaf dit IP-adres te bewerken", "ipbcreateaccount": "Registreren accounts blokkeren", "ipbemailban": "Gebruiker de mogelijkheid ontnemen om e-mail te versturen", @@ -2535,7 +2547,7 @@ "ipb_expiry_invalid": "Ongeldige duur.", "ipb_expiry_old": "Vervaldatum is in het verleden.", "ipb_expiry_temp": "Blokkades voor verborgen gebruikers moeten permanent zijn.", - "ipb_hide_invalid": "Het is niet mogelijk dit account te verbergen; het heeft meer dan {{PLURAL:$1|een bewerking|$1 bewerkingen}}.", + "ipb_hide_invalid": "Het is niet mogelijk dit account te verbergen; het heeft meer dan {{PLURAL:$1|één bewerking|$1 bewerkingen}}.", "ipb_already_blocked": "\"$1\" is al geblokkeerd", "ipb-needreblock": "$1 is al geblokkeerd.\nWilt u de instellingen wijzigen?", "ipb-otherblocks-header": "Andere {{PLURAL:$1|blokkade|blokkades}}", @@ -2943,7 +2955,7 @@ "newimages-legend": "Bestandsnaam", "newimages-label": "Bestandsnaam (of deel daarvan):", "newimages-user": "IP-adres of gebruikersnaam", - "newimages-newbies": "Alleen de bijdragen van nieuwe gebruikers bekijken", + "newimages-newbies": "Alleen bijdragen van nieuwe accounts bekijken", "newimages-showbots": "Uploads door bots weergeven", "newimages-hidepatrolled": "Gecontroleerde uploads verbergen", "newimages-mediatype": "Mediatype:", @@ -3530,11 +3542,14 @@ "tag-mw-removed-redirect-description": "Bewerkingen die een bestaande doorverwijzing veranderen in een niet-doorverwijzing", "tag-mw-changed-redirect-target": "Doorverwijzingsdoel gewijzigd", "tag-mw-changed-redirect-target-description": "Bewerkingen die het doel van een doorverwijzing veranderen", + "tag-mw-blank": "Leeghalen", "tag-mw-blank-description": "Bewerkingen die een pagina leegmaken", "tag-mw-replace": "Vervangen", "tag-mw-replace-description": "Bewerkingen die meer dan 90% van de pagina verwijderen", "tag-mw-rollback": "Terugdraaiing", "tag-mw-rollback-description": "Bewerkingen die eerdere bewerkingen terugdraaien door middel van de koppeling \"terugdraaien\"", + "tag-mw-undo": "Ongedaan maken", + "tag-mw-undo-description": "Bewerkingen die vorige bewerkingen door middel van de ongedaan maken koppeling ongedaan maken", "tags-title": "Labels", "tags-intro": "Op deze pagina staan de labels waarmee de software iedere bewerking kan markeren, en hun betekenis.", "tags-tag": "Labelnaam", @@ -3717,11 +3732,11 @@ "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|heeft}} pagina $3 naar $4 hernoemd over een doorverwijzing zonder een doorverwijzing achter te laten", "logentry-patrol-patrol": "$1 {{GENDER:$2|heeft}} versie $4 van pagina $3 gemarkeerd als gecontroleerd", "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|heeft}} versie $4 van pagina $3 automatisch gemarkeerd als gecontroleerd", - "logentry-newusers-newusers": "Gebruikersaccount $1 {{GENDER:$2|is}} aangemaakt", + "logentry-newusers-newusers": "Gebruikersaccount $1 is {{GENDER:$2|aangemaakt}}", "logentry-newusers-create": "Gebruikersaccount $1 {{GENDER:$2|is}} aangemaakt", "logentry-newusers-create2": "Gebruikersaccount $3 is {{GENDER:$2|aangemaakt}} door $1", "logentry-newusers-byemail": "Gebruikersaccount $3 is {{GENDER:$2|aangemaakt}} door $1 en het wachtwoord is per e-mail verzonden", - "logentry-newusers-autocreate": "Gebruikersaccount $1 {{GENDER:$2|is}} automatisch aangemaakt", + "logentry-newusers-autocreate": "Gebruikersaccount $1 is automatisch {{GENDER:$2|aangemaakt}}", "logentry-protect-move_prot": "$1 heeft de beveiligingsinstellingen {{GENDER:$2|verplaatst}} van $4 naar $3", "logentry-protect-unprotect": "$1 heeft de beveiliging {{GENDER:$2|opgeheven}} van $3", "logentry-protect-protect": "$1 heeft $3 {{GENDER:$2|beveiligd}} $4", @@ -3774,7 +3789,7 @@ "feedback-useragent": "Useragent:", "searchsuggest-search": "Doorzoek {{SITENAME}}", "searchsuggest-containing": "bevat...", - "api-error-badtoken": "Interne fout: het token klopt niet.", + "api-error-badtoken": "Interne fout: Foutief token.", "api-error-emptypage": "Het aanmaken van nieuwe, lege pagina's is niet toegestaan.", "api-error-publishfailed": "Interne fout: de server kon het tijdelijke bestand niet publiceren.", "api-error-stashfailed": "Interne fout: de server kon het tijdelijke bestand niet opslaan.", @@ -3993,9 +4008,9 @@ "authmanager-provider-password-domain": "Wachtwoord- en domeingebaseerde authentificatie", "authmanager-provider-temporarypassword": "Tijdelijk wachtwoord", "authprovider-confirmlink-message": "Op basis van uw recente aanmeldpogingen kunnen de volgende accounts aan uw wiki-account worden gekoppeld. Het koppelen stelt u in staat in te loggen via deze accounts. Selecteer welke accounts gekoppeld moeten worden.", - "authprovider-confirmlink-request-label": "Accounts die aan elkaar moeten worden gekoppeld.", + "authprovider-confirmlink-request-label": "Accounts die aan elkaar gekoppeld moeten worden.", "authprovider-confirmlink-success-line": "$1: Succesvol gekoppeld.", - "authprovider-confirmlink-failed": "Account koppelen is niet volledig gelukt: $1", + "authprovider-confirmlink-failed": "Het koppelen van accounts is niet volledig gelukt: $1", "authprovider-confirmlink-ok-help": "Doorgaan na het weergeven van de storingsmeldingen over het koppelen.", "authprovider-resetpass-skip-label": "Overslaan", "authprovider-resetpass-skip-help": "Sla het resetten van het wachtwoord over.", @@ -4008,10 +4023,10 @@ "specialpage-securitylevel-not-allowed": "Sorry, het is u niet toegestaan gebruik te maken van deze pagina omdat uw identiteit niet kon worden geverifieerd.", "authpage-cannot-login": "Niet in staat om aan te melden.", "authpage-cannot-login-continue": "Niet in staat om in te loggen. Uw sessie is waarschijnlijk verlopen.", - "authpage-cannot-create": "Kon het account aanmaken niet starten.", - "authpage-cannot-create-continue": "Niet in staat het account aan te maken. Uw sessie is waarschijnlijk verlopen.", - "authpage-cannot-link": "Niet in staat om het account te koppelen.", - "authpage-cannot-link-continue": "Niet in staat het account te koppelen. Uw sessie is waarschijnlijk verlopen.", + "authpage-cannot-create": "Niet in staat het aanmaken van het account te starten.", + "authpage-cannot-create-continue": "Niet in staat het aanmaken van het account voort te zetten. Uw sessie is waarschijnlijk verlopen.", + "authpage-cannot-link": "Niet in staat het koppelen van de accounts te starten.", + "authpage-cannot-link-continue": "Niet in staat het koppelen van de accounts voort te zetten. Uw sessie is waarschijnlijk verlopen.", "cannotauth-not-allowed-title": "Geen toegang", "cannotauth-not-allowed": "U hebt geen toestemming om deze pagina te gebruiken", "changecredentials": "Authenticatiegegevens wijzigen", @@ -4023,7 +4038,7 @@ "removecredentials-invalidsubpage": "$1 is geen geldig identificatietype.", "removecredentials-success": "Uw authenticatiegegevens zijn verwijderd.", "credentialsform-provider": "Soort authenticatiegegevens:", - "credentialsform-account": "Gebruikersnaam:", + "credentialsform-account": "Accountnaam:", "cannotlink-no-provider-title": "Er zijn geen accounts om te koppelen", "cannotlink-no-provider": "Er zijn geen accounts om te koppelen.", "linkaccounts": "Accounts koppelen", diff --git a/languages/i18n/nn.json b/languages/i18n/nn.json index 4dd7fcb406..225857c903 100644 --- a/languages/i18n/nn.json +++ b/languages/i18n/nn.json @@ -1087,8 +1087,9 @@ "action-upload_by_url": "laste påå denne fila frÃ¥ ein URL", "action-writeapi": "bruke skrive-API", "action-delete": "slette denne sida", - "action-deleterevision": "slette denne endringa", + "action-deleterevision": "slette versjonar", "action-deletedhistory": "sjÃ¥ slettehistorikken til sida", + "action-deletedtext": "sjÃ¥ teksten til sletta versjonar", "action-browsearchive": "søke i sletta sider", "action-undelete": "attopprette denne sida", "action-suppressrevision": "sjÃ¥ og attopprette denne skjulte endringa", @@ -1131,6 +1132,8 @@ "rcfilters-activefilters": "Aktive filter", "rcfilters-advancedfilters": "Avanserte filter", "rcfilters-limit-title": "Tal endringar som skal visast", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|éi endring|$1 endringar}}, $2", + "rcfilters-date-popup-title": "Søk i tidsperioden", "rcfilters-days-title": "Dei siste dagane", "rcfilters-hours-title": "Dei siste timane", "rcfilters-days-show-days": "{{PLURAL:$1|Éin dag|$1 dagar}}", @@ -1145,7 +1148,7 @@ "rcfilters-savedqueries-add-new-title": "Lagra gjeldande filterinnstillingar", "rcfilters-clear-all-filters": "Fjern alle filter", "rcfilters-show-new-changes": "SjÃ¥ dei nyaste endringane", - "rcfilters-search-placeholder": "Filtrer siste endringar (gÃ¥ gjennom liste eller skriv filternamn)", + "rcfilters-search-placeholder": "Filtrer endringar (nytt meny eller søk etter filternamn)", "rcfilters-invalid-filter": "Ugyldig filter", "rcfilters-empty-filter": "Ingen aktive filter. Alle bidrag er viste.", "rcfilters-filterlist-title": "Filter", @@ -1632,6 +1635,7 @@ "listusers": "Brukarliste", "listusers-editsonly": "Vis berre brukarar med endringar", "listusers-creationsort": "Sorter etter opprettingsdato", + "listusers-desc": "Sorter i minkande rekkjefylgd", "usereditcount": "{{PLURAL:$1|éi endring|$1 endringar}}", "usercreated": "{{GENDER:$3|Oppretta}} den $1 $2", "newpages": "Nye sider", @@ -2877,8 +2881,10 @@ "table_pager_limit_submit": "GÃ¥", "table_pager_empty": "Ingen resultat", "autosumm-blank": "Tømde sida", - "autosumm-replace": "Erstattar innhaldet pÃ¥ sida med «$1»", + "autosumm-replace": "Erstatta innhaldet pÃ¥ sida med «$1»", "autoredircomment": "Omdirigerer til [[$1]]", + "autosumm-removed-redirect": "Fjerna omdirigering til [[$1]]", + "autosumm-changed-redirect-target": "Endra omdirigeringsmÃ¥l frÃ¥ [[$1]] til [[$2]]", "autosumm-new": "Oppretta sida med «$1»", "autosumm-newblank": "Oppretta tom side", "lag-warn-normal": "Endringar som er nyare enn {{PLURAL:$1|sekund|sekund}} er ikkje viste pÃ¥ denne lista.", @@ -2989,6 +2995,15 @@ "tag-filter": "[[Special:Tags|Merke]]filter:", "tag-filter-submit": "Filtrer", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Merke}}]]: $2)", + "tag-mw-contentmodelchange": "endring av innhaldsmodell", + "tag-mw-new-redirect": "Ny omdirigering", + "tag-mw-removed-redirect": "Fjerna omdirigering", + "tag-mw-changed-redirect-target": "OmdirigeringsmÃ¥l endra", + "tag-mw-changed-redirect-target-description": "Endringar som endrar mÃ¥let til ei omdirigering", + "tag-mw-blank-description": "Endringar som tømmer ei side", + "tag-mw-replace": "Bytte ut", + "tag-mw-replace-description": "Endringar som fjernar meir enn 90 % av innhaldet pÃ¥ ei side", + "tag-mw-rollback": "Attenderulling", "tags-title": "Merke", "tags-intro": "Denne sida listar opp merka som programvara kan merkja ei endring med, og kva desse tyder.", "tags-tag": "Merkenamn", @@ -3027,7 +3042,10 @@ "compare-invalid-title": "Tittelen du oppgav er ugild.", "compare-title-not-exists": "Tittelen du oppgav finst ikkje.", "compare-revision-not-exists": "Versjonen du oppgav finst ikkje.", - "diff-form": "eit '''skjema'''", + "diff-form": "Skilnader", + "permanentlink": "Fast lenkje", + "permanentlink-revid": "Versjons-ID", + "permanentlink-submit": "GÃ¥ til versjon", "dberr-problems": "Nettstaden har tekniske problem.", "dberr-again": "Venta nokre minutt og last sida inn pÃ¥ nytt.", "dberr-info": "(Kan ikkje kontakta databasetenaren: $1)", @@ -3051,6 +3069,7 @@ "logentry-delete-delete": "$1 {{GENDER:$2|sletta}} sida $3", "logentry-delete-delete_redir": "$1 {{GENDER:$2|sletta}} omdirigeringa $3 gjennom overskriving", "logentry-delete-restore": "$1 {{GENDER:$2|attoppretta}} sida $3 ($4)", + "restore-count-revisions": "{{PLURAL:$1|éin versjon|$1 versjonar}}", "logentry-delete-event": "$1 {{GENDER:$2|endra}} synlegdomen av {{PLURAL:$5|éi loggoppføring|$5 loggoppføringar}} pÃ¥ $3: $4", "logentry-delete-revision": "$1 {{GENDER:$2|endra}} synlegdomen til {{PLURAL:$5|éin versjon|$5 versjonar}} pÃ¥ sida $3: $4", "logentry-delete-event-legacy": "$1 {{GENDER:$2|endra}} synlegdomen til loggoppføringar pÃ¥ $3", @@ -3195,8 +3214,15 @@ "date-range-to": "Til dato:", "randomrootpage": "Tilfeldig rotside", "log-action-filter-rights": "Type endring av rettar:", + "log-action-filter-delete-delete_redir": "Overskriving av omdirigering", + "log-action-filter-delete-restore": "Attoppretting av side", + "log-action-filter-delete-revision": "Versjonssletting", + "log-action-filter-move-move": "Flytting utan overskriving av omdirigeringar", + "log-action-filter-move-move_redir": "Flytting med overskriving av omdirigeringar", + "log-action-filter-suppress-revision": "Versjonsundertrykking", "authmanager-userdoesnotexist": "Brukarkontoen «$1» er ikkje oppretta.", "authmanager-provider-temporarypassword": "Mellombels passord", "userjsispublic": "Merk: JavaScript-undersider bør ikkje innehalda konfidensielle data sidan dei er synlege for andre brukarar.", - "usercssispublic": "Merk: CSS-undersider bør ikkje innehalda konfidensielle data sidan dei er synlege for andre brukarar." + "usercssispublic": "Merk: CSS-undersider bør ikkje innehalda konfidensielle data sidan dei er synlege for andre brukarar.", + "revid": "versjon $1" } diff --git a/languages/i18n/nys.json b/languages/i18n/nys.json index cc4b85a4aa..a575d203af 100644 --- a/languages/i18n/nys.json +++ b/languages/i18n/nys.json @@ -4,6 +4,7 @@ "Gnangarra" ] }, + "underline-never": "Warra", "sunday": "Sunday", "monday": "Monday", "tuesday": "Tuesday", @@ -54,6 +55,9 @@ "oct": "Oct", "nov": "Nov", "dec": "Dec", + "january-date": "January $1", + "february-date": "February $1", + "march-date": "March $1", "pagecategories": "{{PLURAL:$1|Warrangan}}", "category_header": "Warrangan bibol $1", "subcategories": "Warrangang", @@ -64,10 +68,12 @@ "category-article-count": "{{PLURAL:$2|Nidja warrangan ken-y ngawaliny bibol.|Ngawaliny {{PLURAL:$1|bibol|$1 bibol-ung}} il nidja warrangan, ung $2 bandang.}}", "category-file-count": "{{PLURAL:$2|Nidja warrangan kore ken-y file.|Ngawaliny {{PLURAL:$1|file be|$1 files be}} bura nidja warrangan, winnir barnder-yuck $2.}}", "listingcontinuesabbrev": "koorliny", + "noindex-category": "Uart gwabbanijow bibol", "broken-file-category": "Bibol nidja bunga-nginnaga file beda", "about": "Nartj nitja", "newwindow": "(yalgaranan yyinang window)", "cancel": "nanap", + "mypage": "Bibol", "mytalk": "Waarnk", "navigation": "waardiny biirt", "and": " wer", @@ -82,7 +88,7 @@ "searcharticle": "Koorl", "history": "Bibol kooralang", "history_short": "Kura", - "printableversion": "willgi", + "printableversion": "willgi-iny dum-bart", "permalink": "Kalyakoorl beda", "view": "Genuny", "view-foreign": "djinang il $1", @@ -96,6 +102,7 @@ "talk": "waangelanginy", "views": "genuny", "toolbox": "kootoo burrongain birniny", + "tool-link-emailuser": "Email nidja {{GENDER:$1|niall}}", "otherlanguages": "wam wanga", "redirectedfrom": "(Dtallangiritch $1)", "redirectpagesub": "Dtallangiritch bibol", @@ -144,10 +151,13 @@ "nstab-category": "Warrangan", "mainpage-nstab": "Keny mia", "nosuchspecialpage": "Yuwart birdarra bibol", - "badtitle": "Yuwart kwel-le", + "nospecialpagetext": "<strong>Noonook burung waangkininy warra brid-iya bibol.</strong>\n\nGwabbanijow-ung kwop brid-iya bibol quadgabe [[Special:SpecialPages|{{int:specialpages}}]].", + "badtitle": "Yuwart katta wir-iny", + "badtitletext": "Gurdu-bakkin-yugow bibol katta wir-iny warra, bal-yure, ka beda-warra inter-language ka interwiki katta wir-iny.\nBal kuttuck ken-y ka boolah characters ally-uart nidja bura katta wir-iny.", "viewsource": "djinang birnup", "viewsource-title": "Djinang birnup $1", "viewsourcetext": "Noonook djinang wer ngaran uran uran nidja bibol", + "yourname": "Niall kwel-le:", "userlogin-yourname": "Ngun-ya Kwel-le", "userlogin-yourname-ph": "djarbiny niall-kwel-le", "userlogin-yourpassword": "Ban-a-warryn", @@ -155,6 +165,7 @@ "createacct-yourpassword-ph": "Djarbiny ban-a-warryn", "createacct-yourpasswordagain": "Kaya ban-a-warryn", "createacct-yourpasswordagain-ph": "Djarbiny ban-a-warryn nitj", + "userlogin-remembermypassword": "Burung yalguer nguny yaarlkoorl", "login": "Yaarlkoorl", "userlogin-noaccount": "Uart wer gaduk", "userlogin-joinproject": "Mitchee-yuop {{SITENAME}}", @@ -174,11 +185,12 @@ "pt-createaccount": "Walbirniny gaduk", "pt-userlogout": "woort koorl", "passwordreset": "Wallakiny ban-a-warryn", + "passwordreset-username": "Niall kwel-le", "bold_sample": "Moorn text", "bold_tip": "Moorn text", "italic_sample": "Italic text", "italic_tip": "Italic text", - "link_sample": "Beda kwel-le", + "link_sample": "Beda katta wir-iny", "link_tip": "Bura beda", "extlink_sample": "http://www.example.com beda kat-ta-biddi", "extlink_tip": "Ban-dak beda (katta-rah http:// prefix)", @@ -193,26 +205,37 @@ "summary": "Koorada waangkininy:", "minoredit": "neyp-nopb wallak", "watchthis": "djinang nidja bibol", - "savearticle": "Burung bibol", + "savearticle": "Burung dordak-anan bibol", "preview": "Nun-gow", "showpreview": "Yong-a gwytch-ang-at", "showdiff": "Yong-a wallak", "anoneditwarning": "<strong>Warning:</strong> You are not logged in. Noonook IP-karl-up will be publicly djinang il noonook wallak. Noonook-il <strong>[$1 log in]</strong> or <strong>[$2 create an gudak]</strong>, noonook wallak will be attributed to noonook niall-kwel-le, along with other benefits.", + "blockedtitle": "Niall be nap-nap", + "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"email this user\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.", "loginreqlink": "yaarlkoorl", + "newarticletext": "Noonook ngwaliny beda bibol uart-yogow yeye.\nWallak bibol qadgin mar waangkin ngardal (djinang [$1 mar yira bibol] ngatta katitjiny)\nWarra bainya noonook nidja, click noonook bowser's <strong>woort koorl</strong>button", "anontalkpagetext": "----\n<em>Nidja waangkininy bibol for an anonymous niall uart-quadga gudak, or who does not use it.</em>\nWe therefore have to use the numerical IP-karl-up to identify him/her.\nSuch an IP-karl-up can be shared by several niall.\nIf noonook anonymous niall and feel that irrelevant waangkin have been directed at noonook, please [[Special:CreateAccount|quadga gudak]] or [[Special:UserLogin|log in]] to avoid future confusion with other anonymous niall.", "noarticletext": "There is currently no text in this page.\nYou can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} create this page]</span>.", + "noarticletext-nopermission": "Nidja yeye uart text il nidja bibol.\nNoonook [[Special:Search/{{PAGENAME}}|genuniny-ung nidja bibol katta wir-iny]] bura wam bibol, ka <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} genuniny boonadairn]</span>, noonook uart kaya ijow walbirniny nidja bibol.", "userpage-userdoesnotexist-view": "Niall gaduk $1 be uart yeye-quadga", + "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>.", + "previewnote": "<strong>Katich nidja be djinang-kura\nNoonook wallak uart burung yeye burunginy", "continue-editing": "Koorl wallakiny-up", "editing": "Wallak $1", "creating": "Wallakiny $1", "editingsection": "wallakiny $1 (quanyartign)", "templatesused": "{{PLURAL:$1|Birnup}} il nidja bibol:", + "templatesusedpreview": "{{PLURAL:$1|Birnup}} nidja kura djinang:", "template-protected": "(nap-nap)", "template-semiprotected": "(bun-ga nap-nap)", "hiddencategories": "Nidja bibol bunga il {{PLURAL:$1|Kopin Warrangan}}:", + "permissionserrors": "Kaya warra", + "permissionserrorstext-withaction": "Noonook uart kaya ijow $2 ngawaliny-ung {{PLURAL:$1|niat-juk}}", + "recreate-moveddeleted-warn": "<strong>Warning: You are recreating a page that was previously deleted.</strong>\n\nYou should consider whether it is appropriate to continue editing this page.\nThe deletion and move log for this page are provided here for convenience:", + "moveddeleted-notice": "Nidja bibol barranginy\nBarranginy, protection and ngatti boonadairn ngardal", "content-model-wikitext": "wikitext", "undo-failure": "uart nidja wallak bukadjee bura-didara wallak", - "viewpagelogs": "Djinang boorna-ang nidja bibol", + "viewpagelogs": "Djinang boonadairn nidja bibol", "currentrev-asof": "Yuttock wallak nun o-luk $1", "revisionasof": "Revision as of $1", "revision-info": "Yumbarng nune o-luk $1 borduck {{GENDER:$6|$2}}$7", @@ -221,6 +244,7 @@ "currentrevisionlink": "Yuttock wallak", "cur": "yeye", "last": "kura", + "histlegend": "Diff selection: Mark the radio boxes of the revisions to compare and hit enter or the button at the bottom.<br />\nLegend: <strong>({{int:cur}})</strong> = difference with latest revision, <strong>({{int:last}})</strong> = difference with preceding revision, <strong>{{int:minoreditletter}}</strong> = minor edit.", "history-fieldset-title": "Djinanginy wer revisions", "histfirst": "kura", "histlast": "yeye", @@ -228,11 +252,15 @@ "history-feed-description": "Djinang kooralan wer nidja bibol il wiki", "history-feed-item-nocomment": "$1 ally-war $2", "rev-delundel": "wallak djinanyiny", + "mergelog": "Mitchee-youp boonadairn", "history-title": "Djinang kooralang $1", "difference-title": "kura nidja wallak nidja ken-y il \"$1\"", "lineno": "biritt $1", + "compareselectedversions": "Nidja ka nidja kura wallak", "editundo": "yowal", + "diff-empty": "(Uart ngatti-ngatti)", "diff-multi-sameuser": "({{PLURAL:$1|Ken-y bura-didara ngatti-ngatti|$1 bura-didara ngatti-ngatti}} niall-ang kopin)", + "diff-multi-otherusers": "({{PLURAL:$1|Ken- bura-didara ngatti-ngatti|$1 bura-didara ngatti-ngatti}} borduck {{PLURAL:$2|ken-y niall|$2 niall}} uart yong-a)", "searchresults": "genunniny natj", "searchresults-title": "genunniny natj $1", "prevn": "Gwytch-ang-at {{PLURAL:$1|$1}}", @@ -241,6 +269,8 @@ "nextn-title": "Warnign $1 {{PLURAL:$1|nam-nam}}", "shown-title": "Yong-a $1 {{PLURAL:$1|nam-nam}} bibol", "viewprevnext": "Genuny ($1 {{int:pipe-separator}} $2) ($3)", + "searchmenu-exists": "<strong>Bokojee bibol kwel-le \"[[:$1]]\" bura nidja wiki</strong>{{PLURAL:$2|djinang warrba nam-nam}}", + "searchmenu-new": "<strong>Walbirniny bibol \"[[:$1]]\" il nidja wiki!</strong> {{PLURAL:$2|0=|Djinang-ung nam-nam quadgabe mokininy.}}", "searchprofile-articles": "Katitjiny bibol", "searchprofile-images": "yual wonga", "searchprofile-everything": "Bulyaru", @@ -250,29 +280,44 @@ "searchprofile-everything-tooltip": "Djinang yennar katitjiny (wer waarnk bibol)", "searchprofile-advanced-tooltip": "Djinang uart bura yennar kwel-le-up", "search-result-size": "$1 ({{PLURAL:$2|1 word|$2 words}})", + "search-result-category-size": "{{PLURAL:$1|bunga il}} ({{PLURAL:$2|Kopin Warrangan}}, {{PLURAL:$3|1 file|$3 files}})", "search-redirect": "(Dtallangiritchang $1)", "search-section": "(quanyartign $1)", + "search-file-match": "(mokin file waangkiny)", "search-suggest": "Quadga bornign noonook:$1", "searchall": "bandang", "search-showingresults": "{{PLURAL:$4|Yuul-man-waangk-in <strong>$1</strong> ung <strong>$3</strong>|Yuul-man-waangk-in <strong>$1 – $2</strong> ung <strong>$3</strong>}}", "search-nonefound": "uart katitjiny", "mypreferences": "Nidj-wer-nidja", + "prefs-user-pages": "Niall bibol", + "group-user": "Niall", "group-bot": "Balyat", + "group-sysop": "Brid-iya", + "group-user-member": "{{GENDER:$1|niall}}", + "grouppage-user": "{{ns:project}}:Niall", "grouppage-bot": "{{ns:project}}:Balyat", + "grouppage-sysop": "{{ns:project}}:Brid-iya", "right-writeapi": "Ngarlark il mar-waangkininy API", - "newuserlogpage": "Niall quadga boorna waangkiny", - "rightslog": "Niall gaduk boorna waagnkiny", + "newuserlogpage": "Niall quadga boonadairn", + "rightslog": "Niall gaduk boonadairn", "action-edit": "dookeniny bibol", "action-createaccount": "quadga nidja niall gaduk", + "action-move-rootuserpages": "ngatta birnup niall bibol", + "action-block": "nap-nap nidja niall-ang wallak-iny", "enhancedrc-history": "kura", "recentchanges": "Yeye wallak", "recentchanges-legend": "Yeye wallak ngatti-ngatti", "recentchanges-summary": "Yeye wallak balgang nidja bibol il wiki", + "recentchanges-noresult": "Uart wallak kura-yeye mokin nidja katitjiny", + "recentchanges-feed-description": "balgang yeye wallak ijow wiki nidja ngan-iny", "recentchanges-label-newpage": "Nidja wallak yyinang bibol", "recentchanges-label-minor": "Nidja neyp-nopb wallak", "recentchanges-label-bot": "Nidja balyat wallak", - "recentchanges-label-unpatrolled": "nidja wallak yuwart djinang byrida", + "recentchanges-label-unpatrolled": "nidja wallak uart djinang brid-iya", + "recentchanges-label-plusminus": "Bibol boola wallak nidja ngarnan bytes", "recentchanges-legend-heading": "<strong>Bun-ga:</strong>", + "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (djinang [[Special:NewPages|gwabbanijow-ung milgar bibol]])", + "rcnotefrom": "Ngardal {{PLURAL:$5|wallak}} kura <strong>$3, $4</strong> (yira ijow <strong>$1</strong> yong-a).", "rclistfrom": "Yong-a yy-inang wallak quadgin-ang $2 $3", "rcshowhideminor": "$1 neyp-nopb wallak", "rcshowhideminor-show": "Yong-a", @@ -298,6 +343,7 @@ "minoreditletter": "n", "newpageletter": "M", "boteditletter": "b", + "number_of_watching_users_pageview": "[$1 djinang-iny {{PLURAL:$1|niall}}]", "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} mila wallak", "rc-old-title": "goryat quadga jin $1", "recentchangeslinked": "Noyyang wallak", @@ -308,12 +354,13 @@ "recentchangeslinked-page": "Bibol kwel-le:", "recentchangeslinked-to": "Yong-a wallak bibol beda nitja bibol", "upload": "Yirra file", - "uploadlogpage": "Irak boorna waangkin", + "uploadlogpage": "Bil-lang boonadairn", "filedesc": "koorada waangkininy", "license": "Licensing:", "license-header": "Licensing", "imgfile": "file", "listfiles": "File list", + "listfiles_user": "Niall", "file-anchor-link": "File", "filehist": "File kooralang", "filehist-help": "Click il date/time djinang file jin-bal dundak ally-war time.", @@ -328,6 +375,7 @@ "filehist-comment": "Waarnkiny", "imagelinks": "File malyn", "linkstoimage": "Ngawaliny {{PLURAL:$1|bibol beda}} nidja file:", + "linkstoimage-more": "Bool $1 {{PLURAL:$1|bibol beda}} nidja file.\nNgawaliny gwabbaninijow yong-a {{PLURAL:$1|keny bibol beda}} bura nidja file.\nGaign [[Special:WhatLinksHere/$2|gwabbanijow bulyaru]] mila.", "nolinkstoimage": "Uart bibol beda nitja file", "linkstoimage-redirect": "$1 (file Dtallangiritch) $2", "sharedupload-desc-here": "Nidja file winjang $1 wer kaya malyn uame-projects.\n\nWarrang-an it [$2 file Warrang-an bibol] yong-a yal-lal-la begoory", @@ -335,11 +383,14 @@ "upload-disallowed-here": "Noonook uart wallak nidja file", "randompage": "alia bibol", "statistics": "Bula-wer-bula", + "statistics-header-users": "Niall bula-wer-bula", "double-redirect-fixer": "Dtallangiritch fixer", "nbytes": "$1 {{PLURAL:$1|byte|bytes}}", "nmembers": "$1 {{PLURAL:$1|Noonook}}", + "prefixindex": "Boola bibol wer prefix", "listusers": "Niall list", "newpages": "Milgar bibol", + "newpages-username": "Niall kwel-le:", "move": "Ngatta", "pager-newer-n": "{{PLURAL:$1|ngolango $1}}", "pager-older-n": "{{PLURAL:$1|kura}}", @@ -348,7 +399,10 @@ "booksources-search": "genuniny", "specialloguserlabel": "Ngunynilan", "speciallogtitlelabel": "djuno (Katta-wiriny ka {{ns:user}}:Niall-kewl-le wer niall):", - "log": "Boorna-waangkin", + "log": "Boonadairn", + "all-logs-page": "Bulyaru yong-a boorna", + "alllogstext": "Bulyaru yong-a il bundung bulyaru boorna nidja {{SITENAME}}\nNoonook djinanginy nulu-borak toyntch-wung boonadairn, niall kwel-le, ka bibol", + "logempty": "Uart mokininy yallabel bura boonadairn", "allpages": "Bandang bibol", "allarticles": "Bandang bibol", "allpagessubmit": "Koorl", @@ -363,13 +417,15 @@ "watch": "Djinang", "unwatch": "Uart djinang", "watchlist-details": "{{PLURAL:$1|$1 bibol}} noonook Djinanglist (wer waangkin bibol).", + "wlheader-showupdated": "Bibol yallabel wallak kura noonook djinang balgup be yong-a bura <strong>moorn</strong>", + "wlnote": "Ngardal {{PLURAL:$1|yuttock wallak|nidja yuttock <strong>$1</strong> wallak}} il yuttock {{PLURAL:$2|hour|<strong>$2</strong> hours}}, nun o-luk $3, $4.", "wlshowlast": "Yong-a yeye $1 hours $2 biryt", "watchlist-options": "djinanglist nidj-ka-nidja", "enotif_reset": "djiler yennar bibol nyiny", - "dellogpage": "Deletion boorn waagnkin", + "dellogpage": "Barranginy boonadairn", "rollbacklink": "yowal", "rollbacklinkcount": "yowal $1 {{PLURAL:$1|wallak}}", - "protectlogpage": "Nap-nap boorna-waangkin", + "protectlogpage": "Nap-nap boonadairn", "protectedarticle": "nap-nap \"[[$1]]\"", "modifiedarticleprotection": "wallak nap-nap-ang \"[[$1]]\"", "protect-default": "kaya yennar niall", @@ -377,24 +433,28 @@ "restriction-move": "Ngatta", "namespace": "kwel-el-up", "invert": "mudjardo", + "tooltip-invert": "Djiler nidja kuta ijow kwib-idj-ee wallak ijow bibol bura toyntch-wung kwel-leup (and noyyang kwel-leup il djiler)", "namespace_association": "Gabbyn kwel-le-up", + "tooltip-namespace_association": "Djiler nidja kuta ijow wer waangkin or bibol kwel-leup noyyang kwel-leup", "blanknamespace": "(Keny)", "contributions": "{{GENDER:$1|Niall}} wallak", "contributions-title": "Niall wallak-ang $1", "mycontris": "Ngun-ya katitjiny", "anoncontribs": "Nguny katitiny", "contribsub2": "Natj {{GENDER:$3|$1}} ($2)", - "nocontribs": "uart katitiny wallak quadgabe barranginy", + "nocontribs": "uart wallak quadgabe mokininy nidja katitjiny", "uctop": "(yeye)", "month": "Month-ang (wer gwytch-ang-at)", "year": "Year-ang (wer gwytch-ang-at)", "sp-contributions-newbies": "Yong-a wallak il ngolango gaduk", - "sp-contributions-blocklog": "nap-nap boorna waangkin", + "sp-contributions-blocklog": "nap-nap boonadairn", "sp-contributions-uploads": "irak", - "sp-contributions-logs": "Boorna-waangkin", + "sp-contributions-logs": "boonadairn", "sp-contributions-talk": "waarnk", "sp-contributions-search": "djinanginy ngun-ya katitiny", "sp-contributions-username": "IP karl-up ka niall-kwel-le", + "sp-contributions-toponly": "Yong-a wallak yeye kura-wallak", + "sp-contributions-newonly": "Yong-a wallak yallabel bibol quadga", "sp-contributions-submit": "genunniny", "whatlinkshere": "natj beda nitja", "whatlinkshere-title": "Bibol beda $1", @@ -407,7 +467,7 @@ "whatlinkshere-prev": "{{PLURAL:$1|Gwytch-ang-at $1}}", "whatlinkshere-next": "{{PLURAL:$1|warnign $1}}", "whatlinkshere-links": "← beda", - "whatlinkshere-hideredirs": "$1 redirects", + "whatlinkshere-hideredirs": "$1 dtallangiritch", "whatlinkshere-hidetrans": "$1 transclusions", "whatlinkshere-hidelinks": "$1 beda", "whatlinkshere-hideimages": "$1 file beda", @@ -416,12 +476,15 @@ "infiniteblock": "kal-ya-kool", "blocklink": "nap-nap", "contribslink": "ngunya katitjiny", - "blocklogpage": "Nap-nap boorna waangkin", + "blocklogpage": "Nap-nap boonadairn", + "blocklogentry": "nap-nap [[$1]] borduck dan-dee-deenang time $2 $3", + "reblock-logentry": "wallak nap-nap-ang [[$1]] borduck dan-dee-deenang time $2 $3", "block-log-flags-nocreate": "gaduk quadga warra", "proxyblocker": "Proxy blocker", - "movelogpage": "ngatta boorn waangkin", + "movelogpage": "Ngatta boonadairn", "export": "Export bibol", "thumbnail-more": "Koom-bariny", + "importlogpage": "Yal-burrung boorn", "tooltip-pt-userpage": "{{GENDER:|Noonook Niall}} bibol", "tooltip-pt-mytalk": "{{GENDER:|Noonook}} waangkin bibol", "tooltip-pt-preferences": "{{GENDER:|Noonook}} Nidj-wer-nidja", @@ -468,11 +531,14 @@ "tooltip-ca-nstab-mediawiki": "Djinang system waagnkin", "tooltip-ca-nstab-template": "Djinang birnup", "tooltip-ca-nstab-category": "Genuny warrangan bibol", + "tooltip-minoredit": "Djiler nidja wallak kardidi", "tooltip-save": "Burung noonook wallak", "tooltip-preview": "Djaning gwytch-ang-at barranginy noonook wallak.", "tooltip-diff": "Yong-a wallak noonook maar waangkininy", + "tooltip-compareselectedversions": "Djinang nidja wallak nidja koogal kura djinang-ung nidja bibol", "tooltip-watch": "noonook watchlist wer bibol", "tooltip-rollback": "\"Yowal\" wallak ijow yuttock niall wallak nidja bibol ken-y-click", + "tooltip-undo": "\"Undo\" yowal nidja wallak and yalgaranan nidja wallak bibol bura nun-gow-iny. Kaya wer-iny niat-juk bura koorada waangkiny.", "tooltip-summary": "Djarbiny koo-ra-da waangkininy", "simpleantispam-label": "Anti-spam check.\nDo <strong>not</strong> fill this in!", "pageinfo-title": "Kadadjiny nitja \"$1\"", @@ -486,20 +552,29 @@ "pageinfo-article-id": "Bibol ID", "pageinfo-language": "Bibol wallak waagnkin", "pageinfo-content-model": "Bibol katitininy birnup", + "pageinfo-robot-policy": "Gwabbanijow borduck balyat-ta", "pageinfo-robot-index": "Kaya", "pageinfo-robot-noindex": "Uart", "pageinfo-watchers": "Bula-ang bibol djinanging", + "pageinfo-few-watchers": "Ngar-ral yallabel $1 {{PLURAL:$1|noonook djinanginy}}", "pageinfo-redirects-name": "Boola nidja bibol dtallangiritch", "pageinfo-subpages-name": "Bula culang bibol-ung nidja bibol", "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|Dtallangiritch|Boola dtallangiritch}}; $3 {{PLURAL:$3|Uart dtallangiritch}})", "pageinfo-firstuser": "Bibol quadga", "pageinfo-firsttime": "date il bibol quadga", + "pageinfo-lastuser": "Gwytch niall", "pageinfo-lasttime": "Date yeye wallak", + "pageinfo-edits": "Ngarnan wallak", + "pageinfo-authors": "Ngarnan-ung wallakwallak niall", + "pageinfo-recent-edits": "yeye bula-ang wallakang (winji kura $1)", + "pageinfo-recent-authors": "yeye bula-ang wallakwallak niall", "pageinfo-magic-words": "Mulga {{PLURAL:$1|waangkin}} ($1)", "pageinfo-hidden-categories": "Kopin {{PLURAL:$1|Waaragan}}", "pageinfo-templates": "Transcluded {{PLURAL:$1|birnup}} ($1)", "pageinfo-toolboxlink": "bibol kadadjiny", + "pageinfo-contentpage": "kaya jin katitiny bibol", "pageinfo-contentpage-yes": "Kaya", + "patrol-log-page": "Patrol boorn", "previousdiff": "← kura wallak", "nextdiff": "yy-inang wallak →", "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|bibol}}", @@ -512,6 +587,7 @@ "show-big-image-other": "Wam {{PLURAL:$2|nidja-boola}}: $1.", "show-big-image-size": "$1 × $2 pixels", "metadata": "Metadata", + "metadata-help": "This file contains additional information, probably added from the digital camera or scanner used to create or digitize it.\nIf the file has been modified from its original state, some details may not fully reflect the modified file.", "metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude", "exif-orientation": "Winje koorliny", "exif-xresolution": "I-jow nidja-boola", @@ -535,23 +611,33 @@ "watchlisttools-edit": "Djinang wer wallak djinanglist", "watchlisttools-raw": "Wallak dalyar djinanglist", "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|niall waarnk]])", + "redirect": "Dtallangiritch borduck file, niall, bibol, kura djinang, ka boonadairn ID", + "redirect-summary": "This special page redirects to a file (given the filename), a page (given a revision ID or page ID), a user page (given a numeric user ID), or a log entry (given the log ID). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], or [[{{#Special:Redirect}}/logid/186]].", "redirect-submit": "Koorl", "redirect-lookup": "Djinang yira:", + "redirect-value": "Value:", "redirect-user": "Niall ID", "redirect-page": "Bibol ID", + "redirect-revision": "Bibol kura djinang", "redirect-file": "File kwel-le", "specialpages": "birdarra bibol", "tag-filter": "[[Special:Tags|Tag]] Uart nidj kia nidj:", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)", "tags-active-yes": "Kaya", "tags-active-no": "Uart", + "tags-hitcount": "$1 {{PLURAL:$1|wallak}}", "logentry-delete-delete": "$1 {{GENDER:$2|Barrangin}} bibol $3", "logentry-delete-restore": "$1 {{GENDER:$2|garro-yual}} bibol $3 ($4)", + "logentry-delete-revision": "$1 {{GENDER:$2|wallak}} djinang-ung {{PLURAL:$5|kura djinang}} il bibol $3: $4", "revdelete-content-hid": "Kopin mar waangkin", "logentry-move-move": "$1 {{GENDER:$2|ngatta}} bibol $3 il $4", + "logentry-move-move-noredirect": "$1 {{GENDER:$2|ngatta}} bibol $3 il $4 borduckang dtallangiritch", + "logentry-move-move_redir": "$1 {{GENDER:$2|ngatta}} bibol $3 il $4 bockech dtallangiritch", + "logentry-patrol-patrol-auto": "$1 eche-na {{GENDER:$2|djiler}} kura djinang $4 il bibol $3 kaya wallak", "logentry-newusers-create": "Niall gaduk $1 {{GENDER:$2|ally-quadga}}", "logentry-newusers-autocreate": "Niall gaduk $1 {{GENDER:$2|ally-quadga}} eche-na", "logentry-upload-upload": "$1 {{GENDER:$2|irak}} $3", + "logentry-upload-overwrite": "$1 {{GENDER:$2|bil-lang}} yyinang dum-bart-ang $3", "searchsuggest-search": "genuniny {{SITENAME}}", "duration-days": "$1 {{PLURAL:$1|biryt}}", "randomrootpage": "Alia bibol birnup" diff --git a/languages/i18n/pl.json b/languages/i18n/pl.json index cfe95f2370..68bca8af86 100644 --- a/languages/i18n/pl.json +++ b/languages/i18n/pl.json @@ -605,11 +605,11 @@ "botpasswords-insert-failed": "Nie udało się dodać robota o nazwie \"$1\". Czy był już wcześniej dodany?", "botpasswords-update-failed": "Nie udało się zmienić robota o nazwie \"$1\". Czy został usunięty?", "botpasswords-created-title": "Hasło bota stworzone", - "botpasswords-created-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało utworzone.", + "botpasswords-created-body": "Hasło bota \"$1\" {{GENDER:$2|użytkownika|użytkowniczki}} \"$2\" zostało utworzone.", "botpasswords-updated-title": "Hasło bota zaktualizowane", - "botpasswords-updated-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało zaktualizowane.", + "botpasswords-updated-body": "Hasło bota \"$1\" {{GENDER:$2|użytkownika|użytkowniczki}} \"$2\" zostało zaktualizowane.", "botpasswords-deleted-title": "Hasło bota usunięte", - "botpasswords-deleted-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało usunięte.", + "botpasswords-deleted-body": "Hasło bota \"$1\" {{GENDER:$2|użytkownika|użytkowniczki}} \"$2\" zostało usunięte.", "botpasswords-newpassword": "Nowe hasło do zalogowania się przez <strong>$1</strong> to <strong>$2</strong>. <em>Proszę je zapisać w celu wykorzystania w przyszłości.</em> <br> (Dla starszych botów, które wymagają loginu takiego samego jak ewentualna nazwa użytkownika można użyć <strong>$3</strong> jako nazwę użytkownika i <strong>$4</strong> jako hasło.)", "botpasswords-no-provider": "BotPasswordsSessionProvider nie jest dostępne.", "botpasswords-restriction-failed": "Logowanie nie powiodło się z powodu ograniczeń na hasło bota.", @@ -821,6 +821,7 @@ "parser-template-loop-warning": "Wykryto pętlę w szablonie [[$1]]", "template-loop-category": "Strony z pętlami szablonów", "template-loop-category-desc": "Strona zawiera pętlę szablonów, czyli szablon, który wywołuje sam siebie rekursywnie.", + "template-loop-warning": "<strong>Ostrzeżenie:</strong> Ta strona wywołuje [[:$1]], co tworzy pętlę szablonu (nieskończone wywołanie rekurencyjne).", "parser-template-recursion-depth-warning": "Przekroczno limit głębokości rekurencji szablonu ($1)", "language-converter-depth-warning": "Przekroczono ograniczenie ($1) głębokości zagnieżdżenia konwersji językowej", "node-count-exceeded-category": "Strony, gdzie przekroczono liczbę węzłów", @@ -1361,6 +1362,7 @@ "recentchanges-summary": "Ta strona przedstawia historię ostatnich zmian w tej wiki.", "recentchanges-noresult": "Brak zmian w wybranym okresie spełniających twoje kryteria.", "recentchanges-network": "Z powodu błędu technicznego nie można załadować żadnych wyników. Spróbuj odświeżyć stronę.", + "recentchanges-notargetpage": "Wprowadź powyżej nazwę strony, aby zobaczy zmiany związane z tą stroną.", "recentchanges-feed-description": "Obserwuj najświeższe zmiany w tej wiki.", "recentchanges-label-newpage": "W tej edycji utworzono nową stronę", "recentchanges-label-minor": "To jest drobna zmiana", @@ -1377,7 +1379,9 @@ "rcfilters-group-results-by-page": "Grupuj wyniki według stron", "rcfilters-activefilters": "Aktywne filtry", "rcfilters-advancedfilters": "Zaawansowane filtry", - "rcfilters-limit-title": "Zmian do pokazania", + "rcfilters-limit-title": "Wyników do pokazania", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|zmiana|$1 zmiany|$1 zmian}}, $2", + "rcfilters-date-popup-title": "Przeszukiwany okres", "rcfilters-days-title": "Ostatnich dni", "rcfilters-hours-title": "Ostatnich godzin", "rcfilters-days-show-days": "$1 {{PLURAL:$1|dzień|dni}}", @@ -1491,6 +1495,9 @@ "rcfilters-watchlist-showupdated": "<strong>Wytłuszczono</strong> strony, których nie odwiedził{{GENDER:|e|a|e}}ś od czasu zapisania ostatnich zmian.", "rcfilters-preference-label": "Wyłącz ulepszenia strony Ostatnie zmiany", "rcfilters-preference-help": "Wycofuje wszystkie zmiany interfejsu z 2017 i narzędzia dodane od tamtej pory.", + "rcfilters-filter-showlinkedfrom-option-label": "Pokaż zmiany na stronach linkowanych <strong>Z</strong> tej strony", + "rcfilters-filter-showlinkedto-option-label": "Pokaż zmiany na stronach linkujących <strong>DO</strong> tej strony", + "rcfilters-target-page-placeholder": "Wprowadź nazwę strony", "rcnotefrom": "Poniżej {{PLURAL:$5|pokazano zmianę|pokazano zmiany}} {{PLURAL:$5|wykonaną|wykonane}} po <strong>$3, $4</strong> (nie więcej niż '''$1''' pozycji).", "rclistfromreset": "Zresetuj wybór daty", "rclistfrom": "Pokaż nowe zmiany od $3 $2", @@ -1535,7 +1542,7 @@ "recentchangeslinked-feed": "Zmiany w linkowanych", "recentchangeslinked-toolbox": "Zmiany w linkowanych", "recentchangeslinked-title": "Zmiany w linkowanych z „$1”", - "recentchangeslinked-summary": "Poniżej znajduje się lista ostatnich zmian na stronach linkowanych z podanej strony (lub we wszystkich stronach należących do podanej kategorii).\nStrony z [[Special:Watchlist|listy obserwowanych]] są '''wytłuszczone'''.", + "recentchangeslinked-summary": "Wprowadź nazwę strony, aby zobaczyć zmiany na stronach linkowanych przez tę stronę lub linkujących do niej. (Aby zobaczyć strony z kategorii wpisz Category:Nazwa). Strony z [[Special:Watchlist|listy obserwowanych]] są '''wytłuszczone'''.", "recentchangeslinked-page": "Tytuł strony:", "recentchangeslinked-to": "Pokaż zmiany nie na stronach linkowanych, a na stronach linkujących do podanej strony", "recentchanges-page-added-to-category": "dodano [[:$1]] do kategorii", @@ -1547,6 +1554,7 @@ "uploadbtn": "Prześlij plik", "reuploaddesc": "Przerwij wysyłanie i wróć do formularza wysyłki", "upload-tryagain": "Zapisz zmieniony opis pliku", + "upload-tryagain-nostash": "Prześlij ponownie przesłany plik i zmodyfikowany opis", "uploadnologin": "Nie jesteś zalogowany", "uploadnologintext": "Musisz $1 przed przesłaniem plików.", "upload_directory_missing": "Katalog dla przesyłanych plików ($1) nie istnieje i nie może zostać utworzony przez serwer WWW.", @@ -1606,6 +1614,7 @@ "file-deleted-duplicate-notitle": "Plik jest identyczny z plikiem, który został wcześniej usunięty, a jego nazwa została ukryta. Należy poprosić kogoś z możliwością przeglądania ukrytych danych, aby przeanalizował sytuację przed przystąpieniem do jego ponownego przesłania.", "uploadwarning": "Ostrzeżenie o przesyłaniu", "uploadwarning-text": "Zmień poniższy opis pliku i spróbuj ponownie.", + "uploadwarning-text-nostash": "Ponownie prześlij plik, zmodyfikuj poniższy opis i spróbuj ponownie.", "savefile": "Zapisz plik", "uploaddisabled": "Przesyłanie plików wyłączone", "copyuploaddisabled": "Przesyłanie poprzez podanie adres URL jest wyłączone.", @@ -1728,7 +1737,17 @@ "uploadstash-exception": "Nie udało się zapisać przesyłanego pliku w magazynie tymczasowym ($1): „$2”.", "uploadstash-bad-path": "Ścieżka nie istnieje.", "uploadstash-bad-path-invalid": "Ścieżka jest nieprawidłowa.", + "uploadstash-bad-path-unknown-type": "Nieznany typ „$1”.", + "uploadstash-file-not-found-no-thumb": "Nie można uzyskać miniaturki.", + "uploadstash-file-not-found-no-local-path": "Brak lokalnej ścieżki dla skalowanego elementu.", + "uploadstash-file-not-found-no-object": "Nie można utworzyć lokalnego obiektu pliku dla miniatury.", + "uploadstash-file-not-found-no-remote-thumb": "Nie udało się pobrać miniatury: $1\nURL = $2", "uploadstash-file-not-found-missing-content-type": "Brakuje nagłówka content-type.", + "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-extension": "Rozszerzenie ma wartość zerową.", + "uploadstash-zero-length": "Plik ma zerowy rozmiar.", "invalid-chunk-offset": "Nieprawidłowe przesunięcie fragmentu", "img-auth-accessdenied": "Odmowa dostępu", "img-auth-nopathinfo": "Brak PATH_INFO.\nSerwer nie został skonfigurowany, tak aby przekazywał tę informację.\nMożliwe, że jest oparty na CGI i nie może obsługiwać img_auth.\nWięcej o informacji o autoryzacji grafik na https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.", @@ -2405,6 +2424,7 @@ "sp-contributions-newonly": "Pokazuj tylko edycje tworzące nową stronę", "sp-contributions-hideminor": "Ukryj drobne zmiany", "sp-contributions-submit": "Szukaj", + "sp-contributions-outofrange": "Nie można wyświetlić żadnych wyników. Żądany zakres IP jest większy niż limit CIDR równy /$1.", "whatlinkshere": "Linkujące", "whatlinkshere-title": "Strony linkujące do „$1”", "whatlinkshere-page": "Strona:", @@ -2528,6 +2548,7 @@ "ipb_blocked_as_range": "Błąd – adres IP $1 nie został zablokowany bezpośrednio i nie może zostać odblokowany.\nNależy on do zablokowanego zakresu adresów $2. Odblokować można tylko cały zakres.", "ip_range_invalid": "Niepoprawny zakres adresów IP.", "ip_range_toolarge": "Zakresy IP większe niż /$1 są niedozwolone.", + "ip_range_exceeded": "Zakres IP przekracza zakres maksymalny. Dozwolony zakres to /$1.", "proxyblocker": "Blokowanie proxy", "proxyblockreason": "Twój adres IP został zablokowany, ponieważ jest to adres otwartego proxy.\nO tym poważnym problemie dotyczącym bezpieczeństwa należy poinformować dostawcę Internetu lub pomoc techniczną.", "sorbsreason": "Twój adres IP znajduje się na liście serwerów open proxy w DNSBL, używanej przez {{GRAMMAR:B.lp|{{SITENAME}}}}.", @@ -2671,6 +2692,8 @@ "import-mapping-namespace": "Importuj do przestrzeni nazw:", "import-mapping-subpage": "Importuj jako podstrony strony:", "import-upload-filename": "Nazwa pliku:", + "import-upload-username-prefix": "Prefiks interwiki:", + "import-assign-known-users": "Przypisz edycje lokalnym użytkownikom, jeżeli istnieją użytkownicy o tych nazwach", "import-comment": "Komentarz:", "importtext": "Korzystając na źródłowej wiki z narzędzia [[Special:Export|eksportu]] wyeksportuj plik.\nZapisz go na swoim dysku, a następnie prześlij go tutaj.", "importstart": "Trwa importowanie stron...", @@ -2679,6 +2702,7 @@ "imported-log-entries": "Zaimportowano $1 {{PLURAL:$1|wpis|wpisy|wpisów}} rejestru.", "importfailed": "Import nie powiódł się: $1", "importunknownsource": "Nieznany format importowanych danych", + "importnoprefix": "Nie podano prefiksu interwiki", "importcantopen": "Nie można otworzyć importowanego pliku", "importbadinterwiki": "Błędny link interwiki", "importsuccess": "Import zakończony powodzeniem!", @@ -3360,6 +3384,8 @@ "autosumm-blank": "UWAGA! Usunięcie treści (strona pozostała pusta)!", "autosumm-replace": "UWAGA! Zastąpienie treści hasła bardzo krótkim tekstem: „$1”", "autoredircomment": "Przekierowanie do [[$1]]", + "autosumm-removed-redirect": "Usunięto przekierowanie do [[$1]]", + "autosumm-changed-redirect-target": "Zmiana elementu docelowego przekierowania z [[$1]] na [[$2]]", "autosumm-new": "Utworzono nową stronę \"$1\"", "autosumm-newblank": "Utworzono pustą stronę", "size-bytes": "$1 {{PLURAL:$1|bajt|bajty|bajtów}}", @@ -3521,6 +3547,19 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Znacznik|Znaczniki}}]]: $2)", "tag-mw-contentmodelchange": "zmiana modelu zawartości", "tag-mw-contentmodelchange-description": "Edycje, które [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel zmieniają model zawartości] strony", + "tag-mw-new-redirect": "Nowe przekierowanie", + "tag-mw-new-redirect-description": "Edycja, która tworzy nowe przekierowanie albo zmienia istniejącą stronę (nie-przekierowanie) na przekierowanie", + "tag-mw-removed-redirect": "Usunięto przekierowanie", + "tag-mw-removed-redirect-description": "Edycja, która zmienia dotychczasowe przekierowanie na nie-przekierowanie", + "tag-mw-changed-redirect-target": "Zmieniony cel przekierowania", + "tag-mw-changed-redirect-target-description": "Edycja, która zmienia stronę docelową przekierowania", + "tag-mw-blank": "Usunięcie całej zawartości strony", + "tag-mw-blank-description": "Edycja, która blankuje stronę", + "tag-mw-replace": "Zastąpiono", + "tag-mw-replace-description": "Edycja, która usuwa ponad 90% zawartości strony", + "tag-mw-rollback": "Wycofanie zmian", + "tag-mw-rollback-description": "Edycja, która przywraca poprzednią wersję przy użyciu funkcji cofania zmian (rollback)", + "tag-mw-undo": "Cofnij", "tags-title": "Znaczniki", "tags-intro": "Na tej stronie znajduje się lista znaczników, którymi oprogramowanie może oznaczyć edycje, oraz ich opisy.", "tags-tag": "Nazwa znacznika", diff --git a/languages/i18n/ps.json b/languages/i18n/ps.json index bfa021df52..a757afc6d8 100644 --- a/languages/i18n/ps.json +++ b/languages/i18n/ps.json @@ -93,7 +93,7 @@ "february-gen": "فبروري", "march-gen": "مارچ", "april-gen": "اپرېل", - "may-gen": "می", + "may-gen": "مې", "june-gen": "جون", "july-gen": "جولای", "august-gen": "اگسټ", @@ -580,8 +580,10 @@ "changeemail-nochange": "مهرباني وکړئ یو بل نوی برېښناليک پته ولیکئ.", "resettokens": "د ټوکنونو بیاکتنه", "resettokens-text": "تاسو کولی شئ ټوکنونه بیا ځای پرځای کړئ کوم چې دلته ستاسو د حساب سره تړلی ځینې مشخصو معلوماتو ته دلا سرسۍ اجازه ورکوي.\n\nتاسو باید دا کار وکړئ که چیرې تاسو په ناڅاپي توګه له چا سره شریک کړي یا ستاسو حساب ورسره موافق وي.", + "resettokens-no-tokens": "د بیا جوړولو لپاره هیڅ نښه نشته.", "resettokens-tokens": "ټوکنونه:", "resettokens-token-label": "$1 (اوسنی ارزښت: $2)", + "resettokens-watchlist-token": "د ویب فیډ (د اتوم / آر ایس ایس) لپاره ډک کړئ [[Special:Watchlist| د خپل د کتار لیست په پاڼو کې بدلونونه]]", "resettokens-done": "د رایو بیا راګرځول.", "resettokens-resetbutton": "د ټاکل شوي ټوکنونو بیا راګرځول", "bold_sample": "زغرد متن", @@ -616,7 +618,10 @@ "blankarticle": "<strong>خبرتیا:</strong> تاسو د یو خالي مخ جوړلو په حال کي ياست.\nکه «$1» دوهم ځلي کښي کاږي، نو مخ به د معلوماتو بغير جوړ سي.", "anoneditwarning": "<strong>گواښنه:</strong> تاسې غونډال کې نه ياست ننوتي. که تاسې کوم سمونونه ترسره کوۍ نو ستاسې IP پته به ټولو ته د دې مخ د سمونونو په پېښليک کې ښکاري. که تاسې په خپل نوم <strong>[$1 کې ننوځئ]</strong> يا <strong>[$2 يو گڼون جوړ کړئ]</strong>، نو ستاسې سمونونه به ستاسې کارن-نوم اړونده ثبت شي چې ډېرې نورې گټې هم لري.", "anonpreviewwarning": "''تاسې غونډال ته نه ياست ننوتي. خوندي کولو سره به ستاسې IP پته به د دې مخ د سمونونو په پېښليک کې ثبت شي.''", + "missingsummary": "<strong>یادونه:</strong> تاسو د سمون لنډیز ندی چمتو کړی.\nکه تاسو \"$1\" کليک کړي نو بیا به ستاسو بدلون پرته له کوم انتظاره خوندي شي.", + "selfredirect": "<strong>خبرداری:</strong> تاسو دا پاڼه دپاڼي خپل مخ ته استوي.ښایي تاسو د ګرځولو لپاره ناسم هدف مشخص کړی وي، یا تاسو ممکن په غلطه پاڼه سمونه کوي.\nکه تاسو \"$1\" بيا کلیک کړي، د مخ ورګرځونه به په هر دليل جوړه شي.", "missingcommenttext": "لطفاً کمينټ لاندې وليکۍ.", + "missingcommentheader": "<strong>یادونه:</strong> تاسو د سمون لنډیز ندی چمتو کړی.\nکه تاسو \"$1\" کليک کړي نو بیا به ستاسو بدلون پرته له کوم انتظاره خوندي شي.", "summary-preview": "د لنډيز مخليدنه:", "subject-preview": "د پروژې بيا ليدنه:", "previewerrortext": "د بدلونونو د مخليدنو په وخت کې مو يوه ستونزه رامېنځ ته شوه.", @@ -633,29 +638,39 @@ "loginreqlink": "ننوتل", "loginreqpagetext": "د نورو مخونو د کتلو لپاره تاسو بايد $1 وکړۍ.", "accmailtitle": "پټنوم ولېږل شو.", + "accmailtext": "په ناڅاپي توګه جوړ شوي پټنوم د [[User talk:$1|$1]] لپاره و $2 ته ولیږل شو. دا کېدی شي په <em>[[Special:ChangePassword|پټنوم بدلون]] کي بدلون ومومي </em> مخ د ننوتلو په وخت کې.", "newarticle": "(نوی)", "newarticletext": "تاسې د يوې داسې تړنې څارنه کړې چې لا تر اوسه پورې نه شته.\nکه همدا مخ ليکل غواړۍ، نو په لانديني چوکاټ کې خپل متن وټاپئ (د لا نورو مالوماتو لپاره د [$1 لارښود مخ] وگورئ).\nکه چېرته تاسې دلته په تېروتنه راغلي ياست، نو يواځې د خپل د کتنمل '''مخ پر شا''' تڼۍ مو وټوکئ.", "anontalkpagetext": "----''دا د يوه ورکنومي کارن چې کارن-نوم نه لري او يا خپل کارن-نوم نه کاروي، د سکالو يوه پاڼه ده. نو د يوه کس د پېژندلو پخاطر موږ د هماغه کارن د انټرنېټ شمېره يا IP پته دلته ثبتوؤ. داسې يوه IP پته د ډېرو کارنانو لخوا هم کارېدلی شي. که تاسې يو ورکنومی کارن ياست او تاسې ته دا څرگندېږي چې تاسې ته نااړونده پېغامونه او تبصرې اشاره شوي، نو د نورو بې نومو کارنانو او ستاسې ترمېنځ د ټکنتوب د مخ نيونې لپاره لطفاً [[Special:CreateAccount|يو گڼون جوړ کړۍ]] او يا هم [[Special:UserLogin|غونډال ته ورننوځۍ]].''", "noarticletext": "دم مهال په دې مخ کې څه نشته.\nتاسې کولای شی چې په نورو مخونو کې [[Special:Search/{{PAGENAME}}|د دې مخ د سرليک پلټنه]]،\n<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={{FULLPAGENAMEE}}}} اړونده يادښتونه وپلټۍ]</span>، خو تاسې د دې مخ د جوړولو اجازه نه لرۍ.", + "missing-revision": "سمون #$1 د «{{FULLPAGENAME}}» څخه شتون نه لري.\n\nدا عموما د ړنگ شوي مخ د تاریخ لپاره د لینک لاندینۍ پيښې سره تړاو لري.\nکولای شي نور معلومات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ړنګ شوي] ساحه کې پيدا کړي.", "userpage-userdoesnotexist": "د \"<nowiki>$1</nowiki>\" گڼون نه دی ثبت شوی.\nلطفاً ځان ډاډه کړئ چې آيا تاسې په رښتيا همدا مخ جوړول/سمول غواړئ.", "userpage-userdoesnotexist-view": "د \"$1\" گڼون نه دی ثبت شوی.", "blocked-notice-logextract": "دم مهال په دې کارن بنديز لگېدلی.\nد بنديز يادښت تازه مالومات په لاندې توگه دي:", "clearyourcache": "'''يادښت:''' د غوره توبونو د خوندي کولو وروسته، خپل د کتنمل (بروزر) ساتل شوې حافظه تازه کړی.\n* '''فايرفاکس/ سفري:''' په دې کتنمل کې د ''Reload'' د ټکوهلو په وخت د ''Shift'' تڼۍ نيولې وساتی، او يا هم ''Ctrl-F5'' يا ''Ctrl-R''تڼۍ کېښکاږۍ (په Apple Mac کمپيوټر باندې ''⌘-R'' کېښکاږۍ)\n* '''گووگل کروم:''' په دې کتنمل کې د ''Ctrl-Shift-R'' تڼۍ کېښکاږۍ (د مک لپاره ''⌘-Shift-R'')\n* '''انټرنټ اېکسپلورر:''' په دې کتنمل کې د ''Refresh'' د ټکوهلو په وخت کې د ''Ctrl'' تڼۍ کېښکاږلې ونيسۍ، او يا هم د ''Ctrl-F5'' تڼۍ کېښکاږۍ\n* '''اوپرا''': په دې کتنمل کې د خپل براوزر ساتل شوې حافظه پدې توگه سپينولی شی ''Tools→Preferences''", "usercssyoucanpreview": "'''نکته:''' د سی‌اس‌اس دوتنې د خوندي کولو دمخه خپله، د تڼۍ سره '''{{int:showpreview}}''' دا امتحان کړې.", + "userjsyoucanpreview": "'''نکته:''' د سی‌اس‌اس دوتنې د خوندي کولو دمخه خپله، د تڼۍ سره '''{{int:showpreview}}''' دا امتحان کړې.", "usercsspreview": "'''هېر مو نشي چې دا يوازې ستاسې د کارن CSS مخليدنه ده.'''\n'''تر اوسه پورې لا ستاسې بدلونونه نه دي خوندي شوي!'''", "userjspreview": "'''هېر مو نشي چې دا يوازې ستاسې د کارن د جاوا سکرېپټ آزمېيل/مخليدنه ده.'''\n'''تر اوسه پورې لا ستاسې بدلونونه نه دي خوندي شوي!'''", "sitecsspreview": "'''په پام کې دې وي چې دا يوازې ستاسې د CSS مخليدنه ده.'''\n'''تر اوسه پورې لا ستاسې بدلونونه نه دي خوندي شوي!'''", "sitejspreview": "'''په پام کې مو اوسه چې تاسې يوازې د دغه جاواسکرېپټ کوډ مخليدنه کوۍ.'''\n'''تر اوسه پورې دا نه دی خوندي شوی!'''", + "userinvalidcssjstitle": "<strong>خبرداری:</strong>دلته هیڅ پوست نشته \"$1\".\nد ګمرکونو .ثي اس اس او .ج س مخونه کوچني سرلیک استعمالوي، او داسې نور. {{ns:user}}:Foo/vector.css که مخالف وي نو {{ns:user}}:Foo/Vector.css.", "updated": "(تازه)", "note": "'''يادونه:'''", "previewnote": "'''هېر مو نه شي چې دا يواځې يوه مخليدنه ده.'''\nستاسې لخوا ترسره شوي بدلونونه لا تر اوسه پورې نه دي خوندي شوي!!", "continue-editing": "د سمولو سيمې ته ورتلل", + "previewconflict": "دا غوښتنه د متن د سمون په سیمه کې متن منعکس کوي ځکه چې دا به هلته ښکاره شي چي تاسو يي د خوندي کولو لپاره غوره کړئ.", + "session_fail_preview": "وبخښي! موږ د سیشن ډاټا د ضایع کيدلو له امله ستاسو سمون ندی ترسره کړي..\n\nکېدی شي تاسو د سيسټم څخه وتلي ياست. <strong>لطفا تایید کړئ چې تاسو لا تر اوسه ننوتلي یاست او کړنه بیا ترسره کړئ</strong>.\nکه دا تراوسه لا هم کار نه کوي، نو بيا يو ځل [[Special:UserLogout|د دي ځای څخه ووځي]] او بيرته راننوځي، او ډاډه کړئ چې ستاسو براؤزر اجازه لري چې د دې سایټ څخه کوکیز ترلاسه کړي.", + "session_fail_preview_html": "وبخښي! موږ د سیشن ډاټا د ضایع کيدلو له امله ستاسو سمون ندی ترسره کړي..\n\n<em>ځکه د {{SITENAME}} اي ټي ام ال لين بند کړل شوي وو، وړاندې کول د جاواسکرېپ بریدونو په وړاندې د احتیاط په توګه پټ شوي.</em>\n\n<strong>که دا د قانوني سمونې هڅه وي، لطفا بیا هڅه وکړئ.</strong>\nکه دا تراوسه لا هم کار نه کوي، نو بيا يو ځل [[Special:UserLogout|د دي ځای څخه ووځي]] او بيرته راننوځي، او ډاډه کړئ چې ستاسو براؤزر اجازه لري چې د دې سایټ څخه کوکیز ترلاسه کړي.", + "token_suffix_mismatch": "<strong>ستاسو سمون ونه منل شو ځکه چې ستاسو مراجع د تفتیش په نښه کې د تکرار تورو نښې نښانې کړي.</strong>\nدا سمون د پاڼې د ټکو د فساد مخنیولو لپاره ونه منل شوه.\nدا کله پېښ شي چې تاسو د ګوتو ویب سایټ پریمین پراکسي خدمت کاروئ.", + "edit_form_incomplete": "<strong>د سمون فورم ځینې برخې وانه وښتي; دوه ځله يي وګورئ چې ستاسو سمونونه پاتي دي او بیا هڅه وکړئ.</strong>", "editing": "د $1 سمونه", "creating": "$1 جوړېدنې کې دی", "editingsection": "$1 (برخه) په سمېدنې کې دی", "editingcomment": "د $1 سمون (نوې برخه)", "editconflict": "په سمولو کې خنډ: $1", + "explainconflict": "بل چا په دې مخ کي هغه وخت سمون راوست چې تاسو هم په سمون اخته وست.\nد مخ په ساحه کې دا متن شامل دي ځکه چې دا اوس مهال شتون لري.\nستاسو بدلونونه په لاندې متن کې ښودل شوي.\nتاسو باید خپل بدلونونه په موجوده متن کې ضمیمه کړئ..\n<strong>يوازې</strong> د متن په ساحه کې به متن هغه وخت خوندي شي کله چې تاسو دلته \"$1\" کليک کړي.", "yourtext": "ستاسې متن", "storedversion": "زېرمه شوې مخکتنه", "yourdiff": "توپيرونه", @@ -682,6 +697,7 @@ "recreate-moveddeleted-warn": "'''گواښنه: تاسې د يوه داسې مخ بياجوړونه کوۍ کوم چې يو ځل پخوا ړنگ شوی وو.'''\n\nپکار ده چې تاسې په دې ځان پوه کړۍ چې ايا دا تاسې ته وړ ده چې د همدې مخ جوړول په پرله پسې توگه وکړۍ.\nستاسې د اسانتياوو لپاره د همدې مخ د ړنگېدلو يادښت هم ورکړ شوی:", "moveddeleted-notice": "دا مخ ړنگ شوی.\nدلته لاندې ددې مخ د ړنگېدنې او لېږدېدنې ياداښت د سرچينې په توگه ورکړل شوی دي.", "log-fulllog": "بشپړ يادښت کتل", + "edit-hook-aborted": "ړنګول د هوک په واسطه وتړل شو.\nدا نور هيڅ څرګندوني نه ورکوي.", "edit-gone-missing": "د دې مخ اوسمهالول و نه کړای شول.\nداسې ښکاري چې دا مخ ړنگ شوی.", "edit-conflict": "د سمولو خنډ", "edit-no-change": "ستاسې سمون بابېزه وګڼل شو، دا ځکه چې تاسې په متن کې کوم بدلون نه دی راوستلی.", @@ -690,18 +706,27 @@ "postedit-confirmation-saved": "ستاسې سمون خوندي شو.", "edit-already-exists": "په دې نوم يو نوی مخ جوړ نه شو.\nپدې نوم د پخوا نه يو مخ شته.", "defaultmessagetext": "تلواليزه پيغام متن", + "content-failed-to-parse": "په منځپانګې کې د ناسم کولو ناکامي $2 د موډل لپاره $1: $3", + "invalid-content-data": "د ناباوره منځپانګې ډاټا", + "content-not-allowed-here": "\"$1\" په پاڼه کې منځپانګې ته اجازه نشته [[$2]]", + "editpage-invalidcontentmodel-text": "د منځپانګې موډيول \"$1\" ملاتړ ندی شوی.", + "editpage-notsupportedcontentformat-title": "د منځپانګې بڼه نده ملاتړ شوې", "content-model-wikitext": "ويکي متن", "content-model-text": "ساده متن", "content-model-javascript": "جاواسکرېپټ", "content-model-css": "CSS", "content-model-json": "JSON", "content-json-empty-object": "تش څيز", + "content-json-empty-array": "خالي صف", "duplicate-args-category": "هغه مخونه چې دوه اړخیزه استدلالونه د کينډۍ په خبرتيا کي کاروي", "duplicate-args-category-desc": "دا هغه مخ دی چې د کينډيو استدعاگانې لري او دوه ځلي آرگومنټونه کاروي، لکه <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> يا <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.", + "expensive-parserfunction-category": "د ډیری ګران ګرد پارسیر فعالیت کاروونکي مخونه", "post-expand-template-inclusion-warning": "'''گواښنه:''' دا کينډۍ د خپل ټاکلي بريد نه ډېره لويه ده.\nځينې کينډۍ به په کې گډې نه شي.", "post-expand-template-inclusion-category": "هغه مخونه چې په کې د کارېدلو کينډيو شمېر له ټاکلې کچې ډېر دی", "post-expand-template-argument-warning": "'''گواښنه:''' دا مخ لږ تر لږه د يوې کينډۍ عاملين لري چې بې حده لوی دی.\nدا عاملين ړنگ شول.", "post-expand-template-argument-category": "هغه مخونه چې د کينډۍ ړنگ شوي عاملين لري.", + "parser-template-loop-warning": "د کينډۍ پته ترلاسه شوه: [[$1]]", + "template-loop-category": "مخونه له کينډۍ پته ترلاسه شوو سره", "undo-failure": "د منازعې منځنۍ برخې د بدلونونو له امله دا سمون ندی رد شوی.", "undo-norev": "دا سمون ناکړل کېدای نه شي دا ځکه چې دا سمون نشته او يا هم ړنگ شوی.", "viewpagelogs": "د دې مخ يادښتونه کتل", @@ -793,6 +818,7 @@ "mergehistory-list": "د اخږلو وړ سمون پېښليک", "mergehistory-go": "اخږلو وړ سمونونه ښکاره کول", "mergehistory-submit": "بڼې سره يوځای کول", + "mergehistory-empty": "هیڅ بدلون نه شي کیدای.", "mergehistory-done": "د $1 $3 {{PLURAL:$3|بڼه|بڼې}} په برياليتوب سره و [[:$2]] کې {{PLURAL:$3|واخږل شو|واخږل شول}}.", "mergehistory-fail-bad-timestamp": "وخت ټاپه ناسمه ده.", "mergehistory-fail-invalid-source": "د مخ سرچينې ناباوره دي.", @@ -967,17 +993,20 @@ "prefs-editor": "سمونگر", "prefs-preview": "مخليدنه", "prefs-advancedrc": "پرمختللې خوښنې", + "prefs-opt-out": "د پرمختګونو څخه لرې کول", "prefs-advancedrendering": "پرمختللې خوښنې", "prefs-advancedsearchoptions": "پرمختللې خوښنې", "prefs-advancedwatchlist": "پرمختللې خوښنې", "prefs-displayrc": "د ښکارېدو خوښنې", "prefs-displaywatchlist": "د ښکارېدنې خوښنې", + "prefs-tokenwatchlist": "ټوکن", "prefs-diffs": "توپيرونه", - "userrights": "د کارن رښتو سمبالښت", + "userrights": "د کارن حقوق", "userrights-lookup-user": "کارن ډلې سمبالول", "userrights-user-editname": "يو کارن نوم ورکړئ:", - "editusergroup": "{{GENDER:$1|کارن}} ډلې سمول", + "editusergroup": "د کارن ګروپ لوډ کړي", "editinguser": "د <strong>[[User:$1|$1]]</strong> {{GENDER:$1|کارن}} رښتې بدلول $2", + "viewinguserrights": "د <strong>[[User:$1|$1]]</strong> {{GENDER:$1|کارن}} رښتې بدلول $2", "userrights-editusergroup": "{{GENDER:$1|کارن}} ډلې سمول", "userrights-viewusergroup": "د{{GENDER:$1|کارن}} ګروپونه ښکاره کړي", "saveusergroups": "{{GENDER:$1|کارن}} ډلې خوندي کول", @@ -990,6 +1019,16 @@ "userrights-changeable-col": "هغه ډلې چې تاسې يې بدلولی شی", "userrights-unchangeable-col": "هغه ډلې چې تاسې يې نه شی بدلولی", "userrights-irreversible-marker": "$1*", + "userrights-expiry-current": "پای نېټه $1", + "userrights-expiry-none": "پای ته نه رسیږي", + "userrights-expiry": "د پای نېټه:", + "userrights-expiry-existing": "د پای نیټه وخت: $3، $2", + "userrights-expiry-othertime": "بل وخت:", + "userrights-expiry-options": "Û± ورځ:1 day,Û± اوونۍ:1 week,Û± مياشت:1 month,Û³ مياشتې:3 months,Û¶ مياشتې:6 months,Û± کال:1 year", + "userrights-invalid-expiry": "د ډلې لپاره د ختمولو \"$1\" ناباوره دی.", + "userrights-expiry-in-past": "د ډلې لپاره د ختمولو وخت \"$1\" په تیرو کې دی.", + "userrights-cannot-shorten-expiry": "تاسو نشئ کولی چې په ګروپ کې غړیتوب پای ته ورسوي \"$1\". هغه کارنان ددا ګروپ په اضافه کولو او لیږدولو اجازه لري چي پرميشن لري..", + "userrights-conflict": "شخړه د کارن د حقونو بدلونونه! مهرباني وکړئ خپل بدلونونه بیاکتنه تایید کړئ.", "group": "ډله:", "group-user": "کارنان", "group-autoconfirmed": "تاييد شوي کارنان", @@ -1015,12 +1054,17 @@ "right-createpage": "مخونه جوړول (هغه چې د خبرو اترو مخونه نه دي)", "right-createtalk": "د خبرو اترو مخونه جوړول", "right-createaccount": "نوي گڼونونه جوړول", + "right-autocreateaccount": "د خپل شخصي کارن حساب سره په اتوماتيک ډول ننوتل", "right-minoredit": "سمونونه واړه په نخښه کول", "right-move": "مخونه لېږدول", "right-move-subpages": "مخونه د خپلو څېرمه مخونو سره لېږدول", + "right-move-rootuserpages": "وګرځوي روټ کارن مخونه", "right-move-categorypages": "د وېشنيزې مخونه لېږدول", "right-movefile": "دوتنې لېږدول", + "right-suppressredirect": "د مخ ګرځونه بغير له پخوانۍ نوم ځای پر ځای کولو", "right-upload": "دوتنې پورته کول", + "right-reupload": "د همدې دوتنې دپاسه ليکل", + "right-reupload-own": "اوسنۍ دوتنې له ځانه سره پورته کړي", "right-upload_by_url": "د يو URL نه دوتنې پورته کول", "right-writeapi": "د API کښنې کارېدنه", "right-delete": "مخونه ړنگول", @@ -1050,7 +1094,15 @@ "right-userrights-interwiki": "په نورو ويکي گانو د نورو کارنانو کارن-رښتې سمول", "right-siteadmin": "توکبنسټ کولپول او پرانيستل", "right-sendemail": "نورو کارنانو ته برېښليک لېږل", + "grant-generic": "د \"$1\" د حقوقو بنډل", + "grant-group-page-interaction": "د پاڼو سره اړیکه", + "grant-group-file-interaction": "د ميډيا سره اړیکه", + "grant-group-watchlist-interaction": "د خپل کتنلړ سره مرکه وکړئ", "grant-group-email": "برېښليک لېږل", + "grant-group-high-volume": "د لوړ حجم فعالیت ترسره کړئ", + "grant-group-customization": "حساس کول او غوره توبونه", + "grant-group-administration": "اداري کړنو ترسره کول", + "grant-group-private-information": "ستاسو په اړه شخصي معلوماتو ته لاسرسۍ", "grant-group-other": "بېلابېل فعاليتونه", "grant-blockusers": "په کارنانو بنديز لگول او بنديز ليرې کول", "grant-createaccount": "گڼونونه جوړول", @@ -1062,10 +1114,19 @@ "grant-editmywatchlist": "خپل کتنلړ سمول", "grant-editpage": "شته مخونه سمول", "grant-editprotected": "ژغورلي مخونه سمول", + "grant-highvolume": "د لوړ حجم سمون", + "grant-oversight": "د کاروونکو پټول د هغوي د سمونو سره", + "grant-patrol": "د مخونو بدلونونه ګزمه کړي", + "grant-privateinfo": "شخصي معلوماتو ته لاسرسۍ", + "grant-protect": "د مخونو ژغورنه او ژغورنه لري کونه", + "grant-rollback": "د مخونو بدلونونه راګرځونکي", "grant-sendemail": "نورو کارنانو ته برېښليک لېږل", + "grant-uploadeditmovefile": "پورته کول، د دوتنو لیږد او بدلونه", "grant-uploadfile": "نوې دوتنې پورته کول", "grant-basic": "بنسټيزې رښتې", + "grant-viewdeleted": "ړنګ شوي دوتنې او مخونه کتونکي", "grant-viewmywatchlist": "خپل کتنلړ کتل", + "grant-viewrestrictedlogs": "محدود شوي ننوتلي ثبتونې وګورئ", "newuserlogpage": "د کارن-نوم د جوړېدو يادښت", "newuserlogpagetext": "دا د کارن-نوم د جوړېدو يادښت دی", "rightslog": "د کارن رښتو يادښت", @@ -1124,12 +1185,15 @@ "recentchanges-label-plusminus": "د بايټونو د شمېر له مخې د مخ د بدلون کچه", "recentchanges-legend-heading": "<strong>لنډونونه:</strong>", "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|د نويو مخونو لړليک]] هم وگورئ)", - "recentchanges-legend-plusminus": "(<em>±123</em>)", + "recentchanges-legend-plusminus": "(<em>±۱۲۳</em>)", "recentchanges-submit": "ښکاره کول", "rcfilters-tag-remove": "لرې کړئ'$1'", + "rcfilters-legend-heading": "<strong>د لنډیزونو لړليک:</strong>", + "rcfilters-other-review-tools": "د بیاکتنې نور وسايل", "rcfilters-activefilters": "فعال فيلټرونه", "rcfilters-advancedfilters": "پرمختللي فلټرونه", "rcfilters-limit-title": "د ښودلو لپاره بدلونونه", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|بدلونونه|$1 بدلونونه}}، $2", "rcfilters-days-title": "وروستي ورځي", "rcfilters-hours-title": "وروستي ساعتونه", "rcfilters-days-show-days": "$1 {{PLURAL:$1|day|ورځې}}", @@ -1137,6 +1201,7 @@ "rcfilters-highlighted-filters-list": "لوړ شوی: $1", "rcfilters-quickfilters": "خوندي شوی فلټرونه", "rcfilters-quickfilters-placeholder-title": "هيڅ فيلټر نه دي صفت سوي", + "rcfilters-quickfilters-placeholder-description": "ددي لپاره چي د خپل فلټر امستنې سم کړي، او بيايې په دوهم پړاو کې وکاروي، د فعال فلټر ساحې لاندې د بکمارک په نښه کېکاږئ.", "rcfilters-savedqueries-defaultlabel": "خوندي شوی فيلټرونه", "rcfilters-savedqueries-rename": "نوم بدلول", "rcfilters-savedqueries-setdefault": "د فرض په ډول کښېږدي.", @@ -1148,15 +1213,89 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "د فرض په ډول د فيلټر جوړول", "rcfilters-savedqueries-cancel-label": "ناگارل", "rcfilters-savedqueries-add-new-title": "د امستنې اوسنۍ فيلټر خوندي کړي", + "rcfilters-search-placeholder": "د فلټر بدلونونه (د مینو کارول یا د فلټر نوم لټونه)", + "rcfilters-invalid-filter": "غلط فلټر", + "rcfilters-empty-filter": "هيڅ فعال فلټر نشته. ټولي سمونې ښکاره شوي.", "rcfilters-filterlist-title": "چاڼگران", + "rcfilters-filterlist-whatsthis": "دوي سنګه کار کوي؟", "rcfilters-highlightmenu-title": "يو رنګ وټاکۍ", + "rcfilters-filter-editsbyself-label": "بدلونونه ستاسو لخوا", + "rcfilters-filter-editsbyself-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-unregistered-label": "ناثبت", + "rcfilters-filter-user-experience-level-unregistered-description": "هغه سمونکي چې ننوتلي نه وو.", "rcfilters-filter-user-experience-level-newcomer-label": "نوي راغلي", + "rcfilters-filter-user-experience-level-newcomer-description": "ثبت شوي سمونکي چې د ١٠ څخه لږ سمونونه یا څلور ورځني فعالیت يي لرلي وي.", "rcfilters-filter-user-experience-level-learner-label": "زده کوونکي", + "rcfilters-filter-user-experience-level-learner-description": "ثبت شوي سمونکي د کومو تجربې چي په منځ کې راځي \"نوي راتلونکې \" او \"تجربه لرونکي کارنان.\"", + "rcfilters-filter-user-experience-level-experienced-label": "تجربه لرونکي کارنان", + "rcfilters-filter-user-experience-level-experienced-description": "ثبت شوي سمونکي چي تر ÛµÛ°Û° ډير سمونونه او ٣٠ ورځني فعالیت يي لرلي وي.", + "rcfilters-filtergroup-automated": "اتوماتیک سموني", "rcfilters-filter-bots-label": "روباټ", + "rcfilters-filter-bots-description": "هغه آثار چې د اتوماتیک وسیلو لخوا جوړ شوي.", + "rcfilters-filter-humans-label": "انسان (نه بوټ)", + "rcfilters-filter-humans-description": "سمونې چي د انساني سمونوالو لخوا جوړې شوي دي.", + "rcfilters-filtergroup-reviewstatus": "د بیاکتنې حالت", "rcfilters-filter-patrolled-label": "ګزمې شوی", + "rcfilters-filter-patrolled-description": "سمونې چي د ګزمې په توګه په نښه شوي دي.", + "rcfilters-filter-unpatrolled-label": "بي ګزمې شوي", + "rcfilters-filter-unpatrolled-description": "سمونې چي د ګزمې په توګه نه دي په نښه شوي.", + "rcfilters-filtergroup-significance": "ارزښت", + "rcfilters-filter-minor-label": "وړوکي سمونونه", + "rcfilters-filtergroup-watchlist": "د کتنلړ مخونه", + "rcfilters-filter-watchlist-watched-label": "په کتنلړ کي", + "rcfilters-filter-watchlist-notwatched-label": "په کتنلړ کې ندی", + "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": "د بدلون ډول", + "rcfilters-filter-pageedits-label": "د مخ سمونونه", + "rcfilters-filter-pageedits-description": "د ويکي منځپانګې سره سمونونه، خبرې اترې، د کتنې تفصيلات ...", + "rcfilters-filter-newpages-label": "مخ جوړونې", + "rcfilters-filter-newpages-description": "هغه سمونې چي نوي مخونه جوړي.", + "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-filter-lastrevision-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-namespaces-tooltip": "د تشيال له لارې د فلټر پایلې", + "rcfilters-view-tags-tooltip": "د تيګونو په کارولو د فلټر پايلې", + "rcfilters-view-return-to-default-tooltip": "د منځنۍ فلټر مینځ ته راوګرځئ", + "rcfilters-view-tags-help-icon-tooltip": "د ټيګيډ سمونونو په اړه نور معلومات ترلاسه کړئ", + "rcfilters-liveupdates-button": "په ژوندۍ بڼه اوسمهالونې", + "rcfilters-liveupdates-button-title-on": "په ژوندۍ بڼه اوسمهالونه بنده کړئ", + "rcfilters-liveupdates-button-title-off": "نوی بدلونونه لکه څنګه چې پیښیږي ښکاره کړئ", + "rcfilters-watchlist-markseen-button": "ټول بدلونونه لکه ليدل شوي په نښه کړئ", + "rcfilters-watchlist-edit-watchlist-button": "خپل د کتنلړ مخونه سم کړي", + "rcfilters-watchlist-showupdated": "په هغه پاڼو کې بدلونونه چې تاسو نه دي لیدلي، له دې چې بدلونونه پېل شوي د <strong>روڼ</strong> سوليډ نښانو سره.", + "rcfilters-preference-label": "د وروستي بدلونونو اصلاح شوی نسخه پټ کړئ", + "rcfilters-preference-help": "د ٢٠١٧ د انټرفیس بیرته راګرځولو رولونه او ټول وسايل وروسته بیا او وروسته.", + "rcfilters-filter-showlinkedfrom-label": "له لینک سره په تړلي پاڼې کې بدلونونه وښایئ", + "rcfilters-filter-showlinkedfrom-option-label": "مخونه ښکاره کول تړل شویو پاڼو کې <strong>له</strong> يو مخ څخه", + "rcfilters-filter-showlinkedto-label": "په تړلو پاڼو کې بدلونونه ښکاره کړئ", + "rcfilters-filter-showlinkedto-option-label": "بدلونونو سره تړل شوي مخونو کې ښودل <strong>TO</strong> د يو مخ", + "rcfilters-target-page-placeholder": "د مخ نوم ورکړئ", "rcnotefrom": "دلته لاندې د <strong>$3, $4</strong> (څخه <strong>$1</strong> {{PLURAL:$5|راپدېخوا پېښ شوي بدلونونه|ښکاري}}).", + "rclistfromreset": "د نیټې ټاکنې انتخاب", "rclistfrom": "نوي بدلونونه چې له $3، $2 څخه پيلېږي ښکاره کول", "rcshowhideminor": "وړې سمونې $1", "rcshowhideminor-show": "ښکاره کول", @@ -1201,7 +1340,7 @@ "recentchangeslinked-feed": "اړونده بدلونونه", "recentchangeslinked-toolbox": "اړونده بدلونونه", "recentchangeslinked-title": "\"$1\" ته اړونده بدلونونه", - "recentchangeslinked-summary": "دا د هغه بدلونونو لړليک دی چې وروستۍ ځل په تړن لرونکيو مخونو کې د يوه ځانگړي مخ (او يا هم د يوې ځانگړې وېشنيزې غړو) نه رامېنځ ته شوي.\n[[Special:Watchlist|ستاسې د کتنلړ]] مخونه په '''زغرد ليک''' کې ښکاري.", + "recentchangeslinked-summary": "دا د هغه بدلونونو لړليک دی چې وروستۍ ځل په تړن لرونکيو مخونو کې د يوه ځانگړي مخ (او يا هم د يوې ځانگړې وېشنيزې غړو) نه رامېنځ ته شوي. [[Special:Watchlist|ستاسې د کتنلړ]] مخونه په <strong>زغرد ليک</strong> کې ښکاري.", "recentchangeslinked-page": "د مخ نوم:", "recentchangeslinked-to": "د ورکړل شوي مخ پر ځای د اړونده تړلي مخونو بدلونونه ښکاره کول", "recentchanges-page-added-to-category": "[[:$1]] وېشنيزې کې ورگډ شو", @@ -1301,8 +1440,36 @@ "backend-fail-read": "د \"$1\" دوتنه نه شي لوستل کېدای.", "backend-fail-create": "د \"$1\" په دوتنه کې نور څه و نه ليکل شول.", "zip-wrong-format": "ځانگړې شوې دوتنه يوه ZIP دوتنه نه وه.", + "uploadstash": "پورته کول سټش", + "uploadstash-clear": "پاک شوي دوتنې", + "uploadstash-nofiles": "تاسو جعلي فایلونه نلرئ.", + "uploadstash-badtoken": "د دې عمل تعقیب ناکام شو، شاید ممکن چې ستاسو د سمون اعتباري اسناد پای ته ورسیږي. مهرباني وکړئ بیا هڅه وکړئ.", + "uploadstash-errclear": "د دوتنو پاکول ناکام شول.", "uploadstash-refresh": "د دوتنو لړليک بياتازه کول", + "uploadstash-thumbnail": "تڼۍ وګوره", + "uploadstash-exception": "د زیرمه د بار د ژغورلو توان نلري ($1): \"$2\".", + "uploadstash-bad-path": "نښان نشته", + "uploadstash-bad-path-invalid": "د پوست پېژند سم نه دی.", + "uploadstash-bad-path-unknown-type": "ناڅرگنده ډول \"$1\".", + "uploadstash-bad-path-unrecognized-thumb-name": "ناپیژندل شوی ګوته نوم.", + "uploadstash-bad-path-no-handler": "د $1 د ښکاره کولو لپار دوتنه $2 اجرایی وړ ونه موندل شي.", + "uploadstash-bad-path-bad-format": "کیلي \"$1\" په مناسبه بڼه نده.", + "uploadstash-file-not-found": "په \"$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\nنښاني= $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": "تمدید غټ دی.", + "uploadstash-zero-length": "دوتنه صفر اوږدوالی لري.", + "invalid-chunk-offset": "د ناباوره ټوټې ټکی", "img-auth-accessdenied": "لاسرسی رد شو", + "img-auth-nopathinfo": "PATH_INFO موجود ندي.\nستا پالنګر د دې ارزښت ردولو لپاره ندی ټاکل شوی.\nدا کیدای شي د CGI او له مخې د img_auth ملاتړ وکړي.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization وګوري.", "img-auth-nofile": "د $1 په نوم کومه دوتنه نشته.", "img-auth-streaming": "سټريمينګ \"$1\".", "http-invalid-url": "ناسم URL: $1", @@ -1438,7 +1605,7 @@ "fewestrevisions": "لږ مخليدل شوي مخونه", "nbytes": "$1 {{PLURAL:$1|بايټ|بايټونه}}", "ncategories": "$1 {{PLURAL:$1|وېشنيزه|وېشنيزې}}", - "ninterwikis": "$1 {{PLURAL:$1|انټرویکی|انټرویکی}}", + "ninterwikis": "$1 {{PLURAL:$1|interwiki|انټرویکی}}", "nlinks": "$1 {{PLURAL:$1|تړنه|تړنې}}", "nmembers": "$1 {{PLURAL:$1|غړی|غړي}}", "nmemberschanged": "$1 → $2 {{PLURAL:$2|غړی|غړي}}", @@ -1528,6 +1695,9 @@ "apisandbox-results": "پايلې", "apisandbox-request-url-label": "د URL غوښتنه کول:", "apisandbox-request-time": "د غوښتنې وخت: {{PLURAL:$1|$1 م.Ø«}}", + "apisandbox-continue": "پرله پورې", + "apisandbox-continue-clear": "سپينول", + "apisandbox-multivalue-all-values": "د $1 (ټول ارزښتونه)", "booksources": "د کتاب سرچينې", "booksources-search-legend": "د کتابي سرچينو پلټنه", "booksources-isbn": "ISBN:", @@ -1601,6 +1771,8 @@ "listgrouprights-namespaceprotection-header": "د نومتشيال محدوديتونه", "listgrouprights-namespaceprotection-namespace": "نوم-تشيال", "listgrouprights-namespaceprotection-restrictedto": "د کارن سمون ترسره کولو رښته(رښتې)", + "listgrants": "منلې", + "listgrants-grant": "منلې", "listgrants-rights": "رښتې", "trackingcategories": "موندونکې وېشنيزې", "trackingcategories-summary": "په دې مخ کې هغه موندونکې وېشنيزې چې په اتوماتيک ډول د مېډياويکي ساوترې لخوا ډکېږي، د لړليک په توگه راغلي. د وېشنيزو نومونه د اړونده غونډال پيغامونو په بدلون سره چې د {{ns:8}} په نومتشيال کې دي، د بدلېدلو وړتيا لري.", @@ -1694,7 +1866,7 @@ "deletepage": "مخ ړنگول", "confirm": "تاييد", "excontent": "د مخ مېنځپانگه دا وه: \"$1\"", - "excontentauthor": "د مخ مېنځپانگه دا وه: \"$1\" (او يواځينی ونډه وال \"[[Special:Contributions/$2|$2]]\" وه)", + "excontentauthor": "د مخ مېنځپانگه دا وه: «$1» او يواځينی ونډه وال «[[Special:Contributions/$2|$2]] ([[User talk:$2|خبرې اترې]])» وه", "delete-confirm": "\"$1\" ړنگول", "delete-legend": "ړنگول", "historywarning": "گواښنه:''' دا مخ چې تاسې يې د ړنگېدو تکل لرئ نژدې $1 {{PLURAL:$1|بڼه|بڼې}} پېښليک لري:", @@ -1714,8 +1886,9 @@ "deleting-backlinks-warning": "گواښنه:''' دا مخ چې تاسې يې ړنگوی د [[Special:WhatLinksHere/{{FULLPAGENAME}}|نورو مخونو]] سره تړلی او يا هم په نورو مخونو کې نغاړل شوی دی.", "rollbacklink": "په شابېول", "rollbacklinkcount": "$1 {{PLURAL:$1|سمون|سمونونه}} پرشابېول", - "editcomment": "د سمون لنډيز دا و: \"''$1''\".", + "editcomment": "د سمون لنډيز دا وو: \"''$1''\".", "changecontentmodel-title-label": "مخ سرليک", + "changecontentmodel-model-label": "د نوي مېنځپانگې موډل", "changecontentmodel-reason-label": "سبب:", "changecontentmodel-submit": "بدلول", "logentry-contentmodel-change-revertlink": "په څټ گرځول", @@ -1813,11 +1986,11 @@ "sp-contributions-newbies-sub": "د نوو گڼونونو لپاره", "sp-contributions-newbies-title": "د نويو گڼونونو لپاره د کارن ونډې", "sp-contributions-blocklog": "د بنديز يادښت", - "sp-contributions-deleted": "ړنگېدلې کارن ونډې", + "sp-contributions-deleted": "د ړنگ شوي {{GENDER:$1|کارن}} ونډې", "sp-contributions-uploads": "پورته کېدنې", "sp-contributions-logs": "يادښتونه", "sp-contributions-talk": "خبرې اترې", - "sp-contributions-userrights": "د کارن رښتو سمبالښت", + "sp-contributions-userrights": "د {{GENDER:$1|کارن}} رښتو سمبالښت", "sp-contributions-blocked-notice": "دم مهال په دې کارن بنديز لگېدلی.\nد بنديز يادښت تازه مالومات په لاندې توگه دي:", "sp-contributions-search": "د ونډو پلټنه", "sp-contributions-username": "IP پته يا کارن-نوم:", @@ -1841,10 +2014,11 @@ "whatlinkshere-hideimages": "د دوتنې تړنې $1", "whatlinkshere-filters": "چاڼگرونه", "whatlinkshere-submit": "ورځه", + "autoblockid": "خپلواک بنديز #$1", "block": "په کارن بنديز لگول", "unblock": "کارن له بنديزه وېستل", "blockip": "په {{GENDER:$1|کارن}} بنديز لگول", - "blockiptext": "د لاندينۍ فورمې په کارولو سره تاسې يو کارن او يا هم يوې ځانگړې IP پتې باندې د ليکلو بنديزونه لگولی شی. \nدا بايد د پوهې سره دښمنۍ او ورانکارۍ د مخنيولو په تکل او د پښتو ويکيپېډيا د [[{{MediaWiki:Policy-url}}|تگلارې]] سره سم پلي شي.\nد بنديز لپاره مو يو ځانگړی دليل لاندې روښانه کړئ (د ساري په توگه، هغه مخونو ښکاره کول چې ورانکاري په کې ترسره شوې).", + "blockiptext": "د لاندينۍ فورمې په کارولو سره تاسې يو کارن او يا هم يوې ځانگړې IP پتې باندې د ليکلو بنديزونه لگولی شی. \nدا بايد د پوهې سره دښمنۍ او ورانکارۍ د مخنيولو په تکل او د پښتو ويکيپېډيا د [[{{MediaWiki:Policy-url}}|تگلارې]] سره سم پلي شي.\nد بنديز لپاره مو يو ځانگړی دليل لاندې روښانه کړئ (د ساري په توگه، هغه مخونو ښکاره کول چې ورانکاري په کې ترسره شوې).\n[https://ps.wikipedia.org/wiki/Classless_Inter-Domain_Routing سي ډي اي ار] نخښه; the آر اجازه ورکول رینج دی /$1 لپاره د اي پي وي Û´ او /$2 لپاره د اي پي وي Û¶.", "ipaddressorusername": "IP پته يا کارن نوم", "ipbexpiry": "د پای نېټه:", "ipbreason": "سبب:", @@ -1859,10 +2033,12 @@ "ipbhidename": "کارن-نوم له سمون او لړليکونو پټول", "ipbwatchuser": "د دې کارن د خبرو اترو مخ او کارن مخ کتل", "ipb-disableusertalk": "د بنديز لگېدو سره دې د کارن د خبرو اترو مخ د سمولو مخنيوی هم پلي شي", + "ipb-change-block": "د کارن څخه بنديز لرې کول ددغو امستنې له لارې", "ipb-confirm": "د بنديز تاييد", "badipaddress": "ناسمه IP پته", "blockipsuccesssub": "بنديز په برياليتوب سره ولگېده", "blockipsuccesstext": "په [[Special:Contributions/$1|$1]] بنديز لگېدلی.<br />\nد بنديزونو د څارلو لپاره [[Special:BlockList|بنديز لړليک]] وگورۍ.", + "ipb-blockingself": "تاسو پر خپل ځان د بنديز لګولو په حال کې یاست! ایا تاسو ډاډه یاست چې تاسو دا کار کول غواړئ؟", "ipb-edit-dropdown": "د بنديز سببونه سمول", "ipb-unblock-addr": "له $1 بنديز ليرې کول", "ipb-unblock": "له يوه کارن-نوم يا IP پتې بنديز ليري کول", @@ -1876,16 +2052,24 @@ "unblocked-range": "له $1 بنديز ليرې شو", "unblocked-ip": "له [[Special:Contributions/$1|$1]] څخه بنديز ليرې شو.", "blocklist": "بنديز لگېدلي کارنان", + "autoblocklist": "خپلواک بنديزونه", "autoblocklist-submit": "پلټل", + "autoblocklist-legend": "د خپلواک بنديزونو لړليک", + "autoblocklist-localblocks": "ځایي {{PLURAL:$1|خپلواک بنديز|خپلواک بنديزونه}}", + "autoblocklist-total-autoblocks": "د خپلواک بنديز ټول لړليک: $1", + "autoblocklist-empty": "د بنديز لړليک تش دی", + "autoblocklist-otherblocks": "نور {{PLURAL:$1|خپلواک بنديز|خپلواک بنديزونه}}", "ipblocklist": "بنديز لگېدلي کارنان", "ipblocklist-legend": "يو بنديز شوی کارن موندل", "blocklist-userblocks": "گڼون بنديزونه پټول", "blocklist-tempblocks": "لنډمهاله بنديزونه پټول", "blocklist-addressblocks": "يواځې آی پي بنديزونه پټول", + "blocklist-rangeblocks": "پټ اندازه بنديزونه", "blocklist-timestamp": "وخت ټاپه", "blocklist-target": "موخه", "blocklist-expiry": "پای نېټه", "blocklist-by": "بنديز لگونکی پازوال", + "blocklist-params": "بنديز پاراميټرونه", "blocklist-reason": "سبب", "ipblocklist-submit": "پلټل", "ipblocklist-localblock": "سيمه ايز بنديز", @@ -1893,10 +2077,12 @@ "infiniteblock": "نامحدوده", "expiringblock": "په $1 نېټه، $2 بجو پای ته رسېږي", "anononlyblock": "يواځې ورکنومی", + "noautoblockblock": "خپلواک بنديز ترسره نشو", "createaccountblock": "په گڼون جوړولو بنديز لگېدلی", "emailblock": "پر برېښليک بنديز ولگېد", "blocklist-nousertalk": "د خبرواترو خپل مخ نه شی سمولای", "ipblocklist-empty": "د بنديز لړليک تش دی", + "ipblocklist-no-results": "پر غوښتل شوي آي پي پتې باندې بنديز نه دي لګول شوي.", "blocklink": "بنديز لگول", "unblocklink": "بنديز لرې کول", "change-blocklink": "د بنديز بدلون", @@ -1911,9 +2097,12 @@ "unblocklogentry": "بنديز ليرې شو $1", "block-log-flags-anononly": "يواځې ورکنومي کارنان", "block-log-flags-nocreate": "د گڼون جوړول ناچارن شوی", + "block-log-flags-noautoblock": "خپلواک بنديز ترسره نشو", "block-log-flags-noemail": "ددې برېښليک مخه نيول شوی", "block-log-flags-nousertalk": "خپل د خبرو اترو مخ نه شي سمولای", "block-log-flags-hiddenname": "پټ کارن-نوم", + "ipb_expiry_invalid": "د پاي ته رسيدو وخت غلط دی.", + "ipb_expiry_old": "د پای ته رسېدو وخت په تېرمهال کې دی.", "ipb_already_blocked": "پر \"$1\" د پخوا نه بنديز دی", "ipb-needreblock": "پر $1 د پخوا نه بنديز لگېدلی.\nآيا تاسې د امستنو بدلول غواړۍ؟", "ipb-otherblocks-header": "{{PLURAL:$1|بل بنديز|نور بنديزونه}}", @@ -2003,8 +2192,10 @@ "imported-log-entries": "$1 {{PLURAL:$1|يادښتليک راوړل شوی|يادښتليکونه راوړل شوي}}.", "importcantopen": "واردونکې دوتنه و نه پرانيستل شوه.", "importbadinterwiki": "ناسمه ويکيخپلمنځي تړنه", + "importsuccess": "راليږل بشپړ شوه!", + "import-noarticle": "د رالېږدولو لپاره مخونه نشته.", "import-upload": "د XML اومتوک پورته کول", - "import-token-mismatch": "د اومتوک غونډېدنه له لاسه وتلې.\nلطفاً بيا يې وآزمايئ.", + "import-token-mismatch": "د اومتوک غونډېدنه له لاسه وتلې.\n\nتاسو شاید په نښه شوي وي. لطفا ډاډ ترلاسه کړئ چې ته ننوځئ او بیا بیا هڅه وکړه.\nکه تاسو د سیسټم څخه یو ځل بیا پیغام ترلاسه کړئ چي [[Special:UserLogout|ووځي]]، بيا ننوځي، او ډاډه کړئ چې ستاسو براؤزر اجازه لري چې د دې سایټ څخه کوکیز ترلاسه کړي.", "importlogpage": "د واردولو يادښت", "import-logentry-upload-detail": "$1 {{PLURAL:$1|بڼه|بڼې}} راولېږدېدې", "javascripttest": "د جاوا سکرېپټ آزمېښت", @@ -2080,6 +2271,7 @@ "siteuser": "د {{SITENAME}} کارن $1", "anonuser": "د {{SITENAME}} ورکنومی کارن $1", "lastmodifiedatby": "دا مخ وروستی ځل د $3 لخوا په $2، $1 بدلون موندلی.", + "othercontribs": "نور کار پر بنسټ", "others": "نور", "siteusers": "د {{SITENAME}} {{PLURAL:$2|کارن|کارنان}} $1", "anonusers": "د {{SITENAME}} {{PLURAL:$2|ورکنومی کارن|ورکنومي کارنان}} $1", @@ -2095,6 +2287,7 @@ "pageinfo-length": "د مخ اوږدوالی (د تورو له مخې)", "pageinfo-article-id": "د مخ پېژند", "pageinfo-language": "د مخ د مېنځپانگې ژبه", + "pageinfo-language-change": "بدلون", "pageinfo-content-model": "د مخ مېنځپانگې جوړښت", "pageinfo-content-model-change": "بدلول", "pageinfo-robot-policy": "ليکلړ اوډنه د روباټونو لخوا", @@ -2117,7 +2310,7 @@ "pageinfo-recent-authors": "د بېلابېلو ليکوالو وروستنی شمېر", "pageinfo-magic-words": "کوډگريز {{PLURAL:$1|ويي|وييونه}} ($1)", "pageinfo-hidden-categories": "{{PLURAL:$1|پټه وېشنيزه|پټې وېشنيزې}} ($1)", - "pageinfo-templates": "ترجمه {{PLURAL:$1|کينډۍ|کينډۍ}} ($1)", + "pageinfo-templates": "ترجمه {{PLURAL:$1|template|کينډۍ}} ($1)", "pageinfo-toolboxlink": "د مخ مالومات", "pageinfo-redirectsto": "مخ گرځونې و", "pageinfo-redirectsto-info": "مالومات", @@ -2131,6 +2324,7 @@ "pageinfo-category-pages": "د مخونو شمېر", "pageinfo-category-subcats": "د څېرمه وېشنيزو شمېر", "pageinfo-category-files": "د دوتنو شمېر", + "pageinfo-user-id": "کارن پېژند", "markaspatrolleddiff": "دا مخ څارل شوی په نخښه کول", "markaspatrolledtext": "دا مخ څارل شوی په نخښه کول", "markedaspatrolled": "دا مخ څارل شوی په نخښه کول", @@ -2165,7 +2359,9 @@ "show-big-image-preview": "د همدې ليدنې کچه: $1.", "show-big-image-other": "{{PLURAL:$2|بل ژورليد|نورې ژورليدنې}}: $1.", "show-big-image-size": "$1 × $2 پېکسل", + "file-info-gif-looped": "حلقه", "file-info-gif-frames": "$1 {{PLURAL:$1|چوکاټ|چوکاټونه}}", + "file-info-png-looped": "حلقه", "file-info-png-repeat": "$1 {{PLURAL:$1|ځل|ځله}} وغږېده", "file-info-png-frames": "$1 {{PLURAL:$1|چوکاټ|چوکاټونه}}", "newimages": "د نوو دوتنو انځورتون", @@ -2174,14 +2370,16 @@ "newimages-legend": "چاڼگر", "newimages-label": "د دوتنې نوم (يا د دې برخه):", "newimages-showbots": "د روباټونو لخوا پورته کېدنې ښکاره کول", + "newimages-mediatype": "د رسنۍ ډول:", "noimages": "د کتلو لپاره څه نشته.", "ilsubmit": "پلټل", "bydate": "د نېټې له مخې", "sp-newimages-showfrom": "هغه نوې دوتنې چې په $1 په $2 بجو پيلېږي ښکاره کول", + "minutes-abbrev": "$1 دقیقي", "hours-abbrev": "$1 Ú¯", "seconds": "{{PLURAL:$1|$1 ثانيه|$1 ثانيې}}", "minutes": "{{PLURAL:$1|$1 دقيقه|$1 دقيقې}}", - "hours": "{{PLURAL:$1|$1 گړۍ|$1 گړۍ}}", + "hours": "{{PLURAL:$1|يو ساعت|$1 ساعتونه}}", "days": "{{PLURAL:$1|$1 ورځ|$1 ورځې}}", "weeks": "{{PLURAL:$1|$1 اونۍ|$1 اونۍ}}", "months": "{{PLURAL:$1|$1 مياشت|$1 مياشتې}}", @@ -2282,10 +2480,17 @@ "exif-objectname": "لنډ سرليک", "exif-headline": "سرليک", "exif-source": "سرچينه", + "exif-urgency": "بیړنی حالت", + "exif-fixtureidentifier": "د ثابتولو نوم", + "exif-locationdest": "ځای ښودل شوی", + "exif-locationdestcode": "د موقعیت کوډ ښودل شوی", + "exif-objectcycle": "د ورځې وخت چې رسنۍ اراده لري", "exif-contact": "د اړيکو مالومات", "exif-writer": "ليکوال", "exif-languagecode": "ژبه", + "exif-iimversion": "د IIM بڼه", "exif-iimcategory": "وېشنيزه", + "exif-iimsupplementalcategory": "ضمیمه وېشنيزه", "exif-datetimeexpires": "مه يې کاروۍ وروسته له", "exif-datetimereleased": "خپرېدلی په", "exif-identifier": "پېژندنه", @@ -2293,24 +2498,38 @@ "exif-serialnumber": "د کامرې پرله پسې شمېره", "exif-cameraownername": "د کامرې خاوند", "exif-label": "نښکه", + "exif-rating": "درجه (له Ûµ څخه بهر)", "exif-copyrighted": "د رښتو دريځ", "exif-copyrightowner": "د رښتو خاوند", "exif-usageterms": "د کارولو شرايط", "exif-pngfilecomment": "د PNG دوتنې تبصره", "exif-disclaimer": "ردادعاليک", + "exif-contentwarning": "د منځپانګي خبرداری", "exif-giffilecomment": "د GIF دوتنې تبصره", + "exif-intellectualgenre": "د توکو ډول", + "exif-subjectnewscode": "د موضوع کوډ", + "exif-scenecode": "د اي پي ثي ټي(IPTC) منظر کوډ", + "exif-event": "پيښه ښودل شوي", + "exif-organisationinimage": "سازمان ښودل شوي", + "exif-personinimage": "شخص ښودل شوی", "exif-copyrighted-true": "په رښتو سمبال", "exif-copyrighted-false": "د خپراوي د رښتو دريځ نه دی ټاکل شوی", "exif-photometricinterpretation-1": "تور او سپين (تور 0 دی)", "exif-unknowndate": "ناڅرگنده نېټه", "exif-orientation-1": "نورمال", + "exif-orientation-3": "څرخيدونکي °١٨٠", + "exif-orientation-4": "چورليځه اړونه", + "exif-orientation-5": "څرخيدونکي °٩٠ CCW او عمودی یې وویشل", + "exif-orientation-6": "څرخيدونکي °٩٠ CCW", "exif-componentsconfiguration-0": "نشته دی", "exif-exposureprogram-1": "لارښوونيز", "exif-exposureprogram-2": "نورماله پروگرام", "exif-subjectdistance-value": "$1 متره", "exif-meteringmode-0": "ناجوت", "exif-meteringmode-1": "منځالی", + "exif-meteringmode-3": "سپوټ", "exif-meteringmode-5": "مخبېلگه", + "exif-meteringmode-6": "برخيز", "exif-meteringmode-255": "نور", "exif-lightsource-0": "ناجوت", "exif-lightsource-1": "د ورځې رڼا", @@ -2318,6 +2537,10 @@ "exif-lightsource-9": "ښه هوا", "exif-lightsource-10": "ورېځ پوښلې هوا", "exif-lightsource-11": "سيوری", + "exif-lightsource-12": "ورځنې فلوروسینټ (ډالر ÛµÛ·Û°Û° – Û·Û±Û°Û° زره)", + "exif-lightsource-17": "معياري رڼا '''ا'''", + "exif-lightsource-18": "معياري رڼا '''ب'''", + "exif-lightsource-19": "معياري رڼا '''Ø«'''", "exif-lightsource-255": "د رڼا بله سرچينه", "exif-flash-fired-0": "فلش و نه ځلېده", "exif-flash-mode-3": "خپلکاره حالت", @@ -2499,6 +2722,7 @@ "version-other": "بل", "version-hooks": "کونډۍ", "version-hook-name": "کونډۍ نوم", + "version-hook-subscribedby": "سبسکرايبيدنه لخوا د", "version-version": "($1)", "version-no-ext-name": "[بې نومه]", "version-license": "مېډياويکي منښتليک", @@ -2510,6 +2734,7 @@ "version-ext-colheader-description": "څرگندونه", "version-ext-colheader-credits": "ليکوالان", "version-license-title": "د $1 منښتليک", + "version-credits-title": "د کریډیټ د $1 لپاره", "version-poweredby-credits": "دا ويکي د '''[https://www.mediawiki.org/ مېډياويکي]''' په سېک چلېږي، ټولې رښتې خوندي دي © 2001-$1 $2.", "version-poweredby-others": "نور", "version-poweredby-translators": "د translatewiki.net ژباړنان", @@ -2561,6 +2786,10 @@ "tag-filter": "[[Special:Tags|نښلن]] چاڼگر:", "tag-filter-submit": "چاڼگر", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|نښلن|نښلنونه}}]]: $2)", + "tag-mw-blank": "بسته بندي", + "tag-mw-replace": "ځايناستول", + "tag-mw-rollback": "په شابېول", + "tag-mw-undo": "ناکړل", "tags-title": "نښلنونه", "tags-tag": "نښلن نوم", "tags-display-header": "د بدلون په لړليکونو کې ښکارېدنه", @@ -2594,6 +2823,7 @@ "tags-deactivate-submit": "نافعالول", "tags-edit-title": "نښلنونه سمول", "tags-edit-manage-link": "نښلنونه مهارول", + "tags-edit-existing-tags": "شته ټګونه:", "tags-edit-existing-tags-none": "<em>هېڅ</em>", "tags-edit-new-tags": "نوي نښلنونه:", "tags-edit-add": "دا نښلنونه ورگډول:", @@ -2601,6 +2831,11 @@ "tags-edit-remove-all-tags": "(ټول نښلنونه غورځول)", "tags-edit-chosen-placeholder": "ځينې نښلنونه ټاکل", "tags-edit-reason": "سبب:", + "tags-edit-success": "بدلونونه تطبيق شوي دي.", + "tags-edit-failure": "بدلونونه کارول نشي تطبيق کيداي:\n$1", + "tags-edit-nooldid-title": "ناباوره پيښنليک ته اشاره", + "tags-edit-nooldid-text": "تاسو د کوم هدف بیا کتنه نده مشخصه کړې چې دا فعالیت ترسره کړي، یا مشخصه بیاکتنه شتون نلري.", + "tags-edit-none-selected": "مهرباني وکړئ لږترلږه یو ٹیګ غوره یا لرې کړئ.", "comparepages": "مخونه پرتلل", "compare-page1": "Û± مخ", "compare-page2": "Û² مخ", @@ -2610,6 +2845,13 @@ "compare-invalid-title": "کوم سرليک مو چې ځانگړی کړی ناسم دی.", "compare-title-not-exists": "کوم سرليک مو چې ځانگړی کړی نشته.", "compare-revision-not-exists": "کومه مخکتنه مو چې ځانگړې کړې نشته.", + "diff-form": "توپيرونه", + "diff-form-oldid": "د زړي بیاکتنې پيژندګلوي (اختیاري)", + "diff-form-revid": "د بیاکتنې د پيژندګلوي توپیر", + "diff-form-submit": "توپيرونه ښکاره کول", + "permanentlink": "تلپاتې تړنه", + "permanentlink-revid": "د بیاکتنې پيژندګلوي", + "permanentlink-submit": "بیاکتنې ته ولاړ شي", "dberr-problems": "اوبخښۍ! دم مهال دا وېبپاڼه د تخنيکي ستونزو سره مخامخ شوې.", "dberr-usegoogle": "تاسې کولای شی چې هم مهاله د گووگل له لخوا هم د پلټنې هڅه وکړۍ.", "htmlform-invalid-input": "ستاسې ځينې ورکړېينې ستونزې لري", @@ -2628,15 +2870,38 @@ "htmlform-cloner-create": "نور ورگډول", "htmlform-cloner-delete": "غورځول", "htmlform-cloner-required": "لږ تر لږه يو ارزښت ته اړتيا شته.", + "htmlform-date-placeholder": "Ú©Ú©Ú©Ú©-م م-و و", + "htmlform-time-placeholder": "HH:MM:SS", + "htmlform-datetime-placeholder": "YYYY-MM-DD HH:MM:SS", + "htmlform-date-invalid": "هغه ارزښت چې تاسو یې مشخص کړی د پېژندل شوې نیټه ندی. د YYYY-MM-DD بڼه کارولو هڅه وکړئ.", + "htmlform-time-invalid": "هغه ارزښت چې تا مشخص کړی د پېژندل شوي وخت ندی. کارولو هڅه وکړي HH:MM:SS .", + "htmlform-datetime-invalid": "هغه ارزښت چې تا مشخص شوی د پېژندل شوې نیټې او وخت ندی. د YYYY-MM-DD HH کارولو هڅه وکړئ: MM: SS بڼه.", + "htmlform-date-toolow": "هغه ارزښت چې تاسو یې مشخص کړی وي د پیل څخه مخکې د $1 نیټې څخه مخکې وي.", + "htmlform-date-toohigh": "هغه ارزښت چې تاسو یې مشخص کړی وي د $1 وروستی نیټې نیټې وروسته دی.", + "htmlform-time-toolow": "هغه ارزښت چې تاسو یې مشخص کړی وي د ترټولو ترټولو غوره وخت د $1 وخت دی.", + "htmlform-time-toohigh": "هغه ارزښت چې تاسو یې مشخص کړی وي د $1 وروستیو وختونو څخه وروسته وي.", + "htmlform-datetime-toolow": "هغه ارزښت چې تاسو یې مشخص کړئ د پیل نیټه او د $1 وخت څخه مخکې وي.", + "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> یو باوري کارن نوم نه دی.", "logentry-delete-delete": "$1 د $3 مخ {{GENDER:$2|ړنگ کړ}}", "logentry-delete-restore": "$1 د $3 مخ $4 ته {{GENDER:$2|ولېږداوه}}", "logentry-delete-revision": "$1 {{GENDER:$2|بدل شو}} لیدل د{{PLURAL:$5|a هيسټري|$5 هيسټري}} په مخ کي $3: $4", "revdelete-content-hid": "مېنځپانگه پټېدلې", + "revdelete-summary-hid": "پټ سمون لنډیز", "revdelete-uname-hid": "کارن نوم پټ شوی", "revdelete-content-unhid": "مېنځپانگه ښکاره شوی", + "revdelete-summary-unhid": "ښکاره سمون لنډیز", "revdelete-uname-unhid": "ښکاره کارن-نوم", + "revdelete-restricted": "پازوالانو ته پلي شوي محدوديتونه", + "revdelete-unrestricted": "د پازوالانو لپاره لیرې شوي بندیزونه", + "logentry-block-block": "$1 تر $5 $6 نيټې پورې پر {{GENDER:$4|$3}} باندې {{GENDER:$2|بنديز}} ولګوي", + "logentry-block-unblock": "$1 د {{GENDER:$4|$3}} څخه {{GENDER:$2|بنديز}} لري کړ", + "logentry-suppress-block": "$1 تر $5 $6 نيټې پورې پر {{GENDER:$4|$3}} باندې {{GENDER:$2|بنديز}} ولګوي", + "logentry-suppress-reblock": "$1 تر $5 $6 نيټې پورې پر {{GENDER:$4|$3}} باندې {{GENDER:$2|بنديز}} بدلون وموند", + "logentry-import-upload": "$1 $3 د دوتنې اپلوډ له لارې {{GENDER:$2|واردکړ}}", "logentry-move-move": "$1 د $3 مخ $4 ته {{GENDER:$2|ولېږداوه}}", "logentry-move-move-noredirect": "$1 پرته له دې چې يو مخ گرځونی پرېږدي له $3 څخه $4 ته مخ {{GENDER:$2|ولېږداوه}}", "logentry-move-move_redir": "$1 د $3 مخ $4 ته د مخ گرځونې له لارې {{GENDER:$2|ولېږداوه}}.", @@ -2646,30 +2911,53 @@ "logentry-newusers-create": "د $1 کارن گڼون {{GENDER:$2|جوړ شو}}", "logentry-newusers-autocreate": "د $1 گڼون په اتوماتيک ډول {{GENDER:$2|جوړ شو}}", "logentry-protect-unprotect": "$1 له $3 څخه ژغورنه {{GENDER:$2|ليرې کړه}}", + "logentry-protect-protect": "$1 د $3 مخ {{GENDER:$2|وژغوره}} $4", + "logentry-protect-protect-cascade": "$1 {{GENDER:$2|وژغورل شو}} $3 $4 [کڅوړی]", + "logentry-protect-modify": "$1 د $3 د ژغورلو کچه {{GENDER:$2|بدله کړه}} $4", + "logentry-protect-modify-cascade": "$1 د $3 ژغورنې په کچه کي {{GENDER:$2|بدلون راوست}} و $4 [کالیډیډنګ]", "logentry-rights-rights": "$1 د $3 لپاره د غړيتوب ډله له $4 څخه $5 ته {{GENDER:$2|بدله کړه}}", "logentry-rights-rights-legacy": "$1 د $3 لپاره د غړيتوب ډله {{GENDER:$2|بدله کړه}}", "logentry-upload-upload": "$1 $3 {{GENDER:$2|ورپورته يې کړ}}", "logentry-upload-overwrite": "$1 نوي ويرژن {{GENDER:$2|پورته}} سو $3", "logentry-upload-revert": "$1 $3 يې {{GENDER:$2|ورپورته کړه}}", "log-name-managetags": "د نښلن مهارولو يادښت", + "logentry-managetags-create": "$1 {{GENDER:$2|د}} د Ù¼Ú« \"$4\" جوړ کړ", "log-name-tag": "نښلن يادښت", "rightsnone": "(هېڅ)", + "rightslogentry-temporary-group": "$1 (لنډمهاله، تر $2)", + "feedback-adding": "مخ ته د نظریاتو زیاتوالی...", "feedback-back": "پر شا کېدل", + "feedback-bugcheck": "ډېر ښه! بس يوازې يي وګورئ چې دا د مخه نه ده [$1 پيژندل شوي بګ] په نامه پېژندل کېږي.", + "feedback-bugnew": "ما وکتي. نوی بگ ريپورټ کړئ", + "feedback-bugornote": "که تاسو په تفصیل سره د تخنیکي ستونزې تشریح کولو لپاره چمتو یاست هيله ده [$1 بګ ريپورټ کړي].\nکه نه، تاسو کولی شئ لاندې اسانه فورمه وکاروئ. Your comment ستاسو نظر به په پاڼه \"[$3 $2]\" کي ورګډ شي، ستاسو د کارن نوم سره.", "feedback-cancel": "ناگارل", "feedback-close": "ترسره شو", "feedback-external-bug-report-button": "د يوې تخنيکي دندې دوتنه جوړونه", + "feedback-dialog-title": "فييډبيک وسپاري", + "feedback-dialog-intro": "تاسو کولی شئ خپل نظرونه جمع کولو لپاره لاندې اسانه فورمه وکارئ. ستاسو نظر به په پاڼه \"$1\" کې اضافه شي، ستاسو د کارن نوم سره.", + "feedback-error1": "تېروتنه: ناپیژندل شوی پایله ستاسو د اي پي پتي څخه", + "feedback-error2": "تېروتنه: سمون نشو ترسره", + "feedback-error3": "تيروتنه: ستاسو د اي پي پتي څخه هيڅ ځواب ترلاسه نشو", + "feedback-error4": "تيروتنه: د ورکړل شوې نظریې سرليک د پوستولو توان نلري", "feedback-message": "پيغام:", "feedback-subject": "سکالو:", "feedback-submit": "سپارل", + "feedback-terms": "زه پوهیږم چې زما د کاروونکو استازو معلومات زما دقیق برنامه او د عملیاتي سیسټم په اړه معلومات شامل دي او زما د نظرونو سره سره به په عامه توګه شریک شي.", + "feedback-termsofuse": "زه موافق یم چې د کارونې شرایطو سره سم فييډبک وړاندیز وکړم.", + "feedback-thanks": "مننه! ستاسو نظر په مخ \"[$2 $1]\" کي پوسټ شو .", "feedback-thanks-title": "مننه!", "feedback-useragent": "کارن پلاوی:", "searchsuggest-search": "{{SITENAME}} پلټل", + "searchsuggest-containing": "مشتميليږي...", + "api-error-badtoken": "داخلي تېروتنه: خرابه نښه.", "api-error-emptypage": "د نوي تشو مخونو جوړولو پرېښنه نه شته.", + "api-error-publishfailed": "داخلي تېروتنه: پالنګر د لنډمهاله دوتنې په خپرولو کې پاتې راغلي.", + "api-error-stashfailed": "داخلي تېروتنه: پالنګر د لنډ مهال دوتنې د ذخیره کولو توان نلري.", "api-error-unknown-warning": "ناڅرگنده گواښنه: \"$1\".", "api-error-unknownerror": "ناڅرگنده تېروتنه: \"$1\".", "duration-seconds": "$1 {{PLURAL:$1|ثانيه|ثانيې}}", "duration-minutes": "$1 {{PLURAL:$1|دقيقه|دقيقې}}", - "duration-hours": "$1 {{PLURAL:$1|گړۍ|گړۍ}}", + "duration-hours": "$1 {{PLURAL:$1|hour|ساعتونه}}", "duration-days": "$1 {{PLURAL:$1|ورځ|ورځې}}", "duration-weeks": "$1 {{PLURAL:$1|اونۍ|اونۍ}}", "duration-years": "$1 {{PLURAL:$1|کال|کالونه}}", @@ -2677,33 +2965,55 @@ "duration-centuries": "$1 {{PLURAL:$1|پېړۍ|پېړۍ}}", "duration-millennia": "$1 {{PLURAL:$1|زرمه|زرمې}}", "rotate-comment": "انځور $1 {{PLURAL:$1|درجه|درجې}} گړۍ لور تاو شوی", + "limitreport-title": "د پارسیر پروفیلينګ ډاټا:", "limitreport-cputime": "سي پي يو وخت کارېدنه", "limitreport-cputime-value": "$1 {{PLURAL:$1|ثانيه|ثانيې}}", "limitreport-walltime": "اصلي وخت کارېدنه", "limitreport-walltime-value": "$1 {{PLURAL:$1|ثانيه|ثانيې}}", + "limitreport-ppvisitednodes": "د راتلونکو پروسس کوونکو شمیرې شمیرل شوي", "limitreport-ppvisitednodes-value": "$1/$2", + "limitreport-ppgeneratednodes": "د پروسس کوونکو ګرنټيد شمیرل شوي", "limitreport-ppgeneratednodes-value": "$1/$2", + "limitreport-postexpandincludesize": "د پوسټ پراخول داندازې په شمول", "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|بايټ|بايټونه}}", + "limitreport-templateargumentsize": "د کينډۍ د مسايلو کچه", "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|بايټ|بايټونه}}", + "limitreport-expansiondepth": "تر ټولو لوړه ژوره پراختیا", "limitreport-expansiondepth-value": "$1/$2", + "limitreport-expensivefunctioncount": "د قیمتي پارسير فعالیت شمیرې", "limitreport-expensivefunctioncount-value": "$1/$2", "expandtemplates": "کينډۍ غځول", + "expand_templates_intro": "په دا ځانګړي مخ کي متن پاڼه ترلاسه کیږي کوم چي په ټول ډوله مخونو کي کارول کیږي دلته دا مخ بيا بیا وده کوي. د تحلیل دندو لکه <code><nowiki>{{</nowiki>#language:…}}</code> او متغیرونه لکه <code><nowiki>{{</nowiki>CURRENTDAY}}</code> هم سره نښلوي — په واقعیت کې، د ډلو دننه هر څه. دا خپله د ميډياويکي په اړونده مرحله کولو سره ترسره کيږي.", + "expand_templates_title": "د موزوع سرليک، د {{FULLPAGENAME}} لپاره او نور:", "expand_templates_input": "ځايونکی متن:", "expand_templates_output": "پايله", + "expand_templates_xml_output": "د ایکس ایم ایل محصول", + "expand_templates_html_output": "د روو ايچ ټي ام ال څخه وتلي", "expand_templates_ok": "ښه", "expand_templates_remove_comments": "تبصرې غورځول", "expand_templates_remove_nowiki": "په پايلو کې د <nowiki> نښلنونه ځپل", + "expand_templates_generate_xml": "د ایکس ایم ایل پارسه وښیه", "expand_templates_generate_rawhtml": "خام HTML ښکاره کول", "expand_templates_preview": "مخليدنه", + "expand_templates_preview_fail_html": "<em>په دي دليل چي {{SITENAME}} اچ‌ټی‌ام‌ال خام فعال دي او د کارن حساب معلومات ورک شوي، شاته ښکارونه د جاوا سکرپټ د بریدونو په وړاندې د احتیاطي اندازې په توګه پټ دی.</em>\n\n<strong>که دا ښکارندويه تلاښ مشروع دي، هيله ده بیا هڅه وکړئ.</strong>\nکه تر اوسه هم کار نه کوي،سم يي کړي [[Special:UserLogout|د وتلو سیسټم]] بیا کلیک کړی او بيا ورننوځي، او ډاډه اوسي چې ستاسو براؤزر اجازه لري چې د دې سایټ څخه کوکیز ترلاسه کړي.", + "expand_templates_preview_fail_html_anon": "<em>په دي دليل چي {{SITENAME}} اچ‌ټی‌ام‌ال خام فعاله دي او د کارن معلومات له لاسه ورکړل شول، شاته ښکارونه د جاوا سکرپټ د بریدونو په وړاندې د احتیاطي اندازې په توګه پټ دی.</em>\n\n<strong>که دا ښکارندويه تلاښ مشروع دي، هيله ده [[Special:UserLogin|په سیسټم کې ننوځي]] او بیا تلاښ وکړئ.</strong>", + "expand_templates_input_missing": "تاسو اړتیا لرئ چي لږترلږه یو څه متن چمتو کړئ.", "pagelanguage": "د مخ ژبه بدلول", "pagelang-name": "مخ", "pagelang-language": "ژبه", "pagelang-use-default": "تلواليزه ژبه کارول", "pagelang-select-lang": "ژبه ټاکل", + "pagelang-reason": "سبب", "pagelang-submit": "سپارل", + "pagelang-nonexistent-page": "د $1 په نوم کوم مخ نشته", + "pagelang-unchanged-language": "د $1 مخ د $2 ژبو لپاره ټاکل شوی دي.", + "pagelang-unchanged-language-default": "مخ $1 لا د ډیزاین ويکي د ژبې منځپانګې لپاره ټاکل شوی.", + "pagelang-db-failed": "ډاټابیس د پاڼې د ژبي په بدلولو کې پاتې راغلی.", "right-pagelang": "د مخ ژبه بدلول", "action-pagelang": "د مخ ژبه بدلول", "log-name-pagelang": "د ژب بدلون يادښت", + "log-description-pagelang": "دا د پاڼو په ژبو کې د بدلونونو نښې دي.", + "logentry-pagelang-pagelang": "$1 ژبه $3 د $4 څخه و $5 ته {{GENDER:$2| بدله شوه}}", "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (چارن)", "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''ناچارن''')", "mediastatistics": "د رسنيو شمار", @@ -2725,13 +3035,25 @@ "mediastatistics-header-office": "دفتر", "mediastatistics-header-text": "متني", "mediastatistics-header-executable": "اجرايي", + "mediastatistics-header-archive": "کمپريزډ شوي پورمټونه", "mediastatistics-header-total": "ټولې دوتنې", + "json-warn-trailing-comma": "$1 کوماوي په پاي کي جی‌سن {{PLURAL:$1|ړنګ شوی}}.", + "json-error-unknown": "د جي سن سره ستونزه وه. تېروتنه: $1", + "json-error-depth": "د ډیری لوړ ډیزاین ژوره ډیر شوی", + "json-error-state-mismatch": "ناسم یا خراب شوې جي سن", + "json-error-ctrl-char": "د کنټرول کرکټر تېروتنه، ښایي په ناسم ډول انډول شوی وي", + "json-error-syntax": "د سينټاکس تېروتنه", + "json-error-utf8": "ناسم يو ټي اپ وړونکي - ٨، شاید په ناسم ډول انډول شوی وي", + "json-error-recursion": "په ارزښت کې د یو یا ډیرو بیاپروسي حواله چې کوډ شوي وي", + "json-error-inf-or-nan": "مقنایر INF یا NAN یو یا ډیر وخت په مقدار کې", + "json-error-unsupported-type": "د هغه ډول ارزښت چې کوډ نشي کوالی", "headline-anchor-title": "دې برخې ته تړنه", "special-characters-group-latin": "لاتين", "special-characters-group-latinextended": "غځېدلی لاتين", "special-characters-group-ipa": "ن.غ.ا", "special-characters-group-symbols": "سمبولونه", "special-characters-group-greek": "يوناني", + "special-characters-group-greekextended": "یوناني غزول", "special-characters-group-cyrillic": "سرېليک", "special-characters-group-arabic": "عربي", "special-characters-group-arabicextended": "غځېدلې عربي", @@ -2746,30 +3068,153 @@ "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": "منفي نښه", "mw-widgets-dateinput-no-date": "کومه نېټه نه ده ټاکل شوې", "mw-widgets-dateinput-placeholder-day": "Ú©Ú©Ú©Ú©-م م-و و", "mw-widgets-dateinput-placeholder-month": "Ú©Ú©Ú©Ú©-م م", + "mw-widgets-mediasearch-input-placeholder": "د رسنۍ پلټنه", + "mw-widgets-mediasearch-noresults": "پايلې و نه موندل شوې.", "mw-widgets-titleinput-description-new-page": "تر اوسه پورې دا مخ نشته", "mw-widgets-titleinput-description-redirect": "$1 ته ورگرځېدنه", + "mw-widgets-categoryselector-add-category-placeholder": "يوه وېشنيزه ورگډول...", + "mw-widgets-usersmultiselect-placeholder": "نور وراضافه کړئ ...", + "date-range-from": "د نیټې څخه:", + "date-range-to": "تر نيټې:", + "sessionmanager-tie": "تاسو ډیری ډول ډول تصدیقونه نشي نشر کولی: $1.", + "sessionprovider-generic": "$1 برخې", + "sessionprovider-mediawiki-session-cookiesessionprovider": "د کوکی پر بنسټ خپروني", + "sessionprovider-nocookies": "کوکیز ممکن معیوب شي. ډاډ ترلاسه کړئ چې تاسو کوکیز لرونکي يي او بیا پیل کوي.", "randomrootpage": "د ناټاکلې ريښې مخ", + "log-action-filter-block": "د بنديز ډول:", + "log-action-filter-contentmodel": "د منځپانګې نومونې بدلول:", + "log-action-filter-delete": "د ړنګولو ډولː", + "log-action-filter-import": "د واردولو ډول:", + "log-action-filter-managetags": "د لیګ مدیریت ډول:", + "log-action-filter-move": "د ورګرځولو ډول:", + "log-action-filter-newusers": "د ګڼون جوړونې ډول:", + "log-action-filter-patrol": "د ګزمي ډول:", + "log-action-filter-protect": "د ژغورلو ډول:", + "log-action-filter-rights": "د حقوق بدلولو ډول:", + "log-action-filter-suppress": "د ماتوني ډول:", + "log-action-filter-upload": "د پورته کولو ډول:", "log-action-filter-all": "ټول", "log-action-filter-block-block": "بنديز لگول", + "log-action-filter-block-reblock": "د بنديز تعدیل", + "log-action-filter-block-unblock": "بنديز لرې کول", + "log-action-filter-contentmodel-change": "د منځپانګې او ماډل بدلول", + "log-action-filter-contentmodel-new": "د غیر وېشنيزي منځپانګې نمونې سره د پاڼې جوړول", + "log-action-filter-delete-delete": "مخ ړنګونه", + "log-action-filter-delete-delete_redir": "راګرځونه تکرار کړئ", + "log-action-filter-delete-restore": "مخ د ړنگېدو څخه راوګرځوي", + "log-action-filter-delete-event": "مخ ړنګونه", + "log-action-filter-delete-revision": "يواځې ړنگ شوي", + "log-action-filter-import-interwiki": "ټرانس ويکي واردول", + "log-action-filter-import-upload": "د ایکس ایم ایل اپلوډ لخوا وارد", + "log-action-filter-managetags-create": "مخ جوړونې", + "log-action-filter-managetags-delete": "مخ ړنګونه", + "log-action-filter-managetags-activate": "د ټيګ فعاليتونه", + "log-action-filter-managetags-deactivate": "د Ù¼Ú« چلښت", + "log-action-filter-move-move": "د لارښوونې نه مخنیوی کول", + "log-action-filter-move-move_redir": "د لارښوونو د ګرځولو سره لاړ شئ", + "log-action-filter-newusers-create": "د نامعلوم کاروونکي لخوا جوړوني", + "log-action-filter-newusers-create2": "د ثبت شووطکاروونکي لخوا جوړوني", + "log-action-filter-newusers-autocreate": "خپلکاره جوړوني", + "log-action-filter-newusers-byemail": "د پټنوم رامینځته کول د بریښنالیک له لارې", + "log-action-filter-patrol-patrol": "لارښود ګزمې", + "log-action-filter-patrol-autopatrol": "اتومات ګزمې", + "log-action-filter-protect-protect": "ساتنه", + "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-suppress-event": "د ننوتلو فشار", + "log-action-filter-suppress-revision": "د بیاکتنې ماتول", + "log-action-filter-suppress-delete": "د پاڼې ماتول", + "log-action-filter-suppress-block": "د بلاک لخوا د کارن تاوان", + "log-action-filter-suppress-reblock": "د نابلاک لخوا د کارن تاوان", + "log-action-filter-upload-upload": "نوې پورته کونه", + "log-action-filter-upload-overwrite": "بيا پورته کول", + "authmanager-authn-not-in-progress": "تایید په پرمختګ کې ندي یا د ناستې ډاټا ورک شوی. لطفا د پیل څخه بیا شروع وکړئ.", + "authmanager-authn-no-primary": "ورکړل شوي اعتبارات نشی ورکول کیدی.", + "authmanager-authn-no-local-user": "ورکړل شوی اعتبارونه په دې ويکي د هیڅ کارن سره ندی تړلی.", + "authmanager-authn-no-local-user-link": "تاید شوي اعتبار باوري دي مګر په دې ويکي د هیڅ کارن سره ندی تړلی. په مختلفو لارو ننوتئ، یا یو نوی کارن جوړ کړئ، او تاسو به دا اختیار ولرئ چې خپل مخکیني سندونه د دې حساب ته لینک کړئ.", + "authmanager-authn-autocreate-failed": "د ايټو په ګڼون جوړولو کي ناکامه راغلي: $1", + "authmanager-change-not-supported": "ورکړل شوي اعتبارات نشي بدلیدای، ځکه چې هیڅ شی به یې دوی ونه کاروي.", + "authmanager-create-disabled": "د گڼون جوړول ناچارن شوی", + "authmanager-create-from-login": "د خپل حساب جوړولو لپاره، مهرباني وکړئ د کروندو ډک کړئ.", + "authmanager-create-not-in-progress": "تایید په پرمختګ کې ندي یا د ناستې ډاټا ورک شوی. لطفا د پیل څخه بیا شروع وکړئ.", + "authmanager-create-no-primary": "د ورکړل شوي کړني اعتبار نه شي کولی چي د حساب جوړولو لپاره وکارول شي.", + "authmanager-link-no-primary": "د ورکړل شوي تایید وړتیا د حساب کولو لپاره نه کارول کیدی.", + "authmanager-link-not-in-progress": "تایید په پرمختګ کې ندي یا د ناستې ډاټا ورک شوی. لطفا د پیل څخه بیا شروع وکړئ.", + "authmanager-authplugin-setpass-failed-title": "د پټنوم بدلون ترسره نشو", + "authmanager-authplugin-setpass-failed-message": "د تایید کولو پلگ ان د پاسورډ بدلون رد کړ.", + "authmanager-authplugin-create-fail": "د تایید کولو پلگ ان د حساب جوړولو انکار رد کړ.", + "authmanager-authplugin-setpass-denied": "د تاییدولو فلګن بدل شوي پټنوم اجازه نلري.", + "authmanager-authplugin-setpass-bad-domain": "ناباوره ډومین.", + "authmanager-autocreate-noperm": "د اتوماتيک حساب جوړولو جوړولو اجازه نشته.", + "authmanager-autocreate-exception": "د پخوانیو غلطیو له امله د اتوماتیک حساب ورکولو جوړول په عارضي ډول معیوب شوی.", + "authmanager-userdoesnotexist": "د \"$1\" گڼون نه دی ثبت شوی.", + "authmanager-userlogin-remembermypassword-help": "ایا پاسورډ باید د اوږدې مودې لپاره د ناستې د اوږدې مودې لپاره یاد وساتل شي.", + "authmanager-username-help": "د اعتبار لپاره کارن نوم", + "authmanager-password-help": "د اعتبار لپاره پټنوم.", + "authmanager-domain-help": "د بهرني اعتبار لپاره ډومین.", + "authmanager-retype-help": "د پټنوم بیا تاييدول.", "authmanager-email-label": "برېښليک", "authmanager-email-help": "برېښليک پته", "authmanager-realname-label": "اصلي نوم", "authmanager-realname-help": "د کارن اصلي نوم", + "authmanager-provider-password": "د اعتبار لپاره پټنوم.", + "authmanager-provider-password-domain": "د پټنوم او ډومین پر بنسټ اعتبار", + "authmanager-provider-temporarypassword": "لنډمهالی پټنوم", + "authprovider-confirmlink-message": "ستاسو د وروستي لاگ ان هڅو پر بنسټ، لاندې حسابونه د ستاسو د ويکي حساب سره تړلی کیدی شي. د دوی سره نښلول د دې حسابونو له لارې د لیګنګ کولو توان لري. مهرباني وکړئ هغه څوک وټاکئ کوم چې باید ورسره تړاو ولري.", + "authprovider-confirmlink-request-label": "هغه حسابونه چې ورسره تړاو لري", + "authprovider-confirmlink-success-line": "$1: په بریالیتوب سره لینک شو.", + "authprovider-confirmlink-failed": "د حساب لینکول په بشپړ ډول بریالي نه وو: $1", + "authprovider-confirmlink-ok-help": "د اړیکو د ناکام پیغامونو ښودلو وروسته دوام ومومئ.", "authprovider-resetpass-skip-label": "تېرېدل", + "authprovider-resetpass-skip-help": "د پټنځی بیا سمول پریږده.", + "authform-nosession-login": "تصدیق بریالی شو، مګر ستاسو براؤزر ونشو کولی \"یاد وساتئ\".\n\n$1", + "authform-nosession-signup": "ګڼون جوړونه بریالی شوه، مګر ستاسو براؤزر ونشو کولی \"یاد وساتئ\".\n\n$1", + "authform-newtoken": "ورک شوې نښه. $1", + "authform-notoken": "نادرکه نښه", + "authform-wrongtoken": "ناسمه نښه", "specialpage-securitylevel-not-allowed-title": "اجازه نسته", + "specialpage-securitylevel-not-allowed": "بخښنه غواړو، تاسو ته د دې پاڼې کارولو اجازه نه لرې ځکه چې ستا شناخت تایید نشو.", + "authpage-cannot-login": "د ننوتلو پیل کولو توان نلري.", + "authpage-cannot-login-continue": "د ننوتنې دوام نلري. ستاسو ناستې ډیری ممکن وخت نیسي.", + "authpage-cannot-create": "د ګڼون جوړولو پیل نشي کولی.", + "authpage-cannot-create-continue": "د حساب جوړول دوام ته پاتې دي. ستاسو ناستې ډیری دي ممکن به وخت نیسي.", + "authpage-cannot-link": "د حساب لینک کولو پیلولو توان نلري.", + "authpage-cannot-link-continue": "د ننوتنې دوام نلري. ستاسو ناستې ډیری ممکن وخت نیسي.", + "cannotauth-not-allowed-title": "د اجازې تفصيل", + "cannotauth-not-allowed": "تاسو د دې پاڼې کارولو اجازه نلري", + "changecredentials": "دبدلول اعتبارونه", "changecredentials-submit": "بدلول", + "changecredentials-invalidsubpage": "$1 د ناسمو ډولونو اعتبار له مخې، دا د منلو وړ نه دی.", + "changecredentials-success": "ستاسو اعتبارونه بدل شوي.", + "removecredentials": "اعتبارونه غورځول", "removecredentials-submit": "غورځول", + "removecredentials-invalidsubpage": "$1 د ناسمو ډولونو اعتبار له مخې، دا د منلو وړ نه دی.", + "removecredentials-success": "ستاسو اعتبارونه لري شوي.", + "credentialsform-provider": "د اعتبار وړ ډول:", "credentialsform-account": "گڼون نوم:", + "cannotlink-no-provider-title": "دلته د منلو وړ حساب شتون نلري.", + "cannotlink-no-provider": "دلته د منلو وړ حساب شتون نلري.", "linkaccounts": "ورګډ سوي ګڼونونه", "linkaccounts-success-text": "ګڼون ورګډ سو.", "linkaccounts-submit": "لينک کڼوڼونه", "unlinkaccounts": "ناخوښه ګڼونونه", "unlinkaccounts-success": "ګڼون ناخوښه سو.", + "authenticationdatachange-ignored": "د اعتبار ډاټا بدل ندی. کیدای شي کوم برابرونکي ترتیب نه وي؟", + "userjsispublic": "لطفا په یاد ولرئ: جاوا سکریپ پاڼې کې باید محرم معلومات نه وي ځکه چې دوی د نورو کاروونکو لخوا لیدل کیږي.", + "usercssispublic": "لطفا په یاد ولرئ: د ثي اس اس فرعي صفحې باید محرم معلومات ونه لري ځکه چې دوی د نورو کاروونکو لخوا لیدل کیږي.", "restrictionsfield-badip": "ناباوره آي پي آدرس او حدود د : $1", "restrictionsfield-label": "اجازه ورکړل شوي آي پي حدودونه:", + "restrictionsfield-help": "په هر کرښه کې د اي پي پته یا د سینیر رینټ داخل کړئ. د هر شی فعالولو لپاره دا ارزښت وکاروئ: <code>0.0.0.0/0</code><br /><code>::/0</code>", "revid": "بیاکتنه $1", "pageid": "د مخ پېژند$1", "rawhtml-notallowed": "لیبلونه <html> د منظمو ليکنو څخه بهر نشي کارول کیدی.", @@ -2777,7 +3222,9 @@ "gotointerwiki-invalid": "ټاکل شوی سرلیک نامعلوم دی.", "gotointerwiki-external": "تاسي د {{SITENAME}} د پريښودلو په حال کې یاست لیدلو لپاره [[$2]]، کوم یو جلا ویب پاڼه ده.\n\n'''[$1 دوام ورکونه و $1 ته]'''", "undelete-cantedit": "تاسو دا مخ شيه ړنګولي ځکه چې تاسو د دا پامخ د سمون اجازه نه لرئ.", + "undelete-cantcreate": "تاسو دا پاڼه نشي ړنګولی ځکه چې د دې نوم سره هيڅ پاڼه شتون نلري او تاسو د دې مخ د جوړولو اجازه هم نلرئ.", "pagedata-title": "د پاڼې ډاټا", + "pagedata-text": "دا پاڼه د پاڼو لپاره د ډاټا ګراف وړاندې کوي. مهرباني وکړئ د فرعي سرلیک نښې په کارولو سره د يو آر ال سرلیک چمتو کړئ.\n* د محتوا خبرې اترې د ستاسو د مراجعینو پر بنسټ د سرلیک قبولول دي. دا پدې مانا ده چې د پاڼې ډاټا به ستاسو د مراجعینو لخوا غوره شوي بڼه کې چمتو شي.", "pagedata-not-acceptable": "د سمون نمونه ونه موندل شوه. ملاتړ شوي ميمي ډولونه: $1", "pagedata-bad-title": "ناسم سرليک: $1" } diff --git a/languages/i18n/pt-br.json b/languages/i18n/pt-br.json index a540da02bd..7bb87c64d6 100644 --- a/languages/i18n/pt-br.json +++ b/languages/i18n/pt-br.json @@ -1115,6 +1115,7 @@ "timezoneregion-indian": "Oceano Índico", "timezoneregion-pacific": "Oceano Pacífico", "allowemail": "Permitir que outros usuários enviem-me e-mails", + "email-allow-new-users-label": "Permitir e-mails de novos usuários", "email-blacklist-label": "Proibir que estes usuários enviem-me e-mails:", "prefs-searchoptions": "Busca", "prefs-namespaces": "Espaços nominais", @@ -1284,6 +1285,7 @@ "right-siteadmin": "Bloquear e desbloquear o banco de dados", "right-override-export-depth": "Exportar páginas incluindo páginas ligadas até uma profundidade de 5", "right-sendemail": "Enviar email a outros usuários", + "right-sendemail-new-users": "Enviar e-mail para usuários sem ações registradas", "right-managechangetags": "Criar e (des)ativar [[Special:Tags|tags]]", "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] juntamente com as alterações de alguém", "right-changetags": "Adicionar e remover [[Special:Tags|etiquetas]] arbitrárias em revisões e ''logs'' individuais", @@ -1385,6 +1387,7 @@ "recentchanges-noresult": "Nenhuma alteração correspondente a esses critérios foi realizada durante o período selecionado.", "recentchanges-timeout": "Esta pesquisa expirou. Você pode tentar diferentes parâmetros de pesquisa.", "recentchanges-network": "Devido a um erro técnico, nenhum resultado pode ser carregado. Por favor tente atualizar a página.", + "recentchanges-notargetpage": "Digite um nome de página acima para ver as alterações relacionadas a essa página.", "recentchanges-feed-description": "Acompanhe neste feed as mudanças mais recentes do wiki.", "recentchanges-label-newpage": "Esta edição criou uma nova página", "recentchanges-label-minor": "Esta é uma edição menor", @@ -1517,6 +1520,11 @@ "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.", + "rcfilters-filter-showlinkedfrom-label": "Mostrar alterações nas páginas ligadas de", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páginas ligadas da</strong> página selecionada", + "rcfilters-filter-showlinkedto-label": "Mostrar alterações nas páginas que ligam para", + "rcfilters-filter-showlinkedto-option-label": "<strong>Páginas que ligam para</strong> página selecionada", + "rcfilters-target-page-placeholder": "Digite o nome de uma página", "rcnotefrom": "Abaixo {{PLURAL:$5|é a mudança|são as mudanças}} desde <strong>$3, $4</strong> (up to <strong>$1</strong> shown).", "rclistfromreset": "Redefinir seleção da data", "rclistfrom": "Mostrar as novas alterações a partir das $2 de $3", @@ -1561,7 +1569,7 @@ "recentchangeslinked-feed": "Mudanças relacionadas", "recentchangeslinked-toolbox": "Mudanças relacionadas", "recentchangeslinked-title": "Mudanças relacionadas com “$1”", - "recentchangeslinked-summary": "Esta página lista alterações feitas recentemente em páginas com links a uma em específico (ou de membros de uma categoria especificada).\nPáginas de sua [[Special:Watchlist|lista de páginas vigiadas]] são exibidas em '''negrito'''.", + "recentchangeslinked-summary": "Digite um nome de página para ver as alterações nas páginas vinculadas ou a partir dessa página. (Para ver membros de uma categoria, digite Categoria: Nome da categoria). Mudanças nas páginas em [[Special:Watchlist|lista de páginas vigiadas]] são exibidas em <strong>negrito<strong>", "recentchangeslinked-page": "Nome da página:", "recentchangeslinked-to": "Inversamente, mostrar mudanças nas páginas que contêm ligações para esta", "recentchanges-page-added-to-category": "[[:$1]]adicionada à categoria", @@ -3541,6 +3549,8 @@ "tag-mw-replace-description": "Edições que removem mais de 90% do conteúdo de uma página", "tag-mw-rollback": "Reverter", "tag-mw-rollback-description": "Edições que revertem edições anteriores usando o link de reversão", + "tag-mw-undo": "Desfazer", + "tag-mw-undo-description": "Edições que desfazem edições anteriores usando o link de desfazer", "tags-title": "Etiquetas", "tags-intro": "Esta página lista as etiquetas com que o software poderá marcar uma edição, e o seu significado.", "tags-tag": "Nome da etiqueta", diff --git a/languages/i18n/pt.json b/languages/i18n/pt.json index 1110ea58ff..1cbb513f1d 100644 --- a/languages/i18n/pt.json +++ b/languages/i18n/pt.json @@ -75,7 +75,8 @@ "Mansil", "Ngl2016", "RadiX", - "MokaAkashiyaPT" + "MokaAkashiyaPT", + "Athena in Wonderland" ] }, "tog-underline": "Sublinhar hiperligações:", @@ -233,7 +234,7 @@ "tagline": "Da wiki {{SITENAME}}", "help": "Ajuda", "search": "Pesquisa", - "search-ignored-headings": " #<!-- deixe esta linha exatamente como ela está --> <pre>\n# Títulos de página que serão ignorados pela pesquisa.\n# Mudanças a esta lista terão efeito quando a página com o título referido for indexada.\n# Pode forçar a indexação de uma página realizando uma edição nula nessa página.\n# A sintaxe é a seguinte:\n# * Tudo desde um símbolo de cardinal (#) até ao fim da linha é um comentário\n# * Cada linha não vazia é o título exato a ignorar, respeitando o uso de maiúsculas\nReferências\nLinks externos\nVer também\n #</pre> <!-- deixe esta linha exatamente como ela está -->", + "search-ignored-headings": " #<!-- deixe esta linha exatamente como ela está --> <pre>\n# Títulos de página que serão ignorados pela pesquisa.\n# Mudanças a esta lista terão efeito quando a página com o título referido for indexada.\n# Pode forçar a indexação de uma página realizando uma edição nula nessa página.\n# A sintaxe é a seguinte:\n# * Tudo desde um símbolo de cardinal (#) até ao fim da linha é um comentário\n# * Cada linha não vazia é o título exato a ignorar, respeitando o uso de maiúsculas\nReferências\nHiperligações externas\nVer também\n #</pre> <!-- deixe esta linha exatamente como ela está -->", "searchbutton": "Pesquisar", "go": "Prosseguir", "searcharticle": "Ir", @@ -411,8 +412,8 @@ "title-invalid-magic-tilde": "O título de página solicitado contém uma sequência de tis inválida (<nowiki>~~~</nowiki>).", "title-invalid-too-long": "O título de página solicitado é demasiado longo. Não pode exceder $1 {{PLURAL:$1|byte|bytes}} em codificação UTF-8.", "title-invalid-leading-colon": "O título de página solicitado contém um sinal de dois pontos (:) inválido no início.", - "perfcached": "Os seguintes dados encontram-se armazenados na ''cache'' e podem não estar atualizados. {{PLURAL:$1|Está disponível na ''cache'' um máximo de um resultado|Estão disponíveis na ''cache'' um máximo de $1 resultados}}.", - "perfcachedts": "Os seguintes dados encontram-se armazenados na ''cache'' e foram atualizados pela última vez a $1. {{PLURAL:$4|Está disponível na ''cache'' um máximo de um resultado|Estão disponíveis na ''cache'' um máximo de $4 resultados}}.", + "perfcached": "Os seguintes dados encontram-se armazenados na cache e podem não estar atualizados. {{PLURAL:$1|Está disponível na cache um máximo de um resultado|Estão disponíveis na cache um máximo de $1 resultados}}.", + "perfcachedts": "Os seguintes dados estão na cache e foram atualizados pela última vez a $1. {{PLURAL:$4|Está disponível na cache um máximo de um resultado|Estão disponíveis na cache um máximo de $4 resultados}}.", "querypage-no-updates": "As atualizações estão presentemente desativadas para esta página.\nPor enquanto, os dados aqui presentes não poderão ser atualizados.", "viewsource": "Ver código-fonte", "viewsource-title": "Mostrar código-fonte de $1", @@ -1084,6 +1085,7 @@ "timezoneregion-indian": "Oceano Índico", "timezoneregion-pacific": "Oceano Pacífico", "allowemail": "Permitir que outros utilizadores me enviem correio eletrónico", + "email-allow-new-users-label": "Permitir mensagens de correio de utilizadores novos", "email-blacklist-label": "Proibir estes utilizadores de me enviarem correio eletrónico:", "prefs-searchoptions": "Pesquisa", "prefs-namespaces": "Domínios", @@ -1206,7 +1208,7 @@ "right-reupload-own": "Sobrescrever um ficheiro existente carregado pelo mesmo utilizador", "right-reupload-shared": "Sobrescrever localmente ficheiros no repositório partilhado de imagens", "right-upload_by_url": "Carregar um ficheiro de um endereço URL", - "right-purge": "Limpar a ''cache'' de uma página no servidor sem confirmação", + "right-purge": "Limpar a cache de uma página no servidor sem confirmação", "right-autoconfirmed": "Não ser afetado pelos limites de frequência de edição baseados em endereços IP", "right-bot": "Ser tratado como um processo automatizado", "right-nominornewtalk": "Não desencadear o aviso de mensagens novas ao fazer edições menores a páginas de discussão", @@ -1257,6 +1259,7 @@ "right-siteadmin": "Bloquear e desbloquear a base de dados", "right-override-export-depth": "Exportar páginas incluindo páginas hiperligadas até uma profundidade de 5", "right-sendemail": "Enviar correio eletrónico a outros utilizadores", + "right-sendemail-new-users": "Enviar correio eletrónico a utilizadores sem ações registadas", "right-managechangetags": "Criar e (des)ativar [[Special:Tags|etiquetas]]", "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] juntamente com as alterações", "right-changetags": "Adicionar ou remover [[Special:Tags|etiquetas]] arbitrárias em revisões e entradas de registo individuais", @@ -1358,8 +1361,9 @@ "recentchanges-noresult": "Não foi realizada nenhuma alteração que corresponda a estes critérios durante o período especificado.", "recentchanges-timeout": "O tempo limite para esta pesquisa foi ultrapassado. Tente usar parâmetros de pesquisa diferentes.", "recentchanges-network": "Devido a uma falha técnica, não foi possível carregar nenhum resultado. Tente atualizar a página, por favor.", + "recentchanges-notargetpage": "Introduza um nome de página acima para ver as alterações relacionadas a essa página.", "recentchanges-feed-description": "Acompanhe neste ''feed'' as mudanças mais recentes da wiki.", - "recentchanges-label-newpage": "Esta edição criou uma nova página", + "recentchanges-label-newpage": "Esta edição criou uma página nova", "recentchanges-label-minor": "Esta é uma edição menor", "recentchanges-label-bot": "Esta edição foi feita por um robô", "recentchanges-label-unpatrolled": "Esta edição ainda não foi patrulhada", @@ -1490,11 +1494,11 @@ "rcfilters-watchlist-showupdated": "As mudanças das páginas que ainda não visitou após terem sido alteradas aparecem a <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.", - "rcfilters-filter-showlinkedfrom-label": "Mostrar mudanças de páginas para as quais esta página contém hiperligações:", - "rcfilters-filter-showlinkedfrom-option-label": "Mostrar mudanças de páginas PARA AS QUAIS uma página contém hiperligações", - "rcfilters-filter-showlinkedto-label": "Mostrar mudanças de páginas que contêm hiperligações para a página:", - "rcfilters-filter-showlinkedto-option-label": "Mostrar mudanças de páginas QUE CONTÊM hiperligações para uma página", - "rcfilters-target-page-placeholder": "Selecionar uma página", + "rcfilters-filter-showlinkedfrom-label": "Mostrar mudanças de páginas para as quais esta página contém hiperligações", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páginas para as quais</strong> a página selecionada contém hiperligações", + "rcfilters-filter-showlinkedto-label": "Mostrar mudanças nas páginas que contêm hiperligações para", + "rcfilters-filter-showlinkedto-option-label": "<strong>Páginas que contêm hiperligações</strong> para a página selecionada", + "rcfilters-target-page-placeholder": "Introduzir o nome de uma página", "rcnotefrom": "Abaixo {{PLURAL:$5|está a mudança|estão as mudanças}} desde <strong>$2</strong> (mostradas até <strong>$1</strong>).", "rclistfromreset": "Reiniciar a seleção da data", "rclistfrom": "Mostrar as novas mudanças a partir das $2 de $3", @@ -1539,7 +1543,7 @@ "recentchangeslinked-feed": "Alterações relacionadas", "recentchangeslinked-toolbox": "Alterações relacionadas", "recentchangeslinked-title": "Alterações relacionadas com \"$1\"", - "recentchangeslinked-summary": "Esta é uma lista de mudanças recentes a todas as páginas para as quais a página fornecida contém hiperligações (ou de todas as que pertencem à categoria fornecida).\nAs suas [[Special:Watchlist|páginas vigiadas]] aparecem a <strong>negrito</strong>.", + "recentchangeslinked-summary": "Introduza o nome de uma página para ver as mudanças a todas as páginas que contêm hiperligações para ela ou para as quais a página fornecida contém hiperligações (para ver as que pertencem a uma categoria, introduza Categoria:Nome da categoria). As mudanças às suas [[Special:Watchlist|páginas vigiadas]] aparecem a <strong>negrito</strong>.", "recentchangeslinked-page": "Nome da página:", "recentchangeslinked-to": "Inversamente, mostrar mudanças às páginas que contêm hiperligações para esta", "recentchanges-page-added-to-category": "[[:$1]] foi adicionada à categoria", @@ -1948,7 +1952,7 @@ "mostlinkedtemplates": "Páginas mais transcluídas", "mostcategories": "Páginas com mais categorias", "mostimages": "Ficheiros com mais afluentes", - "mostinterwikis": "Páginas com mais ligações interwikis", + "mostinterwikis": "Páginas com mais hiperligações interwikis", "mostrevisions": "Páginas com mais revisões", "prefixindex": "Todas as páginas iniciadas por", "prefixindex-namespace": "Todas as páginas com prefixo (domínio $1)", @@ -2008,14 +2012,14 @@ "apisandbox-fullscreen": "Expandir painel", "apisandbox-fullscreen-tooltip": "Expandir o painel da página de testes para preencher a janela do navegador.", "apisandbox-unfullscreen": "Mostrar página", - "apisandbox-unfullscreen-tooltip": "Reduza o painel da área de testes, para que as ligações de navegação estejam disponíveis.", + "apisandbox-unfullscreen-tooltip": "Reduza o painel da área de testes, para que as hiperligações de navegação do MediaWiki estejam disponíveis.", "apisandbox-submit": "Fazer o pedido", "apisandbox-reset": "Limpar", "apisandbox-retry": "Tentar novamente", "apisandbox-loading": "A carregar a informação do módulo API \"$1\"...", "apisandbox-load-error": "Ocorreu um erro ao carregar a informação do módulo API \"$1\": $2", "apisandbox-no-parameters": "Este módulo API não tem parâmetros.", - "apisandbox-helpurls": "Ligações de ajuda", + "apisandbox-helpurls": "Hiperligações de ajuda", "apisandbox-examples": "Exemplos", "apisandbox-dynamic-parameters": "Parâmetros adicionais", "apisandbox-dynamic-parameters-add-label": "Adicionar parâmetro:", @@ -2048,14 +2052,14 @@ "booksources": "Fontes bibliográficas", "booksources-search-legend": "Pesquisar fontes de livros", "booksources-search": "Pesquisar", - "booksources-text": "É apresentada abaixo uma lista de ligações para outros sítios na Internet que vendem livros novos e usados e talvez possuam informações adicionais sobre os livros que procura:", + "booksources-text": "É apresentada abaixo uma lista de hiperligações para outros sítios na Internet que vendem livros novos e usados e talvez possuam informações adicionais sobre os livros que procura:", "booksources-invalid-isbn": "O número ISBN fornecido não parece ser válido; verifique a existência de erros ao copiar da fonte original.", - "magiclink-tracking-rfc": "Páginas que usam links mágicos RFC", - "magiclink-tracking-rfc-desc": "Esta página usa links mágicos RFC. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", + "magiclink-tracking-rfc": "Páginas que usam hiperligações mágicas RFC", + "magiclink-tracking-rfc-desc": "Esta página usa hiperligações mágicas RFC. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", "magiclink-tracking-pmid": "Páginas que usam links mágicos PMID", - "magiclink-tracking-pmid-desc": "Esta página usa links mágicos PMID. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", + "magiclink-tracking-pmid-desc": "Esta página usa hiperligações mágicas PMID. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", "magiclink-tracking-isbn": "Páginas que usam links mágicos ISBN", - "magiclink-tracking-isbn-desc": "Esta página usa links mágicos ISBN. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", + "magiclink-tracking-isbn-desc": "Esta página usa hiperligações mágicas ISBN. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.", "specialloguserlabel": "Executante:", "speciallogtitlelabel": "Alvo (título ou página ou {{ns:user}}:nome de utilizador):", "log": "Registos", @@ -2092,7 +2096,7 @@ "deletedcontributions": "Edições eliminadas", "deletedcontributions-title": "Edições eliminadas", "sp-deletedcontributions-contribs": "contribuições", - "linksearch": "Pesquisa de ligações externas", + "linksearch": "Pesquisa de hiperligações externas", "linksearch-pat": "Padrão de pesquisa:", "linksearch-ns": "Domínio:", "linksearch-ok": "Pesquisar", @@ -2145,8 +2149,8 @@ "post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.", "post-expand-template-argument-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de um argumento de predefinição (algo em chavetas triplas, como <code>{{{Foo}}}</code>).", "expensive-parserfunction-category-desc": "A página tem demasiadas funções do analisador custosas (como <code>#ifexist</code>) incluídas. Consulte [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].", - "broken-file-category-desc": "A página contém uma ligação quebrada para um ficheiro (uma ligação para incorporar um ficheiro que não existe).", - "hidden-category-category-desc": "Esta é uma categoria com a marca <code><nowiki>__HIDDENCAT__</nowiki></code>, que faz com que ela não apareça na caixa de ligações de categoria nas páginas, por omissão.", + "broken-file-category-desc": "A página contém uma hiperligação quebrada para um ficheiro (uma hiperligação para incorporar um ficheiro que não existe).", + "hidden-category-category-desc": "Esta é uma categoria com a marca <code><nowiki>__HIDDENCAT__</nowiki></code>, que faz com que ela não apareça na caixa de hiperligações de categoria nas páginas, por omissão.", "trackingcategories-nodesc": "Sem descrição disponível.", "trackingcategories-disabled": "A categoria está desativada.", "mailnologin": "Não existe endereço de envio", @@ -2367,7 +2371,7 @@ "undeleterevdel": "O restauro não será efetuado se resulta na remoção parcial da versão mais recente da página ou ficheiro.\nNestes casos, deverá desmarcar ou revelar a versão eliminada mais recente.", "undeletehistorynoadmin": "Esta página foi eliminada.\nO motivo de eliminação é apresentado no resumo abaixo, em conjunto com detalhes dos utilizadores que tinham editado esta página antes da sua eliminação.\nO texto atual destas edições eliminadas encontra-se agora apenas disponível para administradores.", "undelete-revision": "Edição eliminada da página $1 (das $5 de $4), por $3:", - "undeleterevision-missing": "Edição inválida ou não encontrada.\nPode ter usado uma ligação incorreta ou talvez a revisão tenha sido restaurada ou removida do arquivo.", + "undeleterevision-missing": "Edição inválida ou não encontrada.\nPode ter usado uma hiperligação incorreta ou talvez a revisão tenha sido restaurada ou removida do arquivo.", "undeleterevision-duplicate-revid": "Não foi possível restaurar {{PLURAL:$1|uma revisão|$1 revisões}}, porque {{PLURAL:$1|a sua <code>rev_id</code> já estava a ser usada|as respetivas <code>rev_id</code> já estavam a ser usadas}}.", "undelete-nodiff": "Não foram encontradas edições anteriores.", "undeletebtn": "Restaurar", @@ -2437,13 +2441,13 @@ "nolinkshere-ns": "Não existem afluentes para <strong>[[:$1]]</strong> no espaço nominal selecionado.", "isredirect": "página de redirecionamento", "istemplate": "inclusão", - "isimage": "ligação para ficheiro", + "isimage": "hiperligação para ficheiro", "whatlinkshere-prev": "{{PLURAL:$1|anterior|$1 anteriores}}", "whatlinkshere-next": "{{PLURAL:$1|próximo|próximos $1}}", "whatlinkshere-links": "← afluentes", "whatlinkshere-hideredirs": "$1 redirecionamentos", "whatlinkshere-hidetrans": "$1 transclusões", - "whatlinkshere-hidelinks": "$1 ligações", + "whatlinkshere-hidelinks": "$1 hiperligações", "whatlinkshere-hideimages": "$1 ligações para ficheiros", "whatlinkshere-filters": "Filtros", "whatlinkshere-submit": "Continuar", @@ -2626,7 +2630,7 @@ "selfmove": "O título é o mesmo;\nnão é possível mover uma página para ela mesma.", "immobile-source-namespace": "Não é possível mover páginas no domínio \"$1\"", "immobile-target-namespace": "Não é possível mover páginas para o domínio \"$1\"", - "immobile-target-namespace-iw": "Uma ligação interlíngua não é um destino válido para uma movimentação de página.", + "immobile-target-namespace-iw": "Uma hiperligação interwikis não é um destino válido para uma movimentação de página.", "immobile-source-page": "Esta página não pode ser movida.", "immobile-target-page": "Não é possível mover para esse título de destino.", "bad-target-model": "O destino pretendido usa um modelo de conteúdo diferente. Não é possível converter de $1 para $2.", @@ -2641,7 +2645,7 @@ "move-over-sharedrepo": "[[:$1]] já existe num repositório partilhado. Mover um ficheiro para o título [[:$1]] irá substituir o ficheiro partilhado.", "file-exists-sharedrepo": "O nome de ficheiro que escolheu já é utilizado num repositório partilhado.\nEscolha outro nome, por favor.", "export": "Exportar páginas", - "exporttext": "Pode exportar o texto e o histórico de edições de uma página em particular para um ficheiro XML. Poderá então importar esse conteúdo noutra wiki que utilize o programa MediaWiki, através da [[Special:Import|página de importações]].\n\nPara exportar páginas, introduza os títulos na caixa de texto abaixo (um título por linha) e selecione se deseja todas as versões, com as linhas de histórico de edições, ou apenas a edição atual e informações sobre a mais recente das edições.\n\nSe desejar, pode utilizar um link (por exemplo, [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] para a [[{{MediaWiki:Mainpage}}]]).", + "exporttext": "Pode exportar o texto e o histórico de edições de uma página em particular para um ficheiro XML. Poderá então importar esse conteúdo noutra wiki que utilize o programa MediaWiki, através da [[Special:Import|página de importações]].\n\nPara exportar páginas, introduza os títulos na caixa de texto abaixo (um título por linha) e selecione se deseja todas as versões, com as linhas de histórico de edições, ou apenas a edição atual e informações sobre a mais recente das edições.\n\nSe desejar, pode utilizar uma hiperligação (por exemplo, [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] para a [[{{MediaWiki:Mainpage}}]]).", "exportall": "Exportar todas as páginas", "exportcuronly": "Incluir apenas a edição atual, não o histórico completo", "exportnohistory": "----\n<strong>Nota:</strong> A exportação do histórico completo de páginas através deste formulário foi desativada por motivos de desempenho.", @@ -2709,7 +2713,7 @@ "importunknownsource": "Tipo da fonte de importação desconhecido", "importnoprefix": "Não foi fornecido nenhum prefixo interwikis", "importcantopen": "Não foi possível abrir o ficheiro a importar", - "importbadinterwiki": "Ligação interlíngua incorreta", + "importbadinterwiki": "Hiperligação interwikis incorreta", "importsuccess": "Importação completa!", "importnosources": "Não foram definidas as wikis das quais importar e o carregamento direto de históricos encontra-se desativado.", "importnofile": "Não foi carregado nenhum ficheiro de importação.", @@ -2776,7 +2780,7 @@ "tooltip-n-randompage": "Carregar página aleatória", "tooltip-n-help": "Um local reservado para auxílio.", "tooltip-t-whatlinkshere": "Lista de todas as páginas que contêm ligações para esta", - "tooltip-t-recentchangeslinked": "Mudanças recentes nas páginas para as quais esta contém ligação", + "tooltip-t-recentchangeslinked": "Mudanças recentes nas páginas para as quais esta contém hiperligações", "tooltip-feed-rss": "''Feed'' RSS desta página", "tooltip-feed-atom": "''Feed'' Atom desta página", "tooltip-t-contributions": "Ver as contribuições {{GENDER:$1|deste utilizador|desta utilizadora|deste(a) utilizador(a)}}", @@ -2785,7 +2789,7 @@ "tooltip-t-upload": "Carregar ficheiros", "tooltip-t-specialpages": "Lista de páginas especiais", "tooltip-t-print": "Versão para impressão desta página", - "tooltip-t-permalink": "Ligação permanente para esta versão da página", + "tooltip-t-permalink": "Hiperligação permanente para esta revisão da página", "tooltip-ca-nstab-main": "Ver a página de conteúdo", "tooltip-ca-nstab-user": "Ver a página de utilizador", "tooltip-ca-nstab-media": "Ver a página de multimédia", @@ -2828,12 +2832,12 @@ "creditspage": "Créditos da página", "nocredits": "Não há informação disponível sobre os créditos desta página.", "spamprotectiontitle": "Filtro de proteção contra spam", - "spamprotectiontext": "O texto que desejava gravar foi bloqueado pelo filtro de spam.\nEste bloqueio foi provavelmente causado por um link para um site externo que consta da lista negra.", + "spamprotectiontext": "O texto que pretendia gravar foi bloqueado pelo filtro de spam.\nEste bloqueio foi provavelmente causado por uma hiperligação para um ''site'' externo que está na lista negra.", "spamprotectionmatch": "O seguinte texto ativou o filtro de <i>spam</i>: $1", "spambot_username": "MediaWiki limpeza de spam", "spam_reverting": "A reverter para a última revisão que não contém ligação para $1", - "spam_blanking": "Todas as revisões continham ligações para $1; a esvaziar", - "spam_deleting": "Todas as revisões continham ligações para $1; a eliminar", + "spam_blanking": "Todas as revisões continham hiperligações para $1; a esvaziar", + "spam_deleting": "Todas as revisões continham hiperligações para $1; a eliminar", "simpleantispam-label": "Verificação contra spam.\n<strong>Não</strong> preencha isto!", "pageinfo-title": "Informações sobre \"$1\"", "pageinfo-not-current": "Desculpe, é impossível fornecer esta informação para revisões antigas.", @@ -2973,7 +2977,7 @@ "saturday-at": "Sábado às $1", "sunday-at": "Domingo às $1", "yesterday-at": "Ontem às $1", - "bad_image_list": "O formato é o seguinte:\n\nSó são reconhecidos elementos na forma de lista (linhas começadas por *).\nO primeiro link em cada linha deve apontar para o ficheiro que se pretende bloquear.\nQuaisquer outras ligações nessa mesma linha são considerados excepções (ou seja, páginas de onde se pode aceder ao ficheiro).", + "bad_image_list": "O formato é o seguinte:\n\nSó são reconhecidos elementos na forma de lista (linhas começadas por *).\nA primeira hiperligação em cada linha deve apontar para o ficheiro que se pretende bloquear.\nQuaisquer outras hiperligações nessa mesma linha são consideradas exceções (ou seja, páginas de onde se pode aceder ao ficheiro).", "metadata": "Metadados", "metadata-help": "Este ficheiro contém informação adicional, provavelmente acrescentada pela câmara digital ou pelo digitalizador usados para criá-lo.\nCaso o ficheiro tenha sido modificado a partir do seu estado original, alguns detalhes poderão não refletir completamente as mudanças efetuadas.", "metadata-expand": "Mostrar detalhes adicionais", @@ -3334,9 +3338,9 @@ "confirmemail_success": "O seu endereço de correio eletrónico foi confirmado.\nPode agora [[Special:UserLogin|autenticar-se]] e desfrutar da wiki.", "confirmemail_loggedin": "O seu endereço de correio eletrónico foi confirmado.", "confirmemail_subject": "Confirmação de endereço de correio eletrónico da wiki {{SITENAME}}", - "confirmemail_body": "Alguém, provavelmente você a partir do endereço IP $1,\nregistou uma conta \"$2\" com este endereço de correio eletrónico na wiki {{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e ativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}}, abra a seguinte ligação no seu navegador:\n\n$3\n\nSe a conta *não* é sua, abra a seguinte ligação para cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", - "confirmemail_body_changed": "Alguém, provavelmente você a partir do endereço IP $1,\nalterou o endereço de correio eletrónico da conta \"$2\" para este endereço, na wiki{{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e reativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}},\nabra a seguinte ligação no seu navegador:\n\n$3\n\nCaso a conta *não* lhe pertença, abra a seguinte ligação\npara cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", - "confirmemail_body_set": "Alguém, provavelmente você a partir do endereço IP $1,\ndefiniu o seu endereço de correio eletrónico como correio da conta \"$2\" na wiki {{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e reativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}},\nabra a seguinte ligação no seu navegador:\n\n$3\n\nCaso a conta *não* lhe pertença, abra a seguinte ligação\npara cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", + "confirmemail_body": "Alguém, provavelmente você a partir do endereço IP $1,\nregistou uma conta \"$2\" com este endereço de correio eletrónico na wiki {{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e ativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}}, abra a seguinte hiperligação no seu navegador:\n\n$3\n\nSe a conta *não* é sua, siga a seguinte hiperligação para cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", + "confirmemail_body_changed": "Alguém, provavelmente você a partir do endereço IP $1,\nalterou o endereço de correio eletrónico da conta \"$2\" para este endereço, na wiki{{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e reativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}},\nabra a seguinte hiperligação no seu navegador:\n\n$3\n\nCaso a conta *não* lhe pertença, siga a seguinte hiperligação\npara cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", + "confirmemail_body_set": "Alguém, provavelmente você a partir do endereço IP $1,\ndefiniu o seu endereço de correio eletrónico como correio da conta \"$2\" na wiki {{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e reativar\nas funcionalidades de correio eletrónico na wiki {{SITENAME}},\nabra a seguinte hiperligação no seu navegador:\n\n$3\n\nCaso a conta *não* lhe pertença, siga a seguinte hiperligação\npara cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.", "confirmemail_invalidated": "Confirmação de endereço de correio eletrónico cancelada", "invalidateemail": "Cancelar confirmação do correio eletrónico", "notificationemail_subject_changed": "O endereço de correio eletrónico registado na wiki {{SITENAME}} foi alterado", @@ -3503,7 +3507,7 @@ "specialpages-group-developer": "Ferramentas de desenvolvimento", "blankpage": "Página em branco", "intentionallyblankpage": "Esta página foi intencionalmente deixada em branco", - "external_image_whitelist": " # Deixe esta linha exatamente como ela está<pre>\n# Coloque fragmentos de expressões regulares (apenas a parte entre //) abaixo\n# Estas serão comparadas com os URL das imagens externas (com ligação direta)\n# As que corresponderem serão apresentadas como imagens, caso contrário apenas será apresentado um link para a imagem\n# As linhas que começam com um símbolo de cardinal (#) são tratadas como comentários\n# Esta lista não distingue maiúsculas de minúsculas\n\n# Coloque todos os fragmentos de expressões regulares (regex) acima desta linha. Deixe esta linha exatamente como ela está</pre>", + "external_image_whitelist": " # Deixe esta linha exatamente como ela está<pre>\n# Coloque fragmentos de expressões regulares (apenas a parte entre //) abaixo\n# Estas serão comparadas com os URL das imagens externas (com ligação direta)\n# As que corresponderem serão apresentadas como imagens, caso contrário apenas será apresentada uma hiperligação para a imagem\n# As linhas que começam com um símbolo de cardinal (#) são tratadas como comentários\n# Esta lista não distingue maiúsculas de minúsculas\n\n# Coloque todos os fragmentos de expressões regulares (regex) acima desta linha. Deixe esta linha exatamente como ela está</pre>", "tags": "Etiquetas de modificação válidas", "tag-filter": "Filtro de [[Special:Tags|etiquetas]]:", "tag-filter-submit": "Filtrar", @@ -3522,6 +3526,8 @@ "tag-mw-replace-description": "Edições que removem mais de 90% do conteúdo de uma página", "tag-mw-rollback": "Reversão", "tag-mw-rollback-description": "Edições que revertem edições anteriores usando a hiperligação desfazer", + "tag-mw-undo": "Desfazer", + "tag-mw-undo-description": "Edições que desfazem edições anteriores usando a hiperligação «desfazer»", "tags-title": "Etiquetas de modificação válidas", "tags-intro": "Esta página lista as etiquetas com que o software poderá marcar uma edição, e o seu significado.", "tags-tag": "Nome da etiqueta", @@ -3622,7 +3628,7 @@ "diff-form-oldid": "Identificador de revisão antigo (opcional)", "diff-form-revid": "Identificador de revisão da diferença", "diff-form-submit": "Mostrar diferenças", - "permanentlink": "Link permanente", + "permanentlink": "Hiperligação permanente", "permanentlink-revid": "Identificador de revisão", "permanentlink-submit": "Ir para a revisão", "dberr-problems": "Desculpe! Este site está com dificuldades técnicas.", @@ -3856,7 +3862,7 @@ "json-error-recursion": "Uma ou mais referências recursivas no valor a ser codificado", "json-error-inf-or-nan": "Um ou mais valores NaN ou INF no valor a ser codificado", "json-error-unsupported-type": "Foi dado um valor de um tipo que não pode ser codificado", - "headline-anchor-title": "Ligação para esta secção", + "headline-anchor-title": "Hiperligação para esta secção", "special-characters-group-latin": "Latim", "special-characters-group-latinextended": "Latim expandido", "special-characters-group-ipa": "AFI (IPA)", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 072a93cc41..0659453915 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -869,8 +869,8 @@ "noarticletext-nopermission": "See also {{msg-mw|Noarticletext}}.", "noarticletextanon": "{{notranslate}}\nDefault:\n* {{msg-mw|Noarticletext}}", "missing-revision": "Text displayed when the requested revision does not exist using a permalink.\n\nExample: [{{canonicalurl:Project:News|oldid=9999999}} Permalink with invalid revision#]\n\nParameters:\n* $1 - the ID of the missing revision", - "userpage-userdoesnotexist": "Error message displayed when trying to edit or create a page or a subpage that belongs to a user who is not registered on the wiki.\n\nParameters:\n* $1 - a username\n{{identical|userdoesnotexist}}", - "userpage-userdoesnotexist-view": "Shown in user pages of non-existing users. See for example [{{canonicalurl:User:Foo}} User:Foo].\n\nParameters:\n* $1 - a username\n{{identical|userdoesnotexist}}", + "userpage-userdoesnotexist": "Error message displayed when trying to edit or create a page or a subpage that belongs to a user who is not registered on the wiki.\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}", + "userpage-userdoesnotexist-view": "Shown in user pages of non-existing users. See for example [{{canonicalurl:User:Foo}} User:Foo].\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}", "blocked-notice-logextract": "{{gender}}\nParameters:\n* $1 - (Optional) the name of the blocked user. Can be used for GENDER.", "clearyourcache": "Text at the top of .js/.css pages.\n\nWhen translating browser function names, check how they are translated in the localized versions of these web browsers in your language. If a browser is not translated to it, use English or another language in which browsers are most commonly used by the speakers of your language.", "usercssyoucanpreview": "Text displayed on every CSS page.\n\nSee also:\n* {{msg-mw|Userjsyoucanpreview}}\n* {{msg-mw|Showpreview}}", @@ -956,7 +956,7 @@ "content-model-text": "Name for the plain text content model, used when decribing what type of content a page contains. Plaintext means that the content of the page will be rendered as is like:\n\n<nowiki>This is [[plain text]]. <a href=\"https://www.mediawiki.org/\">Really!</a></nowiki>\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}\n\n{{Identical|Plain text}}", "content-model-javascript": "Name for the JavaScript content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}", "content-model-css": "Name for the CSS content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}\n{{Identical|CSS}}", - "content-model-json": "Name for the JSON content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}", + "content-model-json": "{{optional}}\nName for the JSON content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}\n{{identical|JSON}}", "content-json-empty-object": "Used to represent an object with no properties on a JSON content model page.", "content-json-empty-array": "Used to represent an array with no values on a JSON content model page.", "deprecated-self-close-category": "This message is used as a category name for a [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages are placed automatically if they contain invalid self-closed HTML tags, such as <code><b/></code> or <code><span/></code>. The behavior of these will change soon to be consistent with the HTML5 specification, so their use in wikitext is deprecated.", @@ -1259,6 +1259,7 @@ "timezoneregion-indian": "Used in \"Time zone\" listbox in [[Special:Preferences#mw-prefsection-datetime|preferences]], \"date and time\" tab.\n{{Related|Timezoneregion}}", "timezoneregion-pacific": "Used in \"Time zone\" listbox in [[Special:Preferences#mw-prefsection-datetime|preferences]], \"date and time\" tab.\n{{Related|Timezoneregion}}", "allowemail": "Used in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.", + "email-allow-new-users-label": "Used in [[Special:Preferences]] > {{int:prefs-prohibit}} > {{int:email}}.", "email-blacklist-label": "Used in [[Special:Preferences]] > {{int:prefs-prohibit}} > {{int:email}}.", "prefs-searchoptions": "{{Identical|Search}}", "prefs-namespaces": "Shown as legend of the second fieldset of the tab 'Search' in [[Special:Preferences]]\n{{Identical|Namespace}}", @@ -1434,6 +1435,7 @@ "right-siteadmin": "{{doc-right|siteadmin}}", "right-override-export-depth": "{{doc-right|override-export-depth}}", "right-sendemail": "{{doc-right|sendemail}}", + "right-sendemail-new-users": "{{doc-right|sendemail-new-users}}", "right-managechangetags": "{{doc-right|managechangetags}}", "right-applychangetags": "{{doc-right|applychangetags}}", "right-changetags": "{{doc-right|changetags}}", @@ -1559,7 +1561,7 @@ "rcfilters-activefilters": "Title for the filters selection showing the active filters.", "rcfilters-advancedfilters": "Title for the buttons allowing the user to switch to the various advanced filters views.", "rcfilters-limit-title": "Title for the options to change the number of results shown.", - "rcfilters-limit-and-date-label": "Title for the button that opens the operation to control how many results to show and in which time period to search. \n\nParameters: $1 - Number of results shown\n\n$2 - Time period to search. One of {{msg-mw|rcfilters-days-title}} or {{msg-mw|rcfilters-hours-title}} is used as $2", + "rcfilters-limit-and-date-label": "Title for the button that opens the operation to control how many results to show and in which time period to search. \n\nParameters: $1 - Number of results shown\n\n$2 - Time period to search. One of {{msg-mw|rcfilters-days-title}} or {{msg-mw|rcfilters-hours-title}} is used as $2\n{{Identical|Change}}", "rcfilters-date-popup-title": "Section title of date options on recent changes results", "rcfilters-days-title": "Title for the options to change the number of days for the results shown.", "rcfilters-hours-title": "Title for the options to change the number of hours for the results shown.", @@ -1700,11 +1702,11 @@ "rcshowhidepatr-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidepatr}}.\n\nSee also:\n* {{msg-mw|rcshowhidepatr-hide}}\n{{Identical|Show}}", "rcshowhidepatr-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidepatr}}.\n\nSee also:\n* {{msg-mw|rcshowhidepatr-show}}\n{{Identical|Hide}}", "rcshowhidemine": "Option text in [[Special:RecentChanges]]. Parameters:\n* $1 - the \"show/hide\" command, with the text taken from either {{msg-mw|rcshowhidemine-show}} or {{msg-mw|rcshowhidemine-hide}}", - "rcshowhidemine-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-hide}}\n{{Identical|show}}", - "rcshowhidemine-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-show}}\n{{Identical|hide}}", + "rcshowhidemine-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-hide}}\n{{Identical|Show}}", + "rcshowhidemine-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-show}}\n{{Identical|Hide}}", "rcshowhidecategorization": "Option text in [[Special:RecentChanges]]. Parameters:\n* $1 - the \"show/hide\" command, with the text taken from either {{msg-mw|rcshowhidecategorization-show}} or {{msg-mw|rcshowhidecategorization-hide}}", - "rcshowhidecategorization-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-hide}}\n{{Identical|show}}", - "rcshowhidecategorization-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-show}}\n{{Identical|hide}}", + "rcshowhidecategorization-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-hide}}\n{{Identical|Show}}", + "rcshowhidecategorization-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-show}}\n{{Identical|Hide}}", "rclinks": "Used on [[Special:RecentChanges]].\n* $1 - a list of different choices with number of pages to be shown.<br /> Example: \"''50{{int:pipe-separator}}100{{int:pipe-separator}}250{{int:pipe-separator}}500\".\n* $2 - a list of clickable links with a number of days for which recent changes are to be displayed.<br /> Example: \"''1{{int:pipe-separator}}3{{int:pipe-separator}}7{{int:pipe-separator}}14{{int:pipe-separator}}30''\".", "diff": "Short form of \"differences\". Used on [[Special:RecentChanges]], [[Special:Watchlist]], ...\n{{Identical|Diff}}", "hist": "Short form of \"history\". Used on [[Special:RecentChanges]], [[Special:Watchlist]], ...", @@ -1930,7 +1932,7 @@ "uploadstash-bad-path-invalid": "Error message when the upload stash path is invalid.", "uploadstash-bad-path-unknown-type": "Error message when the upload stash key is of an invalid type.\n\nParameters:\n* $1 - the type", "uploadstash-bad-path-unrecognized-thumb-name": "Error message when the upload stash key is of an unrecognized thumbnail", - "uploadstash-bad-path-no-handler": "Error message when no handler can be found for the given upload stash key.\n\nParameters:\n* $1 - the mine type\n* $2 - the file path", + "uploadstash-bad-path-no-handler": "Error message when no handler can be found for the given upload stash key.\n\nParameters:\n* $1 - the MIME type\n* $2 - the file path", "uploadstash-bad-path-bad-format": "Error message when the upload stash key if formatted incorrectly.\n\nParameters:\n* $1 - the stash key", "uploadstash-file-not-found": "Error message when a stashed file can't be found.\n\nParameters:\n* $1 - the stash key", "uploadstash-file-not-found-no-thumb": "Error message when no thumbnail could be generated.", @@ -2664,7 +2666,7 @@ "mycontris": "In the personal urls page section - right upper corner.\n\nSee also:\n* {{msg-mw|Mycontris}}\n* {{msg-mw|Accesskey-pt-mycontris}}\n* {{msg-mw|Tooltip-pt-mycontris}}\n{{Identical|Contribution}}", "anoncontribs": "Same as {{msg-mw|mycontris}} but used for non-logged-in users.\n\nSee also:\n* {{msg-mw|Accesskey-pt-anoncontribs}}\n* {{msg-mw|Tooltip-pt-anoncontribs}}\n{{Identical|Contribution}}", "contribsub2": "Contributions for \"user\" (links). Parameters:\n* $1 is an IP address or a username, with a link which points to the user page (if registered user).\n* $2 is list of tool links. The list contains a link which has text {{msg-mw|Sp-contributions-talk}}.\n* $3 is a plain text username used for GENDER.\n{{Identical|For $1}}", - "contributions-userdoesnotexist": "This message is used in [[Special:Contributions]]. It is used to tell the user that the name he searched for doesn't exists.\n\nParameters:\n* $1 - a username\n\n{{identical|userdoesnotexist}}", + "contributions-userdoesnotexist": "This message is used in [[Special:Contributions]]. It is used to tell the user that the name he searched for doesn't exist.\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}", "nocontribs": "Used in [[Special:Contributions]] and [[Special:DeletedContributions]].\n\nSee examples: [[Special:Contributions/x]] and [[Special:DeletedContributions/x]].\n\nParameters:\n* $1 - (Unused) the user name", "uctop": "This message is used in [[Special:Contributions]]. It is used to show that a particular edit was the last made to a page. Example: 09:57, 11 February 2008 (hist) (diff) Pagename‎ (edit summary) (current)\n{{Identical|Current}}", "month": "Used in [[Special:Contributions]] and history pages ([{{fullurl:Sandbox|action=history}} example]), as label for a dropdown box to select a specific month to view the edits made in that month, and the earlier months. See also {{msg-mw|year}}.", @@ -2819,7 +2821,7 @@ "ipb_blocked_as_range": "Used when unblock of a single IP fails. Parameters:\n* $1 - IP address\n* $2 - IP address range", "ip_range_invalid": "Used as error message in [[Special:Block]].\n\nSee also:\n* {{msg-mw|Range block disabled}}\n* {{msg-mw|Ip range invalid}}\n* {{msg-mw|Ip range toolarge}}", "ip_range_toolarge": "Used as error message in [[Special:Block]]. Parameters:\n* $1 - a number from 0 to 32 for IPv4 (from 0 to 128 for IPv6); a part of CIDR (Classless Inter-Domain Routing) notation.\nSee also:\n* {{msg-mw|Range block disabled}}\n* {{msg-mw|Ip range invalid}}\n* {{msg-mw|Ip range toolarge}}", - "ip_range_exceeded": "Used as error message in HTMLUserTextField when an IP range exceeds its maximum amount. See {{mw-msg|ip_range_toolarge}} for parameter.\n/$1 is the width as a number of bits.", + "ip_range_exceeded": "Used as error message in HTMLUserTextField when an IP range exceeds its maximum amount. See {{msg-mw|ip_range_toolarge}} for parameter.\n/$1 is the width as a number of bits.", "ip_range_toolow": "Used as error message in HTMLUserTextField, if effectively no IP ranges are interpreted as valid (IPv4 CIDR range /32 or IPv6 /128).", "proxyblocker": "Used in [[Special:BlockMe]].\n\nSee also:\n* {{msg-mw|proxyblocker-disabled}}\n* {{msg-mw|proxyblockreason}}\n* {{msg-mw|proxyblocksuccess}}", "proxyblockreason": "Used as explanation of the reason in [[Special:BlockMe]].\n\nSee also:\n* {{msg-mw|proxyblocker-disabled}}\n* {{msg-mw|proxyblocker}}\n* {{msg-mw|proxyblocksuccess}}", @@ -3850,7 +3852,7 @@ "autosumm-new": "The auto summary when creating a new page. $1 are the first X number of characters of the new page.", "autosumm-newblank": "The automatic edit summary when creating a blank page. This is not the same as blanking a page.", "autoblock_whitelist": "{{notranslate}}", - "size-bytes": "Size (of a page, typically) in bytes.", + "size-bytes": "Size (of a page, typically) in bytes.\n{{Identical|Byte}}", "size-kilobytes": "{{optional}}\nSize (of a page, typically) in kibibytes (1 kibibyte = 1024 bytes).", "size-megabytes": "{{optional}}\nSize (of a file, typically) in mebibytes (1 mebibyte = 1024×1024 bytes).", "size-gigabytes": "{{optional}}\nSize (of a file, typically) in gibibytes (1 gibibyte = 1024×1024×1024 bytes).", @@ -3859,7 +3861,7 @@ "size-exabytes": "{{optional}}\nSize (of a file, typically) in exbibytes (1 exbibytes = 1024×1024×1024×1024×1024×1024 bytes).", "size-zetabytes": "{{optional}}\nSize (of a file, typically) in zebibytes (1 zebibytes = 1024×1024×1024×1024×1024×1024×1024 bytes).", "size-yottabytes": "{{optional}}\nSize (of a file, typically) in yobibytes (1 yobibytes = 1024×1024×1024×1024×1024×1024×1024×1024 bytes).", - "size-pixel": "Size (of a file, typically) in pixel.", + "size-pixel": "Size (of a file, typically) in pixel.\n{{Identical|Pixel}}", "size-kilopixel": "{{optional}}\nSize (of a file, typically) in kilopixel (1 kilopixel = 1000 pixel).", "size-megapixel": "{{optional}}\nSize (of a file, typically) in megapixel (1 megapixel = 1000×1000 pixel).", "size-gigapixel": "{{optional}}\nSize (of a file, typically) in gigapixel (1 gigapixel = 1000×1000×1000 pixel).", @@ -4084,6 +4086,8 @@ "tag-mw-replace-description": "Description for \"replace\" change tag", "tag-mw-rollback": "Change tag for rolling back an edit\n{{Identical|Rollback}}", "tag-mw-rollback-description": "Description for \"rollback\" change tag", + "tag-mw-undo": "Change tag for undoing an edit", + "tag-mw-undo-description": "Description for \"undo\" change tag", "tags-title": "The title of [[Special:Tags]].\n{{Identical|Tag}}", "tags-intro": "Explanation on top of [[Special:Tags]]. For more information on tags see [[mw:Manual:Tags|MediaWiki]].", "tags-tag": "Caption of a column in [[Special:Tags]]. For more information on tags see [[mw:Manual:Tags|MediaWiki]].", diff --git a/languages/i18n/roa-tara.json b/languages/i18n/roa-tara.json index a19cd091c5..d85313f0be 100644 --- a/languages/i18n/roa-tara.json +++ b/languages/i18n/roa-tara.json @@ -1367,6 +1367,7 @@ "rcshowhidemine": "$1 cangiaminde mie", "rcshowhidemine-show": "Fà vedè", "rcshowhidemine-hide": "Scunne", + "rcshowhidecategorization": "$1 categorizzazzione d'a pàgene", "rcshowhidecategorization-show": "Fà 'ndrucà", "rcshowhidecategorization-hide": "Scunne", "rclinks": "Vide l'urteme $1 cangiaminde jndr'à l'urteme $2 sciurne", diff --git a/languages/i18n/ru.json b/languages/i18n/ru.json index 7df92e7871..5bf406b01e 100644 --- a/languages/i18n/ru.json +++ b/languages/i18n/ru.json @@ -246,7 +246,7 @@ "category-article-count": "{{PLURAL:$2|1=Эта категория содержит единственную страницу.|{{PLURAL:$1|Показана $1 страница|Показаны $1 страницы|Показано $1 страниц}} из $2, {{PLURAL:$2|находящейся|находящихся}} в данной категории.}}", "category-article-count-limited": "В этой категории {{PLURAL:$1|$1 страница|$1 страницы|$1 страниц|1=только одна страница}}.", "category-file-count": "{{PLURAL:$2|1=Эта категория содержит единственный файл.|{{PLURAL:$1|Показан $1 файл|Показано $1 файла|Показаны $1 файлов}} из $2, {{PLURAL:$2|находящегося|находящихся}} в данной категории.}}", - "category-file-count-limited": "В этой категории {{PLURAL:$1|1=1 файл|$1 файла|$1 файлов}}.", + "category-file-count-limited": "В этой категории {{PLURAL:$1|$1 файл|$1 файла|$1 файлов|1=только один файл}}.", "listingcontinuesabbrev": "(продолжение)", "index-category": "Индексируемые страницы", "noindex-category": "Неиндексируемые страницы", @@ -292,8 +292,8 @@ "create": "Создать", "create-local": "Создать локально", "delete": "Удалить", - "undelete_short": "Восстановить {{PLURAL:$1|1=$1 правку|$1 правки|$1 правок|1=правку}}", - "viewdeleted_short": "Просмотр {{PLURAL:$1|1=$1 удалённой правки|удалённой правки|$1 удалённых правок}}", + "undelete_short": "Восстановить {{PLURAL:$1|$1 правку|$1 правки|$1 правок|1=правку}}", + "viewdeleted_short": "Просмотр {{PLURAL:$1|1=удалённой правки|$1 удалённой правки|$1 удалённых правок}}", "protect": "Защитить", "protect_change": "изменить", "unprotect": "Изменить защиту", @@ -318,12 +318,12 @@ "redirectpagesub": "Страница-перенаправление", "redirectto": "Перенаправление на:", "lastmodifiedat": "Эта страница последний раз была отредактирована $1 в $2.", - "viewcount": "К этой странице обращались $1 {{PLURAL:$1|1=раз|раза|раз}}.", + "viewcount": "К этой странице обращались $1 {{PLURAL:$1|раз|раза|раз}}.", "protectedpage": "Защищённая страница", "jumpto": "Перейти к:", "jumptonavigation": "навигация", "jumptosearch": "поиск", - "view-pool-error": "Извините, в настоящий момент серверы перегружены.\nСлишком много пользователей пытаются просмотреть этот ресурс.\nПожалуйста, подождите и повторите попытку обращения к нему позже.\n\n$1", + "view-pool-error": "Извините, в настоящий момент серверы перегружены.\nЭту страницу пытаются одновременно просмотреть слишком многие.\nПожалуйста, подождите немного перед повторной попыткой обращения к этой странице.\n\n$1", "generic-pool-error": "Извините, в настоящий момент серверы перегружены.\nСлишком много пользователей пытаются просмотреть этот ресурс.\nПожалуйста, подождите и повторите попытку обращения к нему позже.", "pool-timeout": "Истекло время ожидания блокировки", "pool-queuefull": "Накопитель запросов полон", @@ -357,7 +357,7 @@ "backlinksubtitle": "← $1", "retrievedfrom": "Источник — «$1»", "youhavenewmessages": "Вы получили $1 ($2).", - "youhavenewmessagesfromusers": "{{PLURAL:$4|Вы получили}} $1 от {{PLURAL:$3|1=$3 участника|$3 участников|1=другого участника}} ($2).", + "youhavenewmessagesfromusers": "{{PLURAL:$4|Вы получили}} $1 от {{PLURAL:$3|$3 участника|$3 участников|1=другого участника}} ($2).", "youhavenewmessagesmanyusers": "Вы получили $1 от множества пользователей ($2).", "newmessageslinkplural": "{{PLURAL:$1|1=новое сообщение|999=новые сообщения}}", "newmessagesdifflinkplural": "{{PLURAL:$1|1=последнее изменение|999=последние изменения}}", @@ -450,8 +450,8 @@ "title-invalid-magic-tilde": "Запрашиваемый заголовок страницы содержит недопустимую последовательность тильды (<nowiki>~~~</nowiki>).", "title-invalid-too-long": "Запрашиваемый заголовок страницы слишком длинен. Он должен быть не более $1 {{PLURAL:$1|1=байта|байт}} в кодировке UTF-8.", "title-invalid-leading-colon": "Запрашиваемое название страницы содержит недопустимое двоеточие в начале.", - "perfcached": "Данные взяты кэша; они могли устареть. В кэше хранится не более $1 {{PLURAL:$1|1=записи|записей}}.", - "perfcachedts": "Данные взяты из кэша; последний раз он обновлялся в $1. В кэше хранится не более {{PLURAL:$4|1=$1 записи|$1 записей}}.", + "perfcached": "Следующие данные были взяты из кэша и могут быть устаревшими. В кэше хранится не более $1 {{PLURAL:$1|записи|записей}}.", + "perfcachedts": "Данные взяты из кэша; последний раз он обновлялся в $1. В кэше хранится не более {{PLURAL:$4|1=одной записи|$4 записи|$4 записей}}.", "querypage-no-updates": "Обновление этой страницы сейчас отключено.\nПредставленные здесь данные не будут обновляться.", "viewsource": "Просмотр вики-текста", "viewsource-title": "Просмотр исходного текста страницы $1", @@ -482,7 +482,7 @@ "virus-badscanner": "Ошибка настройки. Неизвестный сканер вирусов: ''$1''", "virus-scanfailed": "ошибка сканирования (код $1)", "virus-unknownscanner": "неизвестный антивирус:", - "logouttext": "<strong>Вы завершили сеанс работы.</strong>\n\nНекоторые страницы могут продолжить отображаться так, словно вы все еще находитесь в системе. Для борьбы с этим явлением обновите кэш браузера.", + "logouttext": "<strong>Вы завершили сеанс работы.</strong>\n\nНекоторые страницы могут продолжить отображаться так, словно вы все ещё не завершили сеанс. Для борьбы с этим явлением обновите кэш браузера.", "cannotlogoutnow-title": "Невозможно выйти прямо сейчас", "cannotlogoutnow-text": "Нельзя выйти во время использования $1.", "welcomeuser": "Добро пожаловать, $1!", @@ -531,16 +531,16 @@ "createaccountmail-help": "\nМожет использоваться, чтобы создать учетную запись для другого лица, не узнавая пароль.", "createacct-realname": "Настоящее имя (необязательно)", "createacct-reason": "Причина", - "createacct-reason-ph": "Зачем вы создаёте другую учетную запись", + "createacct-reason-ph": "Зачем вы создаёте другую учётную запись", "createacct-reason-help": "Сообщение, отображаемое в журнале создания учётных записей", "createacct-submit": "Создать учётную запись", "createacct-another-submit": "Создать учётную запись", "createacct-continue-submit": "Продолжить создание учётной записи", "createacct-another-continue-submit": "Продолжить создание учётной записи", "createacct-benefit-heading": "{{SITENAME}} — совместный труд таких же людей, как вы.", - "createacct-benefit-body1": "{{PLURAL:$1|1=$1 правка|$1 правки|$1 правок}}", - "createacct-benefit-body2": "{{PLURAL:$1|1=$1 статья|$1 статьи|$1 статей}}", - "createacct-benefit-body3": "{{PLURAL:$1|1=$1 участник|$1 участника|$1 участников}} за последнее время", + "createacct-benefit-body1": "{{PLURAL:$1|правка|правки|правок}}", + "createacct-benefit-body2": "{{PLURAL:$1|страница|страницы|страниц}}", + "createacct-benefit-body3": "{{PLURAL:$1|участник|участника|участников}} за последнее время", "badretype": "Введённые вами пароли не совпадают.", "usernameinprogress": "Создание учётной записи для данного имени участника уже выполняется.\nПожалуйста, подождите.", "userexists": "Введённое имя участника уже используется.\nПожалуйста, выберите другое имя.", @@ -560,20 +560,20 @@ "login-userblocked": "Участник заблокирован. Вход в систему запрещен.", "wrongpassword": "Введены неверные имя участника или пароль.\nПопробуйте ещё раз.", "wrongpasswordempty": "Пожалуйста, введите непустой пароль.", - "passwordtooshort": "Пароль должен состоять не менее, чем из $1 {{PLURAL:$1|1=символа|символов}}.", + "passwordtooshort": "Пароль должен состоять не менее, чем из $1 {{PLURAL:$1|символа|символов}}.", "passwordtoolong": "Пароль не может содержать более {{PLURAL:$1|1=$1 символа|$1 символов}}.", "passwordtoopopular": "Часто выбираемые пароли не могут быть использованы. Пожалуйста, выберите более уникальный пароль.", "password-name-match": "Введённый пароль должен отличаться от имени участника.", "password-login-forbidden": "Использование этого имени участника и пароля запрещено.", "mailmypassword": "Сбросить пароль", "passwordremindertitle": "Напоминание пароля участника {{grammar:genitive|{{SITENAME}}}}", - "passwordremindertext": "Кто-то (вероятно, вы, с IP-адреса $1) запросил создать\nновый пароль для {{grammar:genitive|{{SITENAME}}}} ($4). Для участника $2\nсоздан временный пароль: $3. Если это был ваш запрос,\nвам следует представиться системе и выбрать новый пароль.\nВаш временный пароль будет действовать в течение $5 {{PLURAL:$5|1=дня|дней}}.\n\nЕсли вы не посылали запроса на смену пароля, или если вы уже вспомнили свой пароль,\nи не желаете его менять, вы можете проигнорировать данное сообщение и\nпродолжить использовать свой старый пароль.", + "passwordremindertext": "Кто-то (вероятно, вы, с IP-адреса $1) запросил создать\nновый пароль для {{grammar:genitive|{{SITENAME}}}} ($4). Для участника $2\nсоздан временный пароль: $3. Если это был ваш запрос,\nвам следует представиться системе и выбрать новый пароль.\nВаш временный пароль будет действовать в течение $5 {{PLURAL:$5|дня|дней}}.\n\nЕсли вы не посылали запроса на смену пароля, или если вы уже вспомнили свой пароль,\nи не желаете его менять, вы можете проигнорировать данное сообщение и\nпродолжить использовать свой старый пароль.", "noemail": "Для участника с именем $1 электронный адрес указан не был.", "noemailcreate": "Вам необходимо указать корректный адрес электронной почты", "passwordsent": "Новый пароль был выслан на адрес электронной почты, указанный для участника $1.\n\nПожалуйста, представьтесь системе заново после получения пароля.", "blocked-mailpassword": "Редактирование с вашего IP-адреса запрещено. Для предотвращения злоупотреблений для этого IP-адреса заблокирована и функция восстановления пароля.", "eauthentsent": "На указанный адрес электронной почты отправлено письмо. \nЧтобы получать письма в дальнейшем, следуйте изложенным там инструкциям для подтверждения, что этот адрес действительно принадлежит вам.", - "throttled-mailpassword": "Функция напоминания пароля уже использовалась в течение {{PLURAL:$1|1=последнего часа|последних $1 часов}}.\nДля предотвращения злоупотреблений, разрешено запрашивать не более одного напоминания {{PLURAL:$1|1=за $1 час|за $1 часов|за $1 часа|1=в час}}.", + "throttled-mailpassword": "Функция напоминания пароля уже использовалась в течение {{PLURAL:$1|последнего $1 часа|последних $1 часов|1=последнего часа}}.\nДля предотвращения злоупотреблений, разрешено запрашивать не более одного напоминания {{PLURAL:$1|за $1 час|за $1 часа|за $1 часов|1=в час}}.", "mailerror": "Ошибка при отправке почты: $1", "acct_creation_throttle_hit": "Посетители с вашего IP-адреса {{PLURAL:$1|была создана $1 учётная запись|было создано $1 учётных записей|были созданы $1 учётных записи}} за последние $2 — это предельное количество для данного отрезка времени.\nВ результате, пользователи с этим IP-адресом в данный момент больше не могут создавать новых учётных записей.", "emailauthenticated": "Ваш адрес электронной почты подтверждён $2 в $3.", @@ -1121,6 +1121,7 @@ "timezoneregion-indian": "Индийский океан", "timezoneregion-pacific": "Тихий океан", "allowemail": "Разрешить другим участникам отправлять мне электронную почту", + "email-allow-new-users-label": "Разрешить электронные письма от совсем новых участников", "email-blacklist-label": "Запретить этим участникам отправлять мне электронную почту:", "prefs-searchoptions": "Поиск", "prefs-namespaces": "Пространства имён", @@ -1144,11 +1145,11 @@ "prefs-help-signature": "Комментарии на страницах обсуждений должны быть подписаны с помощью «<nowiki>~~~~</nowiki>», что будет преобразовано в вашу подпись и время.", "badsig": "Неверная подпись. Проверьте корректность HTML-тегов.", "badsiglength": "Слишком длинная подпись.\nПодпись не должна превышать $1 {{PLURAL:$1|символа|символа|символов}}.", - "yourgender": "Какое описание подходит для вас больше всего?", + "yourgender": "Какое описание вы считаете для себя более подходящим?", "gender-unknown": "При упоминании вас программное обеспечение будет по возможности использовать гендерно-нейтральные слова", "gender-male": "Он редактирует страницы вики", "gender-female": "Онa редактирует страницы вики", - "prefs-help-gender": "Этот параметр задавать необязательно.\nЗначение будет использовано для того, чтобы обращаться к вам и упоминать вас в правильном грамматическом роде.\nЭта информация будет общедоступной.", + "prefs-help-gender": "Этот параметр задавать необязательно.\nЗначение будет использовано для того, чтобы обращаться к вам и упоминать вас в подходящем роде.\nЭта информация будет общедоступной.", "email": "Электронная почта", "prefs-help-realname": "Вводить настоящее имя необязательно.\nЕсли вы заполните его, оно может быть использовано для указания авторства ваших работ.", "prefs-help-email": "Адрес почты не обязателен, но это единственный способ восстановить забытый пароль.", @@ -1171,7 +1172,7 @@ "prefs-displaywatchlist": "Настройки отображения", "prefs-tokenwatchlist": "Токен", "prefs-diffs": "Разница версий", - "prefs-help-prefershttps": "Этот параметр будет применён только после перезахода на сайт.", + "prefs-help-prefershttps": "Этот параметр будет применён только после повторного входа на сайт.", "prefswarning-warning": "Вы внесли в свои настройки изменения, которые ещё не были сохранены.\nЕсли вы покинете эту страницу, не нажав «$1», то настройки обновлены не будут.", "prefs-tabs-navigation-hint": "Совет: Вы можете использовать клавиши стрелок влево и вправо для перехода между вкладками в списке вкладок.", "userrights": "Права участника", @@ -1290,6 +1291,7 @@ "right-siteadmin": "блокировка и разблокировка базы данных", "right-override-export-depth": "экспортирование страниц, включая связанные страницы с глубиной до 5", "right-sendemail": "отправка электронной почты другим участникам", + "right-sendemail-new-users": "отправка электронной почты участникам без записей журналов", "right-managechangetags": "создание и (де)активация [[Special:Tags|меток]]", "right-applychangetags": "применение [[Special:Tags|меток]] вместе со своими правками", "right-changetags": "добавление и удаление произвольных [[Special:Tags|меток]] на отдельных правках и записях в журнале", @@ -1310,7 +1312,7 @@ "grant-delete": "Удаление страниц, правок и записей журнала", "grant-editinterface": "Правка пространства имён MediaWiki и пользовательских CSS/JavaScript", "grant-editmycssjs": "Редактирование ваших пользовательских CSS/JavaScript", - "grant-editmyoptions": "Редактирование ваших настроек", + "grant-editmyoptions": "Редактирование ваших персональных настроек", "grant-editmywatchlist": "Редактирование вашего списка наблюдения", "grant-editpage": "Редактирование существующих страниц", "grant-editprotected": "Редактирование защищённых страниц", @@ -1391,6 +1393,7 @@ "recentchanges-noresult": "Изменений в указанный период, соответствующих указанным условиям, нет.", "recentchanges-timeout": "Время ожидания этого поиска истекло. Вы можете попробовать задать другие параметры поиска.", "recentchanges-network": "Из-за технической ошибки результаты не могут быть загружены. Попробуйте обновить страницу.", + "recentchanges-notargetpage": "Введите название страницы выше, чтобы увидеть правки, связанные с этой страницей.", "recentchanges-feed-description": "Отслеживание последних изменений в вики.", "recentchanges-label-newpage": "Этой правкой была создана новая страница", "recentchanges-label-minor": "Это незначительное изменение", @@ -1523,6 +1526,11 @@ "rcfilters-watchlist-showupdated": "Изменения страниц, которые вы не посещали с того момента, как они изменились, выделены <strong>жирным</strong> и отмечены полным маркером.", "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": "Ниже {{PLURAL:$5|указано изменение|перечислены изменения}} с <strong>$3, $4</strong> (показано не более <strong>$1</strong>).", "rclistfromreset": "Сбросить выбор даты", "rclistfrom": "Показать изменения с $3 $2.", @@ -1568,7 +1576,7 @@ "recentchangeslinked-feed": "Связанные правки", "recentchangeslinked-toolbox": "Связанные правки", "recentchangeslinked-title": "Связанные правки для «$1»", - "recentchangeslinked-summary": "Это список недавних изменений в страницах, на которые ссылается указанная страница (или входящих в указанную категорию).\nСтраницы, входящие в [[Special:Watchlist|ваш список наблюдения]] '''выделены'''.", + "recentchangeslinked-summary": "Введите имя страницы, чтобы увидеть изменения на страницах, ссылающихся на эту страницу или, наоборот, c неё. (Чтобы увидеть членов категории, введите Category:Название категории). Изменения на страницах [[Special:Watchlist|вашего списка наблюдения]] отмечены <strong>жирным шрифтом</strong>.", "recentchangeslinked-page": "Название страницы:", "recentchangeslinked-to": "Наоборот, показать изменения на страницах, которые ссылаются на указанную страницу", "recentchanges-page-added-to-category": "[[:$1]] добавлена в категорию", @@ -1767,9 +1775,17 @@ "uploadstash-bad-path-unrecognized-thumb-name": "Нераспознанное имя миниатюры.", "uploadstash-bad-path-no-handler": "Не найден обработчик для mime-типа $1 файла $2.", "uploadstash-bad-path-bad-format": "Ключ «$1» — в неподходящем формате.", + "uploadstash-file-not-found": "Ключ «$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": "Отсутствует заголовок 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": "Пустое расширение.", "uploadstash-zero-length": "Файл нулевой длины.", "invalid-chunk-offset": "Недопустимое смещение фрагмента", @@ -1855,7 +1871,7 @@ "upload-disallowed-here": "Вы не можете перезаписать этот файл.", "filerevert": "Возврат к старой версии $1", "filerevert-legend": "Возвратить версию файла", - "filerevert-intro": "<span class=\"plainlinks\">Вы возвращаете '''[[Media:$1|$1]]''' к [$4 версии от $3, $2].</span>", + "filerevert-intro": "Вы возвращаете <strong>[[Media:$1|$1]]</strong> к [$4 версии от $3, $2].", "filerevert-comment": "Причина:", "filerevert-defaultcomment": "Возврат к версии от $2, $1 ($3)", "filerevert-submit": "Возвратить", @@ -1865,7 +1881,7 @@ "filedelete": "$1 — удаление", "filedelete-legend": "Удалить файл", "filedelete-intro": "Вы собираетесь удалить файл '''[[Media:$1|$1]]''' со всей его историей.", - "filedelete-intro-old": "<span class=\"plainlinks\">Вы удаляете версию '''[[Media:$1|$1]]''' от [$4 $3, $2].</span>", + "filedelete-intro-old": "Вы удаляете версию <strong>[[Media:$1|$1]]</strong> от [$4 $3, $2].", "filedelete-comment": "Причина:", "filedelete-submit": "Удалить", "filedelete-success": "'''$1''' был удалён.", @@ -2724,6 +2740,7 @@ "import-mapping-subpage": "Импортировать как подстраницы следующей страницы:", "import-upload-filename": "Имя файла:", "import-upload-username-prefix": "Префиксы интервики:", + "import-assign-known-users": "Связать правки с локальными участниками, когда участники с такими именами существуют.", "import-comment": "Примечание:", "importtext": "Пожалуйста, экспортируйте страницу из исходной вики, используя [[Special:Export|соответствующий инструмент]]. Сохраните файл на диск, а затем загрузите его сюда.", "importstart": "Импортирование страниц…", @@ -2732,6 +2749,7 @@ "imported-log-entries": "{{PLURAL:$1|Импортирована $1 запись|Импортированы $1 записи|Импортировано $1 записей}} журнала.", "importfailed": "Не удалось импортировать: $1", "importunknownsource": "Неизвестный тип импортируемой страницы", + "importnoprefix": "Не указан префикс интервики", "importcantopen": "Невозможно открыть импортируемый файл", "importbadinterwiki": "Неправильная интервики-ссылка", "importsuccess": "Импортирование выполнено!", @@ -3085,7 +3103,7 @@ "exif-exposureindex": "Индекс экспозиции", "exif-sensingmethod": "Тип сенсора", "exif-filesource": "Источник файла", - "exif-scenetype": "Сценан кеп", + "exif-scenetype": "Тип сцены", "exif-customrendered": "Дополнительная обработка", "exif-exposuremode": "Режим выбора экспозиции", "exif-whitebalance": "Баланс белого", @@ -3433,6 +3451,8 @@ "autosumm-blank": "Полностью удалено содержимое страницы", "autosumm-replace": "Содержимое страницы заменено на «$1»", "autoredircomment": "Перенаправление на [[$1]]", + "autosumm-removed-redirect": "Удалённое перенаправление на [[$1]]", + "autosumm-changed-redirect-target": "Перенаправление изменено с [[$1]] на [[$2]]", "autosumm-new": "Новая страница: «$1»", "autosumm-newblank": "Создана пустая страница", "size-bytes": "$1 {{PLURAL:$1|байт|байта|байт}}", @@ -3640,10 +3660,12 @@ "tag-mw-changed-redirect-target-description": "Правки, которые изменяют цель перенаправления", "tag-mw-blank": "Очистка", "tag-mw-blank-description": "Правки, которые очищают страницу", - "tag-mw-replace": "Заменено", + "tag-mw-replace": "заменено", "tag-mw-replace-description": "Правки, которые удаляют более 90 % содержимого страницы", - "tag-mw-rollback": "Откат", + "tag-mw-rollback": "откат", "tag-mw-rollback-description": "Правки, которые откатывают предыдущие правки по нажатию ссылки отката", + "tag-mw-undo": "отмена", + "tag-mw-undo-description": "Правки, отменяющие предыдущие с помощью ссылки «отменить»", "tags-title": "Метки", "tags-intro": "На этой странице приведён список меток, которыми программное обеспечение отмечает правки, а также значения этих меток.", "tags-tag": "Имя метки", diff --git a/languages/i18n/sat.json b/languages/i18n/sat.json index bc2270bfc5..08d1c7b27e 100644 --- a/languages/i18n/sat.json +++ b/languages/i18n/sat.json @@ -185,10 +185,10 @@ "newpage": "ᱱᱟᱶᱟ ᱥᱟᱦᱴᱟ", "talkpagelinktext": "ᱨᱚᱲ", "specialpage": "ᱵᱤᱥᱮᱥ ᱥᱟᱦᱴᱟ", - "personaltools": "ᱱᱤᱡᱮᱨᱟᱜ ᱦᱟᱹᱛᱤᱭᱟᱹᱨᱠᱳ", + "personaltools": "ᱱᱤᱡᱮᱨᱟᱜ ᱦᱟᱹᱛᱭᱟᱹᱨᱠᱳ", "talk": "ᱜᱟᱞᱢᱟᱨᱟᱣ", "views": "ᱧᱮᱞᱚᱜᱚᱜ", - "toolbox": "ᱦᱟᱹᱛᱤᱭᱟᱹᱨ", + "toolbox": "ᱦᱟᱹᱛᱭᱟᱹᱨ", "imagepage": "ᱨᱮᱫ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ", "mediawikipage": "ᱠᱷᱚᱵᱚᱨ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ", "templatepage": "ᱪᱷᱟᱸᱪ ᱥᱟᱦᱴᱟ ᱩᱫᱩᱜᱽ ᱢᱮ", @@ -409,7 +409,7 @@ "changepassword": "Uku nombor bodolme", "resetpass_header": "Ekauná¹­ oku namber bodol", "oldpassword": "Mare uku nombor", - "newpassword": "ᱱᱟᱣᱟ ᱩᱠᱩ ᱮᱞᱥᱚᱝᱺ", + "newpassword": "ᱱᱟᱶᱟ ᱫᱟᱱᱟᱝᱥᱟᱵᱟᱫᱽᱺ", "retypenew": "Doṛhate oku namber olme", "resetpass_submit": "Oku namber joṛao ar bhitri bolok", "changepassword-success": "Amaḱ oku namber do napayte bodolena!\nNitoḱ do am bhitritem boloḱkana...", @@ -419,7 +419,7 @@ "botpasswords-label-update": "ᱟᱹᱨᱩ ᱯᱷᱮᱨᱟᱣ", "botpasswords-label-cancel": "ᱵᱟᱫᱽ", "botpasswords-label-delete": "ᱜᱮᱫ ᱜᱤᱰᱤ", - "botpasswords-label-resetpassword": "ᱱᱟᱣᱟᱛᱮ ᱩᱠᱩ ᱮᱞᱥᱚᱝ ᱮᱢᱢᱮ", + "botpasswords-label-resetpassword": "ᱱᱟᱣᱟᱛᱮ ᱫᱟᱱᱟᱝᱥᱟᱵᱟᱫᱽ ᱮᱢ", "botpasswords-label-grants-column": "ᱦᱩᱭᱠᱟᱱ", "botpasswords-bad-appid": "ᱵᱚᱴ ᱧᱤᱛᱩᱢ \"$1\" ᱵᱟᱝ ᱴᱷᱤᱠᱟ᱾", "botpasswords-created-title": "ᱵᱚᱴ ᱩᱠᱩ ᱮᱞᱥᱚᱝ ᱛᱮᱭᱟᱨᱱᱟ", @@ -634,8 +634,8 @@ "powersearch-togglelabel": "Sendra", "powersearch-toggleall": "Sanamaḱ", "powersearch-togglenone": "Okaá¹­aḱ hõ baṅ", - "preferences": "Pạsindko", - "mypreferences": "Pạsindko", + "preferences": "ᱠᱩᱥᱤᱠᱚ", + "mypreferences": "ᱠᱩᱥᱤᱠᱚ", "prefs-edits": "ᱥᱟᱯᱲᱟᱣᱟᱜ ᱮᱞ:", "prefs-skin": "Harta", "skin-preview": "Ńel, Unuduḱ", @@ -1000,7 +1000,7 @@ "tooltip-watch": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱟᱢᱟᱜ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱡᱚᱲᱟᱣᱢᱮ", "tooltip-rollback": "ᱫᱚᱲᱦᱟᱛᱮ ᱢᱤᱫ ᱫᱷᱟᱣ ᱞᱤᱱ ᱛᱮ contributor ᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱥᱟᱯᱲᱟᱣ \"ᱜᱷᱩᱨᱞᱟᱹ ᱟᱹᱪᱩᱨ\" ᱢᱮ", "tooltip-undo": "Noa joṛao kạmire ulá¹­ao \"bạgiyaḱme\" ar ńeloḱ lekate noa joṛao jhicme. Noa do am guḍ karon joṛaoe ektiyariye emama.", - "tooltip-preferences-save": "Pạsindko rukhiyaymẽ", + "tooltip-preferences-save": "ᱠᱩᱥᱤᱠᱚ ᱨᱩᱠᱷᱤᱭᱟᱹᱭᱢᱮ", "tooltip-summary": "Khaá¹­ote guá¹­ katha bhoraome", "others": "Eá¹­agaḱko", "simpleantispam-label": "Ená¹­i espam ńel\nDo <strong>not</strong> noa purạome!", diff --git a/languages/i18n/sd.json b/languages/i18n/sd.json index b889bd4bd7..0cd11fed00 100644 --- a/languages/i18n/sd.json +++ b/languages/i18n/sd.json @@ -300,16 +300,16 @@ "cannotdelete": "$1 نالي صفحو يا فائيل ڊهي نہ سگھيو. ٿي سگھي ٿو تہ ڪنهن ان کي اڳ Û¾ ئي ڊاهي ڇڏيو هجي.", "cannotdelete-title": "$1 نالي صفحي کي ڊاهي نہ ٿا سگھون.", "badtitle": "خراب عنوان", - "badtitletext": "صفحي جو گھربل عنوان ڪار ڪونهي، يا خالي آهي، يا وري غيردرست طريقي سان ڳنڍيل بين‌الزباني يا بين‌الوڪي عنوان آهي. \nان Û¾ هڪ يا هڪ کان وڌيڪ اهڙا اکر موجود آهن، جيڪي عنوان Û¾ استعمال ڪري نہ ٿا سگھجن.", - "title-invalid-utf8": "صفحي جي ڄاڻايل عنوان Û¾ ناقابل ڪار يُو ٽِي ايف اکر شامل آهن.", - "title-invalid-interwiki": "ڄاڻايل عنوان Û¾ اهڙو بين‌الوڪِي ڳنڍڻو شامل آهي، جيڪو عنوانن Û¾ استعمال ڪري نہ ٿو سگھجي.", - "title-invalid-characters": "صفحي جي ڄاڻايل عنوان Û¾ ناقابل ڪار اکر شامل آهن: $1", - "title-invalid-leading-colon": "صفحي جي ڄاڻايل عنوان جي ابتدا Û¾ ناقابل ڪار ڪالن شامل آهي.", + "badtitletext": "صفحي جو گھربل عنوان ڪار ڪونهي، يا خالي آهي، يا وري غيردرست طريقي سان ڳنڍيل بين‌الزباني يا بين‌الوڪي عنوان آهي. \nان Û¾ هڪ يا هڪ کان وڌيڪ اهڙا اکر موجود آهن، جيڪي عنوان Û¾ استعمال ڪري نٿا سگھجن.", + "title-invalid-utf8": "صفحي جي ڄاڻايل عنوان Û¾ ناقابلِڪار يُو ٽِيئيف-8 ترتيب شامل آھي.", + "title-invalid-interwiki": "ڄاڻايل عنوان Û¾ اهڙو بين‌الوڪِي ڳنڍڻو شامل آهي، جيڪو عنوانن Û¾ استعمال ڪري نٿو سگھجي.", + "title-invalid-characters": "صفحي جي ڄاڻايل عنوان Û¾ ناقابلِڪار اکر شامل آهن: \"$1\".", + "title-invalid-leading-colon": "صفحي جي ڄاڻايل عنوان جي ابتدا Û¾ ناقابلِڪار ڪالن شامل آهي.", "viewsource": "ڪوڊ ڏسو", "viewsource-title": "$1 جو ڪوڊ ڏسو", - "protectedpagetext": "هيءُ صفحو ترميمن کان تحفظيل آهي.", - "viewsourcetext": "توهان هن صفحي جو ڪوڊ ڏسي Û½ نقل ڪري سگھو ٿا:", - "namespaceprotected": "توهان کي نانءُ پولار '''$1''' جا صفحا سنوارڻ جا اختيار ناهن.", + "protectedpagetext": "هيءُ صفحو ترميمن Û½ ٻين عملن کان بچائڻ لاءِ تحفظيل آهي.", + "viewsourcetext": "توهان هن صفحي جو ڪوڊ ڏسي Û½ نقل ڪري سگھو ٿا.", + "namespaceprotected": "توهان کي نانءُپولار <strong>$1</strong> جا صفحا سنوارڻ جا اختيار ناهن.", "mycustomcssprotected": "توهان کي هيءُ CSS صفحو سنوارڻ جي اجازت نہ آهي.", "mycustomjsprotected": "توهان کي هيءُ جاوا اسڪرپٽ صفحو سنوارڻ جي اجازت حاصل ڪانهي.", "myprivateinfoprotected": "توهان کي پنهنجي ذاتي معلومات سنوارڻ جي اجازت حاصل نہ آهي.", @@ -320,7 +320,7 @@ "virus-unknownscanner": "اڻڄاتل نِس وائرس:", "cannotlogoutnow-title": "ھاڻي خارج نٿو ٿي سگھجي", "cannotlogoutnow-text": "$1 استعمال ڪرڻ دوران خارج ٿيڻ ممڪن نہ آھي.", - "welcomeuser": "ڀلي ڪري آيا، $1!", + "welcomeuser": "ڀليڪار، $1!", "yourname": "واپرائيندڙ-نانءُ:", "userlogin-yourname": "واپرائيندڙ-نانءُ", "userlogin-yourname-ph": "پنھنجو يوزرنانءُ ڄاڻايو", @@ -385,7 +385,7 @@ "nosuchusershort": "\"$1\" نالي ڪو بہ واپرائيندڙ ناهي.\nپنھنجي هِجي جي Ù¾Úª ڪندا.", "nouserspecified": "توهان کي ڪو واپرائيندڙ-نان‎ءُ ڄاڻائڻو پوندو.", "login-userblocked": "هيءُ واپرائيندڙ بندشيل آهي. داخل ٿيڻ جي اجازت نٿي ڏجي.", - "wrongpassword": "ڏنل ڳجھولفظ غير درست آهي. مھرباني ڪري ٻيھر ڪوشش ڪندا.", + "wrongpassword": "ڏنل واپرائيندڙ-نانءُ يا ڳجھولفظ غير درست آهي.\nمھرباني ڪري ٻيھر ڪوشش ڪندا.", "wrongpasswordempty": "ڏنل ڳجھولفظ خالي هو.\nمهرباني ڪري وري ڪوشش ڪندا.", "passwordtooshort": "ڳجھولفظ Ú¯Ú¾Ù½ Û¾ Ú¯Ú¾Ù½ {{PLURAL:$1|1 اکر|$1 اکرَن}} تي ٻڌل هوڻ گھرجي.", "passwordtoolong": "ڳجھولفظ {{PLURAL:$1|1 اکر|$1 اکرن}} کان وڏو نٿو ٿي سگھي.", @@ -412,13 +412,13 @@ "login-abort-generic": "توهان جو داخل ٿيڻ ناڪام ويو - بند ڪيل", "login-migrated-generic": "توهان جو کاتو لڏي چڪو آهي، Û½ هن وڪيءَ تي توهان جو واپرائيندڙ-نان‎ءُ هاڻي وجود نٿو رکي.", "loginlanguagelabel": "ٻولي: $1", - "createacct-another-realname-tip": "اصل نالو ڄاڻائڻ اختياري آهي. جيڪڏهن توهان اصل نالو ڄاڻايو ٿا، تہ اهو توهان کي توهان جي ڪم جي مڃتا ڏيڻ لاءِ ڪم آندو ويندو.", + "createacct-another-realname-tip": "اصل نالو ڄاڻائڻ اختياري آھي.\nجيڪڏھن توھان اھو ڄاڻائڻ چونڊيو ٿا، تہ اھو واپرائيندڙ کي انھن جي ڪم جي مڃتا ڏيڻ لاءِ ڪم آندو ويندو.", "pt-login": "داخل ٿيو", "pt-login-button": "داخل ٿيو", "pt-login-continue-button": "داخل ٿيڻ جاري رکو", "pt-createaccount": "کاتو کوليو", "pt-userlogout": "خارج ٿيو", - "php-mail-error-unknown": "پي ايڇ پي جي ڪاڄ اندر اڻڄاتل چُڪَ.", + "php-mail-error-unknown": "پي ايڇ پي جي ڪاڄ() اندر اڻڄاتل چُڪَ.", "user-mail-no-addy": "برقٽپال پتو ڄاڻائڻ کان سواءِ برقٽپال اماڻڻ جي ڪوشش ڪئي وئي.", "changepassword": "ڳجھولفظ تبديل ڪريو", "resetpass_announce": "داخل ٿيڻ جو عمل پورو ڪرڻ لاءِ، توهان کي نئون ڳجھولفظ اختيار مقرر ڪرڻو پوندو.", @@ -435,7 +435,7 @@ "botpasswords-label-create": "سرجيو", "botpasswords-label-update": "تجديد", "botpasswords-label-cancel": "رد", - "botpasswords-label-delete": "ڊاهيو", + "botpasswords-label-delete": "ڊاھيو", "botpasswords-label-resetpassword": "ڳجھولفظ ٻيھر مقرر ڪريو", "botpasswords-label-grants-column": "منظور", "botpasswords-bad-appid": "بوٽ نانءُ \"$1\" قابلِڪار ناھي.", @@ -454,7 +454,7 @@ "passwordreset": "ڳجھولفظ مَٽايو", "passwordreset-text-one": "برقٽپال ذريعي عارضي ڳجھولفظ حاصل ڪرڻ لاءِ هيءُ فارم پُر ڪريو.", "passwordreset-disabled": "هن وڪيءَ تي ڳجھولفظ ٻيھر مقرر ڪرڻ وارو چارو غير فعال بڻايو ويو آهي.", - "passwordreset-emaildisabled": "هن وڪيءَ تي برق‌ٽپال واريون خصوصيتون غير فعال بڻايون ويون آهن.", + "passwordreset-emaildisabled": "ھن وڪيءَ تي برق‌ٽپال واريون خصوصيتون غير فعال بڻايون ويون آهن.", "passwordreset-username": "واپرائيندڙ-نانءُ:", "passwordreset-domain": "ميدان:", "passwordreset-email": "برقٽپال پتو:", @@ -477,7 +477,7 @@ "bold_sample": "گھري لکت", "bold_tip": "گھري لکت", "italic_sample": "ترڇي لکت", - "italic_tip": "ترڇي لکت", + "italic_tip": "ٽيڏي لکت", "link_sample": "ڳنڍڻي جو عنوان", "link_tip": "داخلي ڳنڍڻو", "extlink_sample": "http://www.example.com ڳنڍڻي جو عنوان", @@ -503,7 +503,7 @@ "showdiff": "تبديليون ڏيکاريو", "anoneditwarning": "<strong>چتاءُ:</strong> توھان داخل ٿيل نہ آھيو. توھان جو آءِپي پتو عوامي طور ظاھر ٿيندو جي توھان ڪي ترميمون ڪريو ٿا. جيڪڏھن توھان <strong>[$1 داخل ٿيو]</strong> ٿا يا <strong>[$2 کاتو کوليو]</strong> ٿا، تہ ٻين فائدن سان گڏ توھان جون ترميمون توھان جي يوزرنانءَ سان منسوب ڪيون وينديون.", "anonpreviewwarning": "توهان داخل ٿيل نہ آهيو. جيڪڏهن توهان صفحي Û¾ تبديليون سانڍيون تہ اهڙين تبديلين ساڻ توهان جو آءِپي پتو درج ڪيو ويندو.", - "missingcommenttext": "براءِ مھرباني هيٺ پنهنجو تاثر درج ڪندا.", + "missingcommenttext": "براءِ مھرباني ڪو تاثر درج ڪندا.", "summary-preview": "تت جي پيش نگاھ:", "subject-preview": "موضوع جي پيش نگاھ:", "blockedtitle": "واپرائيندڙ بندشيل آهي", @@ -531,7 +531,7 @@ "editingsection": "ترميم ھيٺ $1 (سيڪشن)", "editingcomment": "ترميم هيٺ $1 (نئون سيڪشن)", "editconflict": "ترميمي تڪرار: $1", - "yourtext": "توهان جو ٽيڪسٽ", + "yourtext": "توهان جو متن", "storedversion": "سانڍيل مسودو", "yourdiff": "تفاوت", "copyrightwarning": "ياد رکندا ته {{SITENAME}} لاءِ سموريون ڀاڱيداريون $2 تحت پڌريون ڪجن ٿيون (تفصيلن لاءِ $1 ڏسندا). اوهان جي تحرير کي {{SITENAME}} جي قائدن تحت ترميمي سگهجي ٿو. جيڪڏهن اوهان نه ٿا چاهيو ته اوهان جي لکڻين کي بي رحميءَ سان ترميميو وڃي يا ورهائي عام ڪيو وڃي ته پوءِ پنهنجي لکڻي هتي جمع نه ڪرايو. پنهنجو مواد هتي جمع ڪرڻ جو مطلب هوندو ته توهان کي جمع ڪرايل مواد جي مفت فراهمي Û½ کُليل تبديليءَ تي ڪو به اعتراز ناهي.<br />\nتوهان اهڙي Ù¾Úª ڏيڻ جا پابند Ù¾Ú» آهيو ته توهان جو جمع ڪرايل مواد توهان جو پنهنجو لکيل آهي يا وري توهان ڪنهن مفت وسيلي تان ڪاپي ڪيو آهي.\n'''تحفظيل حق Û½ واسطا رکندڙ مواد واسطيدار مالڪ کان اڳواٽ اجازت وٺڻ کان سواءِ هتي جمع نه ڪريو.'''", @@ -742,7 +742,7 @@ "timezoneregion-europe": "يُورپ", "timezoneregion-indian": "سنڌي ساگر", "timezoneregion-pacific": "ماٺو ساگر", - "allowemail": "ٻين يُوزرس کان ايندڙ ٽپال بحال ڪريو", + "allowemail": "ٻين واپرائيندڙن کي مون ڏانھن برقٽپال ڪرڻ جي اجازت ڏيو", "prefs-searchoptions": "ڳولا", "prefs-namespaces": "نانءُپولار", "default": "ڏنل", @@ -911,7 +911,7 @@ "rcfilters-quickfilters": "سانڍيل ڇاڻيون", "rcfilters-savedqueries-defaultlabel": "سانڍيل ڇاڻيون", "rcfilters-restore-default-filters": "ڏنل ڇاڻيون ريسٽور ڪريو", - "rcfilters-search-placeholder": "تازيون تبديليون ڇاڻيو (ڇانگيو يا لکڻ شروع ڪريو)", + "rcfilters-search-placeholder": "تبديليون ڇاڻيو (مينيو استعمال ڪريو يا ڇاڻيءَ جي ڳولا ڪريو)", "rcfilters-empty-filter": "ڪي بہ سرگرم ڇاڻيون ناھن. سڀ ڀاڱيداريون ڏيکاريل آھن.", "rcfilters-filterlist-title": "ڇاڻيون", "rcfilters-filterlist-whatsthis": "هي ڪيئن ڪم ڪن ٿا؟", diff --git a/languages/i18n/sgs.json b/languages/i18n/sgs.json index fd93bd9cc1..392a55b98c 100644 --- a/languages/i18n/sgs.json +++ b/languages/i18n/sgs.json @@ -163,6 +163,7 @@ "searcharticle": "Ēk", "history": "Poslapė istuorėjė", "history_short": "Istuorėjė", + "history_small": "istuorėjė", "updatedmarker": "pakeist nug tada, kāp lonkiaus paskotėni sÄ«ki", "printableversion": "Atmains spausdėnėmou", "permalink": "NÅ«latėnė nÅ«ruoda", @@ -187,6 +188,7 @@ "views": "Parveizė̄jėmā", "toolbox": "Rakondā", "tool-link-userrights": "MainÄ«tė {{GENDER:$1|nauduotuoja|nauduotuojės}} gropės", + "tool-link-userrights-readonly": "Veizietė {{GENDER:$1|nauduotuojė}} gropės", "tool-link-emailuser": "RaÅ¡Ä«tė gromata {{GENDER:$1|tamou nauduotuojou}}", "imagepage": "Veizietė abruozdielė poslapi", "mediawikipage": "RuodÄ«tė pranešėma poslapi", @@ -205,7 +207,7 @@ "jumptonavigation": "narÅ¡Ä«ms", "jumptosearch": "paėiÅ¡ka", "view-pool-error": "AtsėpraÅ¡uom, bat serverē daba Ä«r parkrautė.\nNuognē pardaug nauduotoju skait ton poslapi.\nPraÅ¡uom palaukat ė mieginkat i ton poslapi patekt apent.\n\n$1", - "generic-pool-error": "AtsėpraÅ¡uom, ale serverē daba Ä«r parkrautė.\nNuognē pardaug nauduotoju skaita ton poslapi.\nPraÅ¡uom palaukat ė mieginkat i ton poslapi patekt apent.\n\n$1", + "generic-pool-error": "AtsipraÅ¡uom, bet serverē daba Ä«r parkrauti.\nNuognē pardaug nauduotoju miegėn jongtėis.\nPraÅ¡uom palaukėt ė mieginkėt i ton poslapi patekt apent.\n\n$1", "pool-errorunknown": "Nežėnuoma klaida", "poolcounter-usage-error": "Naudojėma soklÄ«dėms: $1", "aboutsite": "Aple {{SITENAME}}", @@ -1414,6 +1416,7 @@ "mycontris": "Duovis", "anoncontribs": "KÅ«riejē", "contribsub2": "Nauduotuojė $1 ($2)", + "nocontribs": "Vagol esamus nÅ«statÄ«mus nieka nerast.", "uctop": " (vielÄ«bs)", "month": "Nug mienėsė (ėr onkstiau):", "year": "Nug metu (ėr onkstiau):", @@ -1681,6 +1684,7 @@ "pageinfo-watchers": "Kieravuotuoju skaitlios", "pageinfo-few-watchers": "Mažiau kap $1 {{PLURAL:$1|kieravuotuos|kieravuotuojē|kieravuotuoju}}", "pageinfo-redirects-name": "NÅ«sokėmu ont ton poslapė skaitlios", + "pageinfo-subpages-name": "Poslapie esons poslapė daliÅ« skaitlios.", "pageinfo-firstuser": "Poslapė dėrbiejē", "pageinfo-firsttime": "Padėrbėma čiesos", "pageinfo-lastuser": "VielÄ«bs dėrbies", diff --git a/languages/i18n/shi.json b/languages/i18n/shi.json index cab90a42bd..38894c0b7a 100644 --- a/languages/i18n/shi.json +++ b/languages/i18n/shi.json @@ -843,7 +843,6 @@ "sp-contributions-toponly": "ⵎⵍ ⵖⴰⵔ ⵉⵙⵏⴼⵉⵍⵏ ⴳⴰⵏⵉⵏ ⵜⵓⵏⵖⵉⵍⵉⵏ ⵜⵉⵎⵉⵔⴰⵏⵉⵏ", "sp-contributions-newonly": "ⵎⵍ ⵖⴰⵔ ⵉⵙⵏⴼⵉⵍⵏ ⴳⴰⵏⵉⵏ ⵉⵙⵏⵓⵍⴼⵓⵜⵏ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ", "sp-contributions-submit": "ⵙⵉⴳⴳⵍ", - "sp-contributions-explain": "↓", "whatlinkshere": "ⵎⴰⴷ ⵉⵜⵜⴰⵡⵉⵏ ⵙ ⵖⵉⴷ", "whatlinkshere-title": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵜⴰⵡⵉⵏⵉⵏ ⵙ \"$1\"", "whatlinkshere-page": "ⵜⴰⵙⵏⴰ:", diff --git a/languages/i18n/shn.json b/languages/i18n/shn.json index 3f7ff5af2b..74f24b1155 100644 --- a/languages/i18n/shn.json +++ b/languages/i18n/shn.json @@ -2218,6 +2218,7 @@ "imgmultipagenext": "ၼႃႈလိၵ်ႈတေမႃး", "imgmultigo": "ၵႂႃႇ!", "imgmultigoto": "ၵႂႃႇၸူး ၼႃႈလိၵ်ႈ $1", + "autosumm-new": "ၵေႃႇသၢင်ႈၼႃႈလိၵ်ႈဝႆႉ တင်း \"$1\"", "watchlistedit-normal-title": "မႄးထတ်း သဵၼ်ႈမၢႆပႂ်ႉတူၺ်း", "watchlistedit-normal-legend": "ထွၼ်ပႅတ်ႈ ႁူဝ်ၶေႃႈ တမ်ႈတီႈ သဵၼ်ႈမၢႆမႂ်ႉတူၺ်း", "watchlistedit-normal-submit": "ထွၼ်ပႅတ်ႈ ႁူဝ်ၶေႃႈ", diff --git a/languages/i18n/skr-arab.json b/languages/i18n/skr-arab.json index be42b72e4a..22399b3a02 100644 --- a/languages/i18n/skr-arab.json +++ b/languages/i18n/skr-arab.json @@ -1017,6 +1017,7 @@ "specialpages": "خاص ورقے", "tag-filter": "[[Special:Tags|Tag]] نتارا:", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ|ٹیگز}}]]: $2)", + "tag-mw-undo": "واپس", "tags-active-yes": "ڄیا", "tags-active-no": "کو", "tags-hitcount": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}", diff --git a/languages/i18n/sl.json b/languages/i18n/sl.json index ea0fdd3fe3..aa5b420cbd 100644 --- a/languages/i18n/sl.json +++ b/languages/i18n/sl.json @@ -1014,6 +1014,7 @@ "timezoneregion-indian": "Indijski ocean", "timezoneregion-pacific": "Tihi ocean", "allowemail": "Drugim uporabnikom omogoči poÅ¡iljanje e-poÅ¡te", + "email-allow-new-users-label": "Dovoli e-poÅ¡to od čisto novih uporabnikov", "email-blacklist-label": "Prepreči naslednjim uporabnikom, da mi poÅ¡iljajo e-poÅ¡to:", "prefs-searchoptions": "Iskanje", "prefs-namespaces": "Imenski prostori", @@ -1184,6 +1185,7 @@ "right-siteadmin": "Zaklepanje in odklepanje baze podatkov", "right-override-export-depth": "Izvoz strani, vključno s povezaimi straneh do globine 5", "right-sendemail": "PoÅ¡iljanje e-poÅ¡te drugim uporabnikom", + "right-sendemail-new-users": "PoÅ¡lji e-poÅ¡to uporabnikom brez zabeleženih dejanj", "right-managechangetags": "Ustvarjanje in (dez)aktivacijo [[Special:Tags|oznak]]", "right-applychangetags": "Uveljavitev [[Special:Tags|oznak]] skupaj s spremembami", "right-changetags": "Dodajanje in odstranjevanje poljubnih [[Special:Tags|oznak]] na posameznih redakcijah in dnevniÅ¡kih vnosih", @@ -1285,6 +1287,7 @@ "recentchanges-noresult": "V danem obdobju nobena sprememba ne ustreza tem merilom.", "recentchanges-timeout": "Čas iskanja je potekel. Poskusite uporabiti drugačne parametre iskanja.", "recentchanges-network": "Zaradi tehnične napake rezultatov ne moremo naložiti. Prosimo, poskusite osvežiti stran.", + "recentchanges-notargetpage": "Zgoraj vnesite ime strani, da vidite spremembe, povezane s to stranjo.", "recentchanges-feed-description": "Spremljajte zadnje spremembe wikija prek tega vira.", "recentchanges-label-newpage": "To urejanje je ustvarilo novo stran", "recentchanges-label-minor": "To je manjÅ¡e urejanje", @@ -1416,11 +1419,11 @@ "rcfilters-watchlist-showupdated": "Spremembe strani, ki jih niste obiskali od zadnje spremembe, so prikazane <strong>krepko</strong>, z močnimi oznakami.", "rcfilters-preference-label": "Skrij izboljÅ¡ano različico Zadnjih sprememb", "rcfilters-preference-help": "Povrne preoblikovanje vmesnika leta 2017 in vsa takrat in od takrat dodana orodja.", - "rcfilters-filter-showlinkedfrom-label": "Pokaži spremembe na straneh, na katere se povezuje:", - "rcfilters-filter-showlinkedfrom-option-label": "Pokaži spremembe na straneh, povezanih S strani", - "rcfilters-filter-showlinkedto-label": "Pokaži spremembe na straneh, povezane na:", - "rcfilters-filter-showlinkedto-option-label": "Pokaži spremembe na straneh, povezanih NA stran", - "rcfilters-target-page-placeholder": "Izberite stran", + "rcfilters-filter-showlinkedfrom-label": "Pokaži spremembe na straneh, na katere se povezuje", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Strani, na katere kaže</strong> izbrana stran", + "rcfilters-filter-showlinkedto-label": "Pokaži spremembe na straneh, ki kažejo na", + "rcfilters-filter-showlinkedto-option-label": "<strong>Strani, ki kažejo na</strong> izbrano stran", + "rcfilters-target-page-placeholder": "Vnesite ime strani", "rcnotefrom": "{{PLURAL:$5|Navedena je sprememba|Navedeni sta spremembi|Navedene so spremembe}} od <strong>$3 $4</strong> dalje (prikazujem jih do <strong>$1</strong>).", "rclistfromreset": "Ponastavi izbiro datuma", "rclistfrom": "Prikaži spremembe od $3 $2 naprej", @@ -1465,7 +1468,7 @@ "recentchangeslinked-feed": "Sorodne spremembe", "recentchangeslinked-toolbox": "Sorodne spremembe", "recentchangeslinked-title": "Spremembe, povezane z \"$1\"", - "recentchangeslinked-summary": "To je seznam nedavnih sprememb strani povezanih na določeno stran (ali iz določene kategorije).\nStrani iz [[Special:Watchlist|vaÅ¡ega spiska nadzorov]] so '''odebeljene'''.", + "recentchangeslinked-summary": "Vnesite ime strani, da vidite spremembe strani, povezanih na ali s te strani. (Da vidite člane kategorije, vnesite Kategorija:Ime kategorije.)\nStrani z [[Special:Watchlist|vaÅ¡ega spiska nadzorov]] so <strong>odebeljene</strong>.", "recentchangeslinked-page": "Naslov strani:", "recentchangeslinked-to": "Prikaži spremembe na določeno stran povezanih strani", "recentchanges-page-added-to-category": "[[:$1]] dodano v kategorijo", @@ -3450,6 +3453,8 @@ "tag-mw-replace-description": "Urejanja, ki odstranijo več kot 90 % vsebine strani", "tag-mw-rollback": "Vrnitev", "tag-mw-rollback-description": "Urejanja, ki vrnejo prejÅ¡nja urejanja s povezavo za vrnitev", + "tag-mw-undo": "Razveljavljeno", + "tag-mw-undo-description": "Urejanja, ki razveljavijo prejÅ¡nja urejanja z uporabo povezave za razveljavitev", "tags-title": "Etikete", "tags-intro": "Ta stran navaja etikete, s katerimi lahko programje označi urejanja, in njihov pomen.", "tags-tag": "Ime oznake", diff --git a/languages/i18n/sr-ec.json b/languages/i18n/sr-ec.json index f499863baa..b9be5d7848 100644 --- a/languages/i18n/sr-ec.json +++ b/languages/i18n/sr-ec.json @@ -3260,6 +3260,7 @@ "autosumm-blank": "Уклоњен целокупан садржај странице", "autosumm-replace": "Замењен садржај странице са „$1“", "autoredircomment": "Преусмерење на [[$1]]", + "autosumm-removed-redirect": "Уклоњено преусмјерење ка [[$1]]", "autosumm-new": "Нова страница: $1", "autosumm-newblank": "Направљена празна страница", "size-bytes": "$1 {{PLURAL:$1|бајт|бајта|бајтова}}", @@ -3437,6 +3438,9 @@ "tag-list-wrapper": "([[Special:Tags|$1 {{PLURAL:$1|ознака|ознаке|ознака}}]]: $2)", "tag-mw-contentmodelchange": "промена модела садржаја", "tag-mw-contentmodelchange-description": "Измене које мењају модел садржаја странице", + "tag-mw-new-redirect": "Ново преусмјерење", + "tag-mw-removed-redirect": "Уклоњено преусмјерење", + "tag-mw-rollback": "Враћање", "tags-title": "Ознаке", "tags-intro": "На овој страници је наведен списак ознака с којима програм може да означи измене и његово значење.", "tags-tag": "Назив ознаке", diff --git a/languages/i18n/sr-el.json b/languages/i18n/sr-el.json index 73a9c93a28..ea8f10fb25 100644 --- a/languages/i18n/sr-el.json +++ b/languages/i18n/sr-el.json @@ -3209,6 +3209,7 @@ "tag-filter-submit": "Filtriraj", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Oznaka|Oznake}}]]: $2)", "tag-mw-contentmodelchange-description": "Izmene koje menjaju model sadržaja stranice", + "tag-mw-rollback": "Vraćanje", "tags-title": "Oznake", "tags-intro": "Na ovoj stranici je naveden spisak oznaka s kojima program može da označi izmene i njegovo značenje.", "tags-tag": "Naziv oznake", diff --git a/languages/i18n/sv.json b/languages/i18n/sv.json index 3277d16f1a..a08cea1271 100644 --- a/languages/i18n/sv.json +++ b/languages/i18n/sv.json @@ -1057,7 +1057,7 @@ "recentchangesdays-max": "Maximalt $1 {{PLURAL:$1|dygn}}", "recentchangescount": "Antal redigeringar som visas som standard:", "prefs-help-recentchangescount": "Detta inkluderar senaste ändringarna, sidhistorik och loggar.", - "prefs-help-watchlist-token2": "Detta är den hemliga nyckeln till webbflödet i din bevakningslista.\nNÃ¥gon som vet den kommer att kunna läsa din bevakningslista, sÃ¥ dela inte ut den.\n[[Special:ResetTokens|Klicka här om du behöver Ã¥terställa den]].", + "prefs-help-watchlist-token2": "Detta är den hemliga nyckeln till webbflödet i din bevakningslista.\nNÃ¥gon som vet den kommer att kunna läsa din bevakningslista, sÃ¥ dela inte ut den.\nOm du behöver [[Special:ResetTokens|kan du Ã¥terställa den]].", "savedprefs": "Dina inställningar har sparats", "savedrights": "Användargrupperna för {{GENDER:$1|$1}} har sparats.", "timezonelegend": "Tidszon:", @@ -1077,6 +1077,7 @@ "timezoneregion-indian": "Indiska oceanen", "timezoneregion-pacific": "Stilla havet", "allowemail": "LÃ¥t andra användare skicka e-post till mig", + "email-allow-new-users-label": "TillÃ¥t e-post frÃ¥n nyregistrerade användare", "email-blacklist-label": "Förhindra följande användare att skicka e-post till mig:", "prefs-searchoptions": "Sök", "prefs-namespaces": "Namnrymder", @@ -1246,6 +1247,7 @@ "right-siteadmin": "LÃ¥s och öppna databasen", "right-override-export-depth": "Exportera sidor inklusive länkade sidor till ett djup pÃ¥ 5", "right-sendemail": "Skicka e-post till andra användare", + "right-sendemail-new-users": "Skicka e-post till användare utan loggade handlingar", "right-managechangetags": "Skapa och (in)aktivera [[Special:Tags|märken]]", "right-applychangetags": "Tillämpa [[Special:Tags|märken]] tillsammans med ens ändringar", "right-changetags": "Lägg till och ta bort godtyckliga [[Special:Tags|märken]] pÃ¥ individuella sidversioner och loggposter.", @@ -1347,6 +1349,7 @@ "recentchanges-noresult": "Inga ändringar under den angivna perioden matchar dessa kriterier.", "recentchanges-timeout": "Tidsgränsen för denna sökning har gÃ¥tt ut. Du kan prova andra sökparametrar.", "recentchanges-network": "PÃ¥ grund av ett tekniskt fel kunde inga resultat läsas in. Försök att uppdatera sidan.", + "recentchanges-notargetpage": "Ange namnet pÃ¥ en sida ovan för att se ändringar som är relaterade till denna sida.", "recentchanges-feed-description": "Följ de senaste ändringarna i wikin genom detta flöde", "recentchanges-label-newpage": "Denna redigering skapade en ny sida", "recentchanges-label-minor": "Detta är en mindre ändring", @@ -1479,6 +1482,11 @@ "rcfilters-watchlist-showupdated": "Sidor som har ändrats sedan ditt senaste besök visas i <strong>fetstil</strong> med färgmarkering.", "rcfilters-preference-label": "Dölj den förbättrade versionen av Senaste ändringar", "rcfilters-preference-help": "Stänger det nydesignade gränssnittet frÃ¥n 2017 och alla verktyg som lades till frÃ¥n och med dÃ¥.", + "rcfilters-filter-showlinkedfrom-label": "Visa ändringar pÃ¥ sidor som länkas frÃ¥n", + "rcfilters-filter-showlinkedfrom-option-label": "<strong>Sidor som länkas frÃ¥n</strong> den valda sidan", + "rcfilters-filter-showlinkedto-label": "Visa ändringar pÃ¥ sidor som länkar till", + "rcfilters-filter-showlinkedto-option-label": "<strong>Sidor som länkar till</strong> den valda sidan", + "rcfilters-target-page-placeholder": "Ange namnet pÃ¥ en sida", "rcnotefrom": "Nedan visas {{PLURAL:$5|ändringen|ändringar}} sedan <strong>$3, $4</strong> (upp till <strong>$1</strong> ändringar visas).", "rclistfromreset": "Återställ datumval", "rclistfrom": "Visa nya ändringar frÃ¥n och med $2 $3", @@ -1523,7 +1531,7 @@ "recentchangeslinked-feed": "Relaterade ändringar", "recentchangeslinked-toolbox": "Relaterade ändringar", "recentchangeslinked-title": "Ändringar relaterade till $1", - "recentchangeslinked-summary": "Detta är en lista över de senaste ändringarna pÃ¥ sidor som länkas till frÃ¥n en given sida (eller pÃ¥ sidor som hör till en viss kategori).\nSidor pÃ¥ [[Special:Watchlist|din bevakningslista]] är markerade med '''fetstil'''.", + "recentchangeslinked-summary": "Ange namnet pÃ¥ en sida för att se ändringar pÃ¥ sidor som länkas till eller frÃ¥n denna sida. (För att se medlemmar i en kategori, skriv Kategori:Namnet pÃ¥ kategorin). Ändringar pÃ¥ sidor i [[Special:Watchlist|sin bevakningslista]] är <strong>fetstilade</strong>.", "recentchangeslinked-page": "Sidnamn:", "recentchangeslinked-to": "Visa ändringar pÃ¥ sidor med länkar till den givna sidan istället", "recentchanges-page-added-to-category": "[[:$1]] lades till i kategorin", @@ -3521,6 +3529,8 @@ "tag-mw-replace-description": "Redigeringar som tar bort mer än 90 % av innehÃ¥llet", "tag-mw-rollback": "Tillbakarullning", "tag-mw-rollback-description": "Redigeringar som rullar tillbaka en tidigare redigering med tillbakarullningslänken", + "tag-mw-undo": "Ångra", + "tag-mw-undo-description": "Redigeringar som Ã¥ngrar föregÃ¥ende redigeringar med Ã¥ngralänken", "tags-title": "Märken", "tags-intro": "Denna sida listar de taggar som mjukvaran kan markera en redigering med, och deras betydelse.", "tags-tag": "Märkesnamn", diff --git a/languages/i18n/th.json b/languages/i18n/th.json index 6e93b2fa8f..bfe9af0ecd 100644 --- a/languages/i18n/th.json +++ b/languages/i18n/th.json @@ -64,6 +64,7 @@ "tog-watchlisthideminor": "ซ่อนการแก้ไขเล็กน้อยจากรายการเฝ้าดู", "tog-watchlisthideliu": "ซ่อนการแก้ไขโดยผู้ใช้ล็อกอินจากรายการเฝ้าดู", "tog-watchlistreloadautomatically": "โหลดรายการเฝ้าดูใหม่อัตโนมัติเมื่อใดที่มีการเปลี่ยนตัวกรอง (ต้องการจาวาสคริปต์)", + "tog-watchlistunwatchlinks": "เพิ่มลิงก์เลิกเฝ้าดู/เฝ้าดูโดยตรงเข้าหน่วยรายการเฝ้าดู (ต้องการจาวาสคริปต์เพื่อเปิดปิดการใช้งาน)", "tog-watchlisthideanons": "ซ่อนการแก้ไขโดยผู้ใช้นิรนามจากรายการเฝ้าดู", "tog-watchlisthidepatrolled": "ซ่อนการแก้ไขที่ตรวจสอบแล้วจากรายการเฝ้าดู", "tog-watchlisthidecategorization": "ซ่อนการจัดหมวดหมู่หน้า", @@ -221,7 +222,7 @@ "viewhelppage": "ดูหน้าวิธีใช้", "categorypage": "ดูหน้าหมวดหมู่", "viewtalkpage": "ดูการพูดคุย", - "otherlanguages": "ในภาษาอื่นๆ", + "otherlanguages": "ในภาษาอื่น", "redirectedfrom": "(เปลี่ยนทางจาก $1)", "redirectpagesub": "หน้าเปลี่ยนทาง", "redirectto": "เปลี่ยนทางไป:", @@ -690,7 +691,7 @@ "readonlywarning": "<strong>คำเตือน: ฐานข้อมูลถูกล็อกเพื่อบำรุงรักษา คุณจึงไม่สามารถบันทึกการแก้ไขของคุณได้ในขณะนี้</strong>\nคุณอาจต้องการคัดลอกและวางข้อความของคุณในไฟล์ข้อความ และบันทึกไว้ภายหลัง\n\nผู้ดูแลระบบที่ล็อกฐานข้อมูลให้คำอธิบายดังนี้: $1", "protectedpagewarning": "<strong>คำเตือน: หน้านี้ถูกล็อก เพื่อให้เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบแก้ไขได้เท่านั้น</strong>\nรายการปูมล่าสุดจัดไว้ด้านล่างเพื่อการอ้างอิง:", "semiprotectedpagewarning": "<strong>หมายเหตุ:</strong> หน้านี้ถูกล็อก เพื่อให้เฉพาะผู้ใช้ลงทะเบียนสามารถแก้ไขเท่านั้น\nรายการปูมล่าสุดได้จัดไว้ด้านล่างนี้เพื่อการอ้างอิง", - "cascadeprotectedwarning": "<strong>คำเตือน:</strong> หน้านี้ถูกล็อก และแก้ไขได้เฉพาะผู้ใช้ที่มี[[Special:ListGroupRights|สิทธิบางประการ]]ที่สามารถแก้ไขได้ เนื่องจากหน้านี้รวมอยู่ในหน้าที่ถูกล็อกแบบต่อเรียงต่อไปนี้:", + "cascadeprotectedwarning": "<strong>คำเตือน:</strong> หน้านี้ถูกล็อก และแก้ไขได้เฉพาะผู้ใช้ที่มี[[Special:ListGroupRights|สิทธิบางประการ]]ที่สามารถแก้ไขได้ เนื่องจากหน้านี้รวมอยู่ใน{{PLURAL:$1|หน้า}}ที่ถูกล็อกแบบต่อเรียงต่อไปนี้:", "titleprotectedwarning": "<strong>คำเตือน: หน้านี้ได้รับการป้องกัน สร้างได้เฉพาะผู้ใช้ที่มี[[Special:ListGroupRights|สิทธิจำเพาะ]]เท่านั้น</strong>\nรายการปูมล่าสุดถูกแสดงไว้ด้านล่างเพื่อการอ้างอิง", "templatesused": "{{PLURAL:$1|แม่แบบ}}ที่ใช้ในหน้านี้:", "templatesusedpreview": "{{PLURAL:$1|แม่แบบ}}ที่ใช้ในตัวอย่างนี้:", @@ -791,7 +792,7 @@ "rev-deleted-user": "(ชื่อผู้ใช้ถูกลบออก)", "rev-deleted-event": "(รายละเอียดปูมถูกลบ)", "rev-deleted-user-contribs": "[นำชื่อผู้ใช้หรือเลขที่อยู่ไอพีออกแล้ว - การแก้ไขถูกซ่อนจากรายการแก้ไข]", - "rev-deleted-text-permission": "รุ่นหน้านี้ <strong>ถูกลบ</strong>.\nพบรายละเอียดได้ใน[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ปูมการลบ]", + "rev-deleted-text-permission": "รุ่นหน้านี้ <strong>ถูกลบ</strong>\nพบรายละเอียดได้ใน[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ปูมการลบ]", "rev-suppressed-text-permission": "รุ่นหน้านี้ถูก<strong>ยับยั้ง</strong> \nพบรายละเอียดได้ใน[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ปูมการยับยั้ง]", "rev-deleted-text-unhide": "รุ่นหน้านี้<strong>ถูกลบ</strong>\nพบรายละเอียดได้ใน[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ปูมการลบ]\nคุณยังสามารถ[$1 ดูรุ่นนี้]ได้ถ้าคุณต้องการดำเนินต่อ", "rev-suppressed-text-unhide": "รุ่นหน้านี้<strong>ถูกยับยั้ง</strong>\nพบรายละเอียดพบใน[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ปูมการยับยั้ง]\nคุณยังสามารถ[$1 ดูรุ่นนี้]ได้ถ้าคุณต้องการดำเนินต่อ", @@ -934,7 +935,7 @@ "search-interwiki-caption": "ผลการค้นหาจากโครงการพี่น้อง", "search-interwiki-default": "ผลลัพธ์จาก $1:", "search-interwiki-more": "(เพิ่มเติม)", - "search-interwiki-more-results": "ดูผลการค้นหาเพิ่ม", + "search-interwiki-more-results": "ผลการค้นหาเพิ่ม", "search-relatedarticle": "สัมพันธ์", "searchrelated": "สัมพันธ์", "searchall": "ทั้งหมด", @@ -962,11 +963,11 @@ "datedefault": "ไม่ตั้งค่า", "prefs-labs": "คุณสมบัติทดลอง", "prefs-user-pages": "หน้าผู้ใช้", - "prefs-personal": "ข้อมูลผู้ใช้", + "prefs-personal": "โพรไฟล์ผู้ใช้", "prefs-rc": "ปรับปรุงล่าสุด", "prefs-watchlist": "รายการเฝ้าดู", "prefs-editwatchlist": "แก้ไขรายการเฝ้าดู", - "prefs-editwatchlist-label": "แก้ไขรายการในรายการเฝ้าดูของคุณ:", + "prefs-editwatchlist-label": "แก้ไขหน่วยในรายการเฝ้าดูของคุณ:", "prefs-editwatchlist-edit": "ดูและลบชื่อเรื่องในรายการเฝ้าดูของคุณ", "prefs-editwatchlist-raw": "แก้ไขรายการเฝ้าดูดิบ", "prefs-editwatchlist-clear": "ลบล้างรายการเฝ้าดูของคุณ", @@ -1076,7 +1077,7 @@ "saveusergroups": "บันทึกกลุ่ม{{GENDER:$1|ผู้ใช้}}", "userrights-groupsmember": "สมาชิกของ:", "userrights-groupsmember-auto": "สมาชิกโดยปริยายของ:", - "userrights-groups-help": "คุณสามารถเปลี่ยนแปลงกลุ่มที่ผู้ใช้รายนี้อยู่:\n* กล่องที่มีเครื่องหมายถูก หมายความว่า ผู้ใช้อยู่ในกลุ่มนั้น\n* กล่องที่ไม่มีเครื่องหมายถูก หมายความว่า ผู้ใช้ไม่ได้อยู่ในกลุ่มนั้น\n* เครื่องหมาย * ชี้ว่าคุณไม่สามารถนำกลุ่มนั้นออกได้เมื่อคุณเพิ่มกลุ่มนั้นไปแล้ว หรือกลับกัน", + "userrights-groups-help": "คุณสามารถเปลี่ยนแปลงกลุ่มที่ผู้ใช้รายนี้อยู่:\n* กล่องที่มีเครื่องหมายถูก หมายความว่า ผู้ใช้อยู่ในกลุ่มนั้น\n* กล่องที่ไม่มีเครื่องหมายถูก หมายความว่า ผู้ใช้ไม่ได้อยู่ในกลุ่มนั้น\n* เครื่องหมาย * ชี้ว่าคุณไม่สามารถนำกลุ่มนั้นออกได้เมื่อคุณเพิ่มกลุ่มนั้นไปแล้ว หรือกลับกัน\n* เครื่องหมาย # บี้ว่าคุณสามารถแก้คืนเวลาหมดอายุของสมาชิกภาพกลุ่มนี้เท่านั้น คุณไม่สามารถร่นเวลาหมดอายุได้", "userrights-reason": "เหตุผล:", "userrights-no-interwiki": "คุณไม่มีสิทธิแก้ไขสิทธิผู้ใช้บนวิกิอื่น", "userrights-nodatabase": "ไม่มีฐานข้อมูล $1 หรือฐานข้อมูลอยู่บนเครื่องอื่น", @@ -1085,8 +1086,12 @@ "userrights-expiry-current": "หมดอายุ $1", "userrights-expiry-none": "ไม่มีวันหมดอายุ", "userrights-expiry": "หมดอายุ:", + "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": "กลุ่ม:", "group-user": "ผู้ใช้", @@ -1177,8 +1182,9 @@ "right-siteadmin": "ล็อกและปลดล็อกฐานข้อมูล", "right-override-export-depth": "ส่งออกหน้า รวมหน้าที่เชื่อมโยงกับหน้านี้สูงสุด 5 ลำดับชั้น", "right-sendemail": "ส่งอีเมลหาผู้ใช้อื่น", - "right-managechangetags": "สร้างและเปิด/ปิดใช้งาน[[Special:Tags|ป้ายชื่อ]]", - "right-deletechangetags": "ลบ[[Special:Tags|ป้ายชื่อ]]ออกจากฐานข้อมูล", + "right-managechangetags": "สร้างและเปิด/ปิดใช้งาน[[Special:Tags|ป้ายระบุ]]", + "right-applychangetags": "ใช้[[Special:Tags|ป้ายระบุ]]ร่วมกับการเปลี่ยนแปลงของผู้ใช้", + "right-deletechangetags": "ลบ[[Special:Tags|ป้ายระบุ]]ออกจากฐานข้อมูล", "grant-group-page-interaction": "โต้ตอบกับหน้า", "grant-group-file-interaction": "โต้ตอบกับสื่อ", "grant-group-watchlist-interaction": "โต้ตอบกับรายการเฝ้าดูของคุณ", @@ -1259,7 +1265,7 @@ "action-viewmyprivateinfo": "ดูสารสนเทศส่วนตัวของคุณ", "action-editmyprivateinfo": "แก้ไขสารสนเทศส่วนตัวของคุณ", "action-editcontentmodel": "แก้ไขตัวแบบเนื้อหาของหน้า", - "action-deletechangetags": "ลบป้ายชื่อออกจากฐานข้อมูล", + "action-deletechangetags": "ลบป้ายระบุออกจากฐานข้อมูล", "action-purge": "ล้างหน้านี้", "nchanges": "$1 การเปลี่ยนแปลง", "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ตั้งแต่การเยี่ยมชมครั้งสุดท้าย}}", @@ -1268,10 +1274,13 @@ "recentchanges-legend": "ตัวเลือกการปรับปรุงล่าสุด", "recentchanges-summary": "ติดตามการเปลี่ยนแปลงล่าสุดบนวิกินี้ได้ทางหน้านี้", "recentchanges-noresult": "ไม่มีการเปลี่ยนแปลงในช่วงที่กำหนดซึ่งตรงกับเกณฑ์เหล่านี้", + "recentchanges-timeout": "การค้นหานี้หมดเวลาแล้ว คุณอาจต้องการลองตัวแปรเสริมค้นหาอื่น", + "recentchanges-network": "เนื่องจากข้อผิดพลาดทางเทคนิค จึงไม่สามารถโหลดผลลัพธ์ได้ กรุณาลองรีเฟรชหน้า", + "recentchanges-notargetpage": "กรอกชื่อหน้าด้านบนเพื่อดูการเปลี่ยนแปลงที่สัมพันธ์กับหน้านั้น", "recentchanges-feed-description": "ติดตามการปรับปรุงล่าสุดในวิกินี้ในฟีดนี้", "recentchanges-label-newpage": "การแก้ไขนี้สร้างหน้าใหม่", "recentchanges-label-minor": "เป็นการแก้ไขเล็กน้อย", - "recentchanges-label-bot": "บอตกระทำการแก้ไขนี้", + "recentchanges-label-bot": "บอตเป็นผู้ทำการแก้ไขนี้", "recentchanges-label-unpatrolled": "การแก้ไขนี้ยังไม่ได้ตรวจสอบ", "recentchanges-label-plusminus": "ขนาดของหน้าเปลี่ยนไปจำนวนไบต์เท่านี้", "recentchanges-legend-heading": "<strong>คำอธิบายสัญลักษณ์:</strong>", @@ -1320,6 +1329,9 @@ "rcfilters-highlightmenu-help": "เลือกสีสำหรับเน้นการแสดงคุณสมบัตินี้", "rcfilters-filterlist-noresults": "ไม่พบตัวกรองใด ๆ", "rcfilters-noresults-conflict": "ไม่พบผลลัพธ์ เนื่องจากเงื่อนไขการค้นขัดแย้งกัน", + "rcfilters-state-message-subset": "ตัวกรองนี้ไม่มีผล เนื่องจากผลลัพธ์ของมันรวมอยู่ในผลลัพธ์ของ{{PLURAL:$2|ตัวกรอง}}ที่กว้างกว่าต่อไปนี้ (ลองเน้นเพื่อแยกแยะ) : $1", + "rcfilters-state-message-fullcoverage": "การเลือกตัวกรองในกลุ่มนี้มีค่าเท่ากับไม่เลือก ฉะนั้นตัวกรองนี้จึงไม่มีผล กลุ่มนี้มี: $1", + "rcfilters-filtergroup-authorship": "การประพันธ์มีส่วนร่วม", "rcfilters-filter-editsbyself-label": "การแก้ไขของคุณ", "rcfilters-filter-editsbyself-description": "การแก้ไขของคุณเอง", "rcfilters-filter-editsbyother-label": "การแก้ไขของผู้อื่น", @@ -1331,7 +1343,7 @@ "rcfilters-filter-user-experience-level-unregistered-description": "ผู้ใช้ไม่ล็อกอิน", "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-label": "ผู้เรียนรู้", "rcfilters-filter-user-experience-level-learner-description": "ผู้ใช้ลงทะเบียนที่มีประสบการณ์อยู่ระหว่าง \"ผู้มาใหม่\" กับ \"ผู้ใช้มีประสบการณ์\"", "rcfilters-filter-user-experience-level-experienced-label": "ผู้ใช้ที่มีความเชี่ยวชาญ", "rcfilters-filter-user-experience-level-experienced-description": "ผู้ใช้ลงทะเบียนที่มีการแก้ไขมากกว่า 500 ครั้งและอายุมากกว่า 30 วัน", @@ -1371,6 +1383,9 @@ "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-filter-lastrevision-description": "เฉพาะการเปลี่ยนแปลงล่าสุดของหน้า", @@ -1384,6 +1399,7 @@ "rcfilters-view-namespaces-tooltip": "กรองผลลัพธ์ตามเนมสเปซ", "rcfilters-view-tags-tooltip": "กรองผลลัพธ์โดยใช้ป้ายระบุการแก้ไข", "rcfilters-view-return-to-default-tooltip": "กลับรายการเลือกตัวกรองหลัก", + "rcfilters-view-tags-help-icon-tooltip": "เรียนรู้เพิ่มเกี่ยวกับการแก้ไขที่ติดป้ายระบุ", "rcfilters-liveupdates-button": "การปรับสด", "rcfilters-liveupdates-button-title-on": "ปิดการปรับสด", "rcfilters-liveupdates-button-title-off": "แสดงการเปลี่ยนแปลงใหม่ทันที", @@ -1392,11 +1408,11 @@ "rcfilters-watchlist-showupdated": "การเปลี่ยนแปลงหน้าที่คุณไม่ได้ชมตั้งแต่มีการเปลี่ยนแปลงแสดงด้วย <strong>ตัวหนา</strong> โดยมีเครื่องหมายเข้ม", "rcfilters-preference-label": "ซ่อนรุ่นปรับปรุงของรายการเปลี่ยนแปลงล่าสุด", "rcfilters-preference-help": "ย้อนกลับการออกแบบอินเตอร์เฟซใหม่ปี 2560 และอุปกรณ์ทั้งหมดที่เพิ่มเข้ามาหลังจากนั้น", - "rcfilters-filter-showlinkedfrom-label": "แสดงการเปลี่ยนแปลงหน้าที่ลิงก์มาจาก:", - "rcfilters-filter-showlinkedfrom-option-label": "แสดงการเปลี่ยนแปลงหน้าที่ลิงก์มาจากหน้าหนึ่ง", - "rcfilters-filter-showlinkedto-label": "แสดงการเปลี่ยนแปลงหน้าที่ลิงก์มา:", - "rcfilters-filter-showlinkedto-option-label": "แสดงการเปลี่ยนแปลงหน้าที่ลิงก์มายังหน้าหนึ่ง", - "rcfilters-target-page-placeholder": "เลือกหน้า", + "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> (แสดงมากสุด <strong>$1</strong>)", "rclistfromreset": "กลับค่าเดิมของการเลือกวันที่", "rclistfrom": "แสดงการเปลี่ยนแปลงใหม่เริ่มตั้งแต่ $2, $3", @@ -1442,7 +1458,7 @@ "recentchangeslinked-feed": "ปรับปรุงที่เกี่ยวโยง", "recentchangeslinked-toolbox": "การปรับปรุงที่เกี่ยวโยง", "recentchangeslinked-title": "การปรับปรุงที่โยงกับ \"$1\"", - "recentchangeslinked-summary": "นี่คือรายการเปลี่ยนแปลงล่าสุดของหน้าที่ถูกลิงก์จากหน้าเฉพาะ (หรือไปสมาชิกของหมวดหมู่เฉพาะ)\nหน้าใน[[Special:Watchlist|รายการเฝ้าดูของคุณ]]แสดงเป็น<strong>ตัวหนา</strong>", + "recentchangeslinked-summary": "กรอกชื่อหน้าเพื่อดูการเปลี่ยนแปลงในหน้าที่ลิงก์ไปหรือจากหน้านั้น (ใส่หมวดหมู่:ชื่อหมวดหมู่ เพื่อดูสมาชิกของหมวดหมู่) การเปลี่ยนแปลงของหน้าใน[[Special:Watchlist|รายการเฝ้าดูของคุณ]]แสดงเป็น<strong>ตัวหนา</strong>", "recentchangeslinked-page": "ชื่อหน้า:", "recentchangeslinked-to": "แสดงการเปลี่ยนแปลงไปหน้าซึ่งโยงไปหน้าที่ระบุแทน", "recentchanges-page-added-to-category": "[[:$1]] ถูกเพิ่มเข้าหมวดหมู่", @@ -1508,7 +1524,7 @@ "fileexists-forbidden": "มีไฟล์ชื่อนี้แล้ว และไม่สามารถเขียนทับได้\nหากคุณยังต้องการอัปโหลดไฟล์ของคุณ กรุณาย้อนกลับและใช้ชื่อใหม่ \n[[File:$1|thumb|center|$1]]", "fileexists-shared-forbidden": "ไฟล์ที่ใช้ชื่อนี้มีอยู่แล้วในระบบเก็บไฟล์ในส่วนกลาง\nถ้าคุณยังคงต้องการอัปโหลดไฟล์ของคุณ กรุณาย้อนกลับไปตั้งชื่อใหม่\n[[File:$1|thumb|center|$1]]", "fileexists-no-change": "ไฟล์ที่อัปโหลดเป็นคู่พอดีของ <strong>[[:$1]]</strong> รุ่นปัจจุบัน", - "fileexists-duplicate-version": "ไฟล์ที่อัปโหลดเป็นคู่พอดีของ <strong>[[:$1]]</strong> รุ่นก่อน", + "fileexists-duplicate-version": "ไฟล์ที่อัปโหลดซ้ำกับ <strong>[[:$1]]</strong> {{PLURAL:$2|}}รุ่นก่อนพอดี", "file-exists-duplicate": "ไฟล์นี้ซ้ำกับ{{PLURAL:$1|ไฟล์|ไฟล์}}ต่อไปนี้:", "file-deleted-duplicate": "ไฟล์ที่เหมือนไฟล์นี้ ([[:$1]]) เคยถูกลบไปก่อนหน้านี้แล้ว\nคุณควรตรวจสอบว่าประวัติการลบของไฟล์ก่อนดำเนินการอัปโหลดใหม่", "file-deleted-duplicate-notitle": "ไฟล์ที่เหมือนกับไฟล์นี้เคยถูกลบมาก่อน และชื่อดังกล่าวถูกห้ามใช้ คุณควรสอบถามผู้ที่สามารถดูข้อมูลไฟล์ที่ถูกระงับเพื่อทบทวนสถานการณ์ก่อนดำเนินการอัปโหลดไฟล์อีกครั้ง", @@ -2087,8 +2103,8 @@ "unprotectedarticle": "ยกเลิกการล็อกจาก \"[[$1]]\"", "movedarticleprotection": "ย้ายการตั้งค่าการล็อกจาก \"[[$2]]\" ไป \"[[$1]]\"", "protectedarticle-comment": "ล็อก \"[[$1]]\"", - "modifiedarticleprotection-comment": "เปลี่ยนแปลงระดับการล็อกสำหรับ \"[[$1]]\"", - "unprotectedarticle-comment": "ลบการล็อกจาก \"[[$1]]\"", + "modifiedarticleprotection-comment": "{{GENDER:$2|}}เปลี่ยนระดับการล็อกสำหรับ \"[[$1]]\"", + "unprotectedarticle-comment": "{{GENDER:$2|}}ปลดล็อก \"[[$1]]\"", "protect-title": "เปลี่ยนระดับการล็อกสำหรับ \"$1\"", "protect-title-notallowed": "ดูระดับการล็อกของ \"$1\"", "prot_1movedto2": "เปลี่ยนชื่อ [[$1]] เป็น [[$2]]", @@ -2386,13 +2402,14 @@ "movelogpagetext": "ด้านล่างเป็นรายการการเปลี่ยนชื่อทั้งหมด", "movesubpage": "{{PLURAL:$1|หน้าย่อย|หน้าย่อย}}", "movesubpagetext": "หน้านี้มีหน้าย่อย $1 หน้า ดังด้านล่าง", + "movesubpagetalktext": "หน้าพูดคุยที่สัมพันธ์มี $1 หน้าย่อย ดังแสดงด้านล่าง", "movenosubpage": "หน้านี้ไม่มีหน้าย่อย", "movereason": "เหตุผล:", "revertmove": "ย้อน", "delete_and_move_text": "มีหน้าปลายทาง \"[[:$1]]\" แล้ว \nคุณต้องการลบหน้าดังกล่าวเพื่อสร้างทางสำหรับการย้ายหรือไม่", "delete_and_move_confirm": "ใช่ ลบหน้านั้น", "delete_and_move_reason": "ถูกลบเพื่อสร้างหนทางสำหรับการย้ายจาก \"[[$1]]\"", - "selfmove": "ชื่อหน้าต้นทางและปลายทางเหมือนกัน\nไม่สามารถเปลี่ยนชื่อมาใช้ชื่อเดิมได้", + "selfmove": "ชื่อเรื่องเป็นชื่อเดียวกัน\nไม่สามารถย้ายทับชื่อเดิมได้", "immobile-source-namespace": "ไม่สามารถเปลี่ยนชื่อหน้าในเนมสเปซ \"$1\"", "immobile-target-namespace": "ไม่สามารถย้ายหน้าเข้าเนมสเปซ \"$1\" ได้", "immobile-target-namespace-iw": "ลิงก์ข้ามโครงการมิใช่เป้าหมายที่ถูกต้องของการเปลี่ยนชื่อหน้า", @@ -2489,6 +2506,7 @@ "import-logentry-upload-detail": "นำเข้า $1 {{PLURAL:$1|รุ่นการแก้ไข|รุ่นการแก้ไข}}", "import-logentry-interwiki-detail": "นำเข้า $1 {{PLURAL:$1|รุ่นการแก้ไข|รุ่นการแก้ไข}}จาก $2", "javascripttest": "การทดสอบจาวาสคริปต์", + "javascripttest-pagetext-unknownaction": "ปฏิบัติการที่ไม่รู้จัก: \"$1\"", "javascripttest-qunit-intro": "ดู[$1 เอกสารกำกับการทดสอบ]บน mediawiki.org", "tooltip-pt-userpage": "{{GENDER:|หน้าผู้ใช้}}ของคุณ", "tooltip-pt-anonuserpage": "หน้าผู้ใช้ของเลขที่อยู่ไอพีที่คุณกำลังใช้แก้ไข", @@ -2499,6 +2517,7 @@ "tooltip-pt-mycontris": "รายการหน้าที่{{GENDER:|คุณ}}เขียน", "tooltip-pt-anoncontribs": "รายการการแก้ไขจากเลขที่อยู่ไอพีนี้", "tooltip-pt-login": "สนับสนุนให้คุณล็อกอิน แต่ไม่บังคับ", + "tooltip-pt-login-private": "คุณต้องล็อกอินจึงจะใช้วิกินี้ได้", "tooltip-pt-logout": "ล็อกเอาต์", "tooltip-pt-createaccount": "สนับสนุนให้คุณสร้างบัญชีและล็อกอิน แต่ไม่บังคับ", "tooltip-ca-talk": "อภิปรายเกี่ยวกับหน้าเนื้อหา", @@ -2529,7 +2548,7 @@ "tooltip-feed-rss": "ฟีดชนิดอาร์เอสเอส (RSS) ของหน้านี้", "tooltip-feed-atom": "ฟีดอะตอม (Atom) ของหน้านี้", "tooltip-t-contributions": "รายการเรื่องที่{{GENDER:$1|ผู้ใช้นี้}}เขียน", - "tooltip-t-emailuser": "ส่งอีเมลถึงผู้ใช้นี้", + "tooltip-t-emailuser": "ส่งอีเมลถึง{{GENDER:$1|ผู้ใช้นี้}}", "tooltip-t-info": "สารสนเทศเพิ่มเติมเกี่ยวกับหน้านี้", "tooltip-t-upload": "อัปโหลดไฟล์", "tooltip-t-specialpages": "รายการหน้าพิเศษทั้งหมด", @@ -2547,6 +2566,7 @@ "tooltip-ca-nstab-category": "ดูหน้าหมวดหมู่", "tooltip-minoredit": "ทำเครื่องหมายเป็นการแก้ไขเล็กน้อย", "tooltip-save": "บันทึกการแก้ไขของคุณ", + "tooltip-publish": "เผยแพร่การเปลี่ยนแปลงของคุณ", "tooltip-preview": "แสดงตัวอย่างการเปลี่ยนแปลงของคุณ กรุณาใช้คำสั่งนี้ก่อนบันทึก!", "tooltip-diff": "แสดงการเปลี่ยนแปลงที่คุณทำต่อข้อความ", "tooltip-compareselectedversions": "แสดงความแตกต่างระหว่างสองรุ่นที่เลือกของหน้านี้", @@ -2577,7 +2597,7 @@ "lastmodifiedatby": "แก้ไขหน้านี้ล่าสุดเมื่อเวลา $2, $1 โดย $3", "othercontribs": "พัฒนาจากงานเขียนของ $1", "others": "ผู้อื่น", - "siteusers": "{{PLURAL:$2|ผู้ใช้|ผู้ใช้}}จาก {{SITENAME}} $1", + "siteusers": "{{PLURAL:$2|{{GENDER:$1|ผู้ใช้}}}}จาก {{SITENAME}} $1", "anonusers": "{{PLURAL:$2|ผู้ใช้|ผู้ใช้}}นิรนามจาก {{SITENAME}} $1", "creditspage": "เกียรติแก่ผู้ร่วมสร้าง", "nocredits": "ไม่มีรายชื่อผู้เป็นเกียรติที่ร่วมสร้างหน้านี้", @@ -2598,7 +2618,7 @@ "pageinfo-display-title": "หัวเรื่องของหน้าเมื่อแสดงผล", "pageinfo-default-sort": "ค่าปริยายของคำหลักในการเรียงลำดับ", "pageinfo-length": "ความยาวหน้า (ไบต์)", - "pageinfo-article-id": "หมายเลขประจำหน้า", + "pageinfo-article-id": "เลขหน้า", "pageinfo-language": "ภาษาของเนื้อหาของหน้า", "pageinfo-language-change": "เปลี่ยน", "pageinfo-content-model": "รูปแบบเนื้อหาของหน้า", @@ -2626,6 +2646,7 @@ "pageinfo-templates": "แม่แบบที่ใช้ ($1)", "pageinfo-toolboxlink": "สารสนเทศหน้า", "pageinfo-redirectsto": "เปลี่ยนทางไป", + "pageinfo-redirectsto-info": "สนเทศ", "pageinfo-contentpage": "นับเป็นหน้าเนื้อหา", "pageinfo-contentpage-yes": "ใช่", "pageinfo-protect-cascading": "การล็อกที่ต่อเรียงจากหน้านี้", @@ -2690,6 +2711,7 @@ "newimages-summary": "หน้าพิเศษนี้แสดงไฟล์ที่ถูกอัปโหลดล่าสุด", "newimages-legend": "ตัวกรอง", "newimages-label": "ชื่อไฟล์ (หรือส่วนหนึ่งของชื่อ):", + "newimages-user": "เลขที่อยู่ไอพีหรือชื่อผู้ใช้", "newimages-newbies": "แสดงเฉพาะการมีส่วนร่วมของบัญชีใหม่", "newimages-showbots": "แสดงไฟล์ที่บอตอัปโหลด", "newimages-hidepatrolled": "ซ่อนการอัปโหลดที่ตรวจสอบแล้ว", @@ -3079,8 +3101,9 @@ "watchlisttools-clear": "ล้างรายการเฝ้าดู", "watchlisttools-view": "ดูการเปลี่ยนแปลงที่เกี่ยวข้อง", "watchlisttools-edit": "ดูและแก้ไขรายการเฝ้าดู", - "watchlisttools-raw": "แก้ไขรายการเฝ้าดูทั้งหมด", + "watchlisttools-raw": "แก้ไขรายการเฝ้าดูดิบ", "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|พูดคุย]])", + "timezone-local": "ท้องถิ่น", "duplicate-defaultsort": "<strong>คำเตือน:</strong> หลักเรียงลำดับปริยาย \"$2\" ได้ลบล้างหลักเรียงลำดับปริยาย \"$1\" ที่มีอยู่ก่อนหน้า", "version": "รุ่น", "version-extensions": "ส่วนขยายเพิ่ม (extension) ที่ติดตั้ง", @@ -3114,9 +3137,10 @@ "redirect-lookup": "ค้นดู:", "redirect-value": "ค่า:", "redirect-user": "รหัสผู้ใช้", - "redirect-page": "รหัสประจำหน้า", + "redirect-page": "รหัสหน้า", "redirect-revision": "รุ่นปรับปรุงหน้า", "redirect-file": "ชื่อไฟล์", + "redirect-logid": "เลขปูม", "redirect-not-exists": "ไม่พบค่า", "fileduplicatesearch": "ค้นหาไฟล์ซ้ำซ้อน", "fileduplicatesearch-summary": "ค้นหาไฟล์ซ้ำกันตามค่าแฮช", @@ -3145,24 +3169,32 @@ "blankpage": "หน้าว่าง", "intentionallyblankpage": "หน้านี้ถูกปล่อยว่างโดยเจตนา", "external_image_whitelist": " #เว้นบรรทัดนี้ไว้จากการแก้ไข<pre>\n#ใส่คำอธิบายปกติ (เฉพาะในส่วนที่อยู่ระหว่างสัญลักษณ์ //) ด้านล่างนี้\n#ซึ่งคำอธิบายดังกล่าวจะถูกจับคู่กับ URL ของรูปถ่ายภายนอก\n#ถ้าตรงกันจะปรากฏเป็นภาพออกมา หรือมิเช่นนั้นจะปรากฏเป็นลิงก์ไปยังรูปภาพนั้น\n#บรรทัดที่ขึ้นต้นด้วย # จะถูกกำหนดเป็นหมายเหตุเพิ่มเติม\n#กรุณาพิมพ์ตัวพิมพ์เล็ก-ใหญ่ตามชื่อไฟล์ให้ตรงกัน\n\n#ใส่ส่วนของคำอธิบายด้านบนของบรรทัดนี้และเว้นบรรทัดนี้จากการแก้ไข</pre>", - "tags": "ป้ายกำกับการเปลี่ยนแปลง (ที่สามารถใช้ได้)", - "tag-filter": "ตัวกรอง[[Special:Tags|ป้ายกำกับ]]:", + "tags": "ป้ายระบุการเปลี่ยนแปลงที่สมเหตุสมผล", + "tag-filter": "ตัวกรอง[[Special:Tags|ป้ายระบุ]]:", "tag-filter-submit": "กรอง", - "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ป้ายกำกับ}}]]: $2)", + "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ป้ายระบุ}}]]: $2)", "tag-mw-new-redirect": "เปลี่ยนทางใหม่", "tag-mw-new-redirect-description": "การแก้ไขที่สร้างหน้าเปลี่ยนทางใหม่หรือเปลี่ยนแปลงหน้าเป็นหน้าเปลี่ยนทาง", "tag-mw-removed-redirect": "ลบหน้าเปล่ี่ยนทาง", "tag-mw-removed-redirect-description": "การแก้ไขี่เปลี่ยนหน้าเปลีี่ยนทางเดิมให้มิใช่หน้าเปลี่ยนทาง", + "tag-mw-changed-redirect-target": "เปลี่ยนเป้าหมายหน้าเปลี่ยนทาง", + "tag-mw-changed-redirect-target-description": "การแก้ไขที่เปลี่ยนเป้าหมายของหน้าเปลี่ยนทาง", "tag-mw-blank": "ทำหน้าว่าง", + "tag-mw-blank-description": "การแก้ไขที่ทำหน้าว่าง", + "tag-mw-replace": "ถูกแทน", + "tag-mw-replace-description": "การแก้ไขซึ่งลบเนื้อหากว่า 90% ของหน้า", "tag-mw-rollback": "ย้อนกลับ", - "tags-title": "ป้ายกำกับ", - "tags-intro": "หน้านี้แสดงรายการและความหมายของป้ายกำกับต่างๆ ที่ซอฟต์แวร์อาจจะใช้ทำเครื่องหมายกำกับการแก้ไข", + "tag-mw-rollback-description": "การแก้ไขซึ่งย้อนการแก้ไขก่อนหน้าโดยใช้ลิงก์ย้อนกลับฉุกเฉิน", + "tag-mw-undo": "ทำกลับ", + "tag-mw-undo-description": "การแก้ไขที่ทำกลับการแก้ไขก่อนหน้าโดยใช้ลิงก์ทำกลับ", + "tags-title": "ป้ายระบุ", + "tags-intro": "หน้านี้แสดงรายการและความหมายของป้ายระบุที่ซอฟต์แวร์อาจใช้ทำเครื่องหมายกำกับการแก้ไข", "tags-tag": "ชื่อป้ายกำกับ", "tags-display-header": "สิ่งที่แสดงในรายการการเปลี่ยนแปลง", "tags-description-header": "คำอธิบายความหมายโดยละเอียด", "tags-source-header": "แหล่งที่มา", "tags-active-header": "เปิดใช้งานหรือไม่", - "tags-hitcount-header": "การเปลี่ยนแปลงที่มีป้ายนี้กำกับ", + "tags-hitcount-header": "การเปลี่ยนแปลงที่ติดป้ายระบุ", "tags-actions-header": "ปฏิบัติการ", "tags-active-yes": "ใช่", "tags-active-no": "ไม่", @@ -3174,6 +3206,29 @@ "tags-activate": "เปิดใช้งาน", "tags-deactivate": "ปิดใช้งาน", "tags-hitcount": "$1 การเปลี่ยนแปลง", + "tags-manage-no-permission": "คุณไม่ได้รับอนุญาตให้จัดการเปลี่ยนป้ายระบุ", + "tags-manage-blocked": "คุณไม่สามารถจัดการป้ายระบุเปลี่ยนแปลงระหว่าง{{GENDER:$1|คุณ}}กำลังถูกบล็อก", + "tags-create-heading": "สร้างป้ายระบุใหม่", + "tags-create-explanation": "ป้ายระบุที่สร้างใหม่สามารถใช้ได้ทั้งผู้ใช้และบอตโดยปริยาย", + "tags-create-tag-name": "ชื่อป้ายระบุ:", + "tags-create-reason": "เหตุผล:", + "tags-create-submit": "สร้าง", + "tags-create-no-name": "คุณต้องเจาะจงชื่อป้ายระบุ", + "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\"", + "tags-create-warnings-below": "คุณยังต้องการสร้างป้ายระบุนี้ต่อหรือไม่", + "tags-delete-title": "ลบป้ายระบุ", + "tags-delete-explanation-initial": "คุณกำลังลบป้ายระบุ \"$1\" จากฐานข้อมูล", + "tags-delete-explanation-in-use": "การนี้จะลบ $2 รุ่นทั้งหมดและ/หรือหน่วยปูมซึ่งกำลังใช้อยู่", + "tags-delete-explanation-warning": "ปฏิบัติการนี้<strong>ผันกลับไม่ได้</strong>และ<strong>ไม่สามารถทำกลับ</strong> แม้แต่ผู้ดูแลระบบฐานข้อมูล ให้แน่ใจว่านี่เป็นป้ายระบุที่คุณตั้งใจลบ", + "tags-delete-explanation-active": "<strong>ป้ายระบุ \"$1\" ยังเปิดใช้งานอยู่ และจะยังมีผลใช้ในอนาคต</strong> หากต้องการหยุด ให้ไปยังที่ซึ่งตั้งให้ป้ายระบุนี้มีผลใช้และปิดใช้งานที่นั่น", + "tags-delete-reason": "เหตุผล:", + "tags-delete-submit": "ลบป้ายระบุนี้แบบผันกลับไม่ได้", + "tags-delete-not-allowed": "ลบป้ายระบุที่ส่วนขยายนิยามไม่ได้ยกเว้นส่วนขยายนั้นเจาะจงอนุญาต", + "tags-delete-not-found": "ไม่มีป้ายระบุ \"$1\"", + "tags-delete-too-many-uses": "ป้ายะรุบ \"$1\" มีผลใช้ในกว่า $2 รุ่น ฉะนั้นจึงไม่สามารถลบ", "comparepages": "เปรียบเทียบหน้า", "compare-page1": "หน้า 1", "compare-page2": "หน้า 2", @@ -3184,6 +3239,12 @@ "compare-title-not-exists": "ชื่อเรื่องที่คุณระบุไม่มีอยู่", "compare-revision-not-exists": "รุ่นที่คุณระบุไม่มีอยู่", "diff-form": "ความแตกต่าง", + "diff-form-oldid": "เลขรุ่นปรับปรุงเก่า (เลือกได้)", + "diff-form-revid": "เลขรุ่นปรับปรุงของผลต่าง", + "diff-form-submit": "แสดงผลต่าง", + "permanentlink": "ลิงก์ถาวร", + "permanentlink-revid": "เลขรุ่นปรับปรุง", + "permanentlink-submit": "ไปรุ่น", "dberr-problems": "ขออภัย เว็บไซต์นี้กำลังพบกับข้อผิดพลาดทางเทคนิค", "dberr-again": "กรุณารอสักครู่แล้วจึงโหลดใหม่", "dberr-info": "(ไม่สามารถเข้าถึงฐานข้อมูล: $1)", @@ -3215,11 +3276,13 @@ "htmlform-user-not-exists": "ไม่มี <strong>$1</strong>", "htmlform-user-not-valid": "<strong>$1</strong> มิใช่ชื่อผู้ใช้ที่สมเหตุสมผล", "logentry-delete-delete": "$1 ลบหน้า $3", + "logentry-delete-delete_redir": "$1 {{GENDER:$2|}}ลบหน้าเปลี่ยนทาง $3 โดยการเขียนทับ", "logentry-delete-restore": "$1 กู้คืนหน้า $3 ($4)", + "logentry-delete-restore-nocount": "$1 {{GENDER:$2|}}กู้คืนหน้า $3", "restore-count-revisions": "$1 รุ่น", "restore-count-files": "$1 ไฟล์", "logentry-delete-event": "$1 เปลี่ยนทัศนวิสัยของ $5 รายการปูมใน $3: $4", - "logentry-delete-revision": "$1 เปลี่ยนทัศนวิสัยของ $5 รุ่นในหน้า $3: $4", + "logentry-delete-revision": "$1 {{GENDER:$2|เปลี่ยนทัศนวิสัย}}ของ $5 รุ่นในหน้า $3: $4", "logentry-delete-event-legacy": "$1 เปลี่ยนทัศนวิสัยของเหตุการณ์ปูมในหน้า $3", "logentry-delete-revision-legacy": "$1 เปลี่ยนทัศนวิสัยของรุ่นในหน้า $3", "logentry-suppress-delete": "$1 ระงับหน้า $3", @@ -3246,7 +3309,7 @@ "logentry-move-move_redir": "$1 ย้ายหน้า $3 ไปยัง $4 ทับหน้าเปลี่ยนทาง", "logentry-move-move_redir-noredirect": "$1 ย้ายหน้า $3 ไปยัง $4 ทับหน้าเปลี่ยนทาง โดยไม่สร้างหน้าเปลี่ยนทางตามมา", "logentry-patrol-patrol": "$1 ทำเครื่องหมายว่ารุ่น $4 ของหน้า $3 ได้รับการตรวจสอบแล้ว", - "logentry-patrol-patrol-auto": "$1 ทำเครื่องหมายโดยอัตโนมัติว่ารุ่น $4 ของหน้า $3 ได้รับการตรวจสอบแล้ว", + "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|ทำเครื่องหมาย}}โดยอัตโนมัติว่ารุ่น $4 ของหน้า $3 ได้รับการตรวจสอบแล้ว", "logentry-newusers-newusers": "บัญชีผู้ใช้ $1 ถูกสร้าง", "logentry-newusers-create": "บัญชีผู้ใช้ $1 ถูกสร้าง", "logentry-newusers-create2": "บัญชีผู้ใช้ $3 ถูกสร้างขึ้นโดย $1", @@ -3341,12 +3404,14 @@ "special-characters-group-canadianaboriginal": "แคนาดาพื้นเมืองดั้งเดิม", "special-characters-title-minus": "เครื่องหมายลบ", "mw-widgets-dateinput-no-date": "ไม่เลือกวันที่", + "date-range-from": "ตั้งแต่วันที่:", + "date-range-to": "ถึงวันที่:", "randomrootpage": "สุ่มหน้าราก", "log-action-filter-block": "ประเภทของการบล็อก:", "log-action-filter-contentmodel": "ประเภทของการเปลี่ยนตัวแบบเนื้อหา:", "log-action-filter-delete": "ประเภทของการลบ:", "log-action-filter-import": "ประเภทของการนำเข้า:", - "log-action-filter-managetags": "ประเภทของการกระทำจัดการป้ายระบุ:", + "log-action-filter-managetags": "ประเภทของปฏิบัติการจัดการป้ายระบุ:", "log-action-filter-move": "ประเภทของการย้าย:", "log-action-filter-newusers": "ประเภทของการสร้างบัญชี:", "log-action-filter-patrol": "ประเภทของการตรวจสอบหน้า:", @@ -3368,7 +3433,7 @@ "log-action-filter-import-interwiki": "การนำเข้าข้ามวิกิ", "log-action-filter-managetags-create": "การสร้างป้ายระบุ", "log-action-filter-managetags-delete": "การลบป้ายระบุ", - "log-action-filter-managetags-activate": "การเปิดใช้งานระบุ", + "log-action-filter-managetags-activate": "การเปิดใช้งานป้ายระบุ", "log-action-filter-managetags-deactivate": "การปิดใช้งานระบุ", "log-action-filter-move-move": "การย้ายโดยไม่เขียนทับหน้าเปลี่ยนทาง", "log-action-filter-move-move_redir": "การย้ายโดยเขียนทับหน้าเปลี่ยนทาง", @@ -3389,5 +3454,7 @@ "log-action-filter-upload-overwrite": "อัปโหลดใหม่", "revid": "รุ่น $1", "pageid": "เลขประจำหน้า $1", - "undelete-cantcreate": "คุณไม่สามารถกู้คืนหน้านี้เพราะไม่มีหน้าชื่อนี้อยู่ และคุณไม่ได้รับอนุญาตให้สร้างหน้านี้" + "gotointerwiki": "กำลังออกจาก {{SITENAME}}", + "undelete-cantcreate": "คุณไม่สามารถกู้คืนหน้านี้เพราะไม่มีหน้าชื่อนี้อยู่ และคุณไม่ได้รับอนุญาตให้สร้างหน้านี้", + "pagedata-title": "ข้อมูลหน้า" } diff --git a/languages/i18n/tl.json b/languages/i18n/tl.json index e6f5a51e1d..5082649029 100644 --- a/languages/i18n/tl.json +++ b/languages/i18n/tl.json @@ -1820,7 +1820,6 @@ "sp-contributions-toponly": "Ipakita lang ang mga pamamatnugot na mga huling rebisyon", "sp-contributions-newonly": "Ipakita lang ang mga pamamatnugot na mga nalikhang pahina", "sp-contributions-submit": "Hanapin", - "sp-contributions-explain": "", "whatlinkshere": "Mga nakaturo dito", "whatlinkshere-title": "Mga pahinang kumakawing sa $1", "whatlinkshere-page": "Pahina:", diff --git a/languages/i18n/tr.json b/languages/i18n/tr.json index 403c30b44c..032787aee6 100644 --- a/languages/i18n/tr.json +++ b/languages/i18n/tr.json @@ -97,7 +97,8 @@ "Alerque", "Bulgu", "Botansahin", - "Catrope" + "Catrope", + "Hedda" ] }, "tog-underline": "Bağlantıların altını çizme:", @@ -848,7 +849,7 @@ "page_last": "son", "histlegend": "Fark seçimi: Karşılaştırmayı istediğiniz 2 sürümün önündeki daireleri işaretleyip, \"{{int:Compareselectedversions}}\" düğmesine basın.<br />\nTanımlar: '''({{int:cur}})''' = son revizyon ile arasındaki fark, '''({{int:last}})''' = bir önceki revizyon ile arasındaki fark, '''{{int:minoreditletter}}''' = küçük değişiklik.", "history-fieldset-title": "Geçmişe gözat", - "history-show-deleted": "Sadece silinenler", + "history-show-deleted": "Sadece silinen sürümler", "histfirst": "en eski", "histlast": "en yeni", "historysize": "({{PLURAL:$1|1 bayt|$1 bayt}})", @@ -1001,7 +1002,7 @@ "search-file-match": "(dosya içeriğiyle eşleşiyor)", "search-suggest": "Bunu mu demek istediniz: $1", "search-rewritten": "$1 için sonuçlar gösteriliyor. Bunun yerine $2 için arama yapılsın mı?", - "search-interwiki-caption": "Kardeş projeler", + "search-interwiki-caption": "Kardeş projelerden sonuçlar", "search-interwiki-default": "$1 sonuçları:", "search-interwiki-more": "(daha çok)", "search-interwiki-more-results": "daha fazla sonuç", @@ -1042,7 +1043,7 @@ "prefs-editwatchlist-clear": "Ä°zleme listenizi temizleyin", "prefs-watchlist-days": "Ä°zleme listesinde görüntülenecek gün sayısı:", "prefs-watchlist-days-max": "en fazla $1 {{PLURAL:$1|gün|gün}}", - "prefs-watchlist-edits": "Genişletilmiş izleme listesinde gösterilecek değişiklik sayısı:", + "prefs-watchlist-edits": "Ä°zleme listesinde gösterilecek en fazla değişiklik sayısı:", "prefs-watchlist-edits-max": "En fazla sayı: 1000", "prefs-watchlist-token": "Ä°zleme listesi anahtarı:", "prefs-misc": "Diğer ayarlar", @@ -1334,18 +1335,33 @@ "recentchanges-submit": "Göster", "rcfilters-group-results-by-page": "Sayfalandırılmış grup sonuçları", "rcfilters-activefilters": "Etkin süzgeçler", + "rcfilters-advancedfilters": "Gelişmiş süzgeçler", + "rcfilters-quickfilters": "Kaydedilmiş süzgeçler", + "rcfilters-quickfilters-placeholder-title": "Henüz hiçbir süzgeç kaydedilmedi", + "rcfilters-quickfilters-placeholder-description": "Süzgeç ayarlarınızı kaydetmek ve sonrasında bunları kullanmak için, aşağıda Aktif Süzgeçler alanındaki yer imi simgesine tıklayın.", + "rcfilters-savedqueries-defaultlabel": "Kaydedilmiş süzgeçler", + "rcfilters-savedqueries-rename": "Yeniden adlandır", + "rcfilters-savedqueries-setdefault": "Varsayılan olarak belirle", + "rcfilters-savedqueries-unsetdefault": "Varsayılan olmaktan çıkar", + "rcfilters-savedqueries-remove": "Kaldır", + "rcfilters-savedqueries-new-name-label": "Ad", + "rcfilters-savedqueries-new-name-placeholder": "Süzgecin amacını tanımlayın", + "rcfilters-savedqueries-apply-label": "Süzgeç oluştur", + "rcfilters-savedqueries-add-new-title": "Mevcut süzgeç ayarlarını kaydet", "rcfilters-restore-default-filters": "Varsayılan süzgeçleri geri getir", "rcfilters-clear-all-filters": "Tüm süzgeçleri temizle", - "rcfilters-search-placeholder": "Son değişiklikleri filtrele (gözatın veya yazmaya başlayın)", + "rcfilters-show-new-changes": "Yeni değişiklikleri görüntüle", + "rcfilters-search-placeholder": "Son değişiklikleri filtrele (menüyü kullanın veya süzgeç adını arayın)", "rcfilters-invalid-filter": "Geçersiz süzgeç", "rcfilters-empty-filter": "Etkin süzgeç bulunmuyor. Tüm katkıları gösteriliyor.", "rcfilters-filterlist-title": "Süzgeçler", - "rcfilters-filterlist-whatsthis": "Bu nedir?", - "rcfilters-filterlist-feedbacklink": "Yeni (beta) süzgeçler konusunda geribildirim verin", + "rcfilters-filterlist-whatsthis": "Bunlar nasıl çalışır?", + "rcfilters-filterlist-feedbacklink": "Bu (yeni) süzgeç araçları konusunda ne düşündüğünüzü bize bildirin", "rcfilters-highlightbutton-title": "Sonuçları vurgula", "rcfilters-highlightmenu-title": "Bir renk seçin", "rcfilters-highlightmenu-help": "Bu özelliği vurgulamak için bir renk seçin", "rcfilters-filterlist-noresults": "Süzgeç bulunamadı", + "rcfilters-noresults-conflict": "Arama kriterleri çelişkili olduğu için hiçbir sonuç bulunamadı", "rcfilters-filtergroup-authorship": "Düzenleme sahipliği", "rcfilters-filter-editsbyself-label": "Senin değişiklikleriniz", "rcfilters-filter-editsbyself-description": "Kendi katkılarınız.", @@ -1379,13 +1395,17 @@ "rcfilters-filter-major-description": "Küçük olarak etiketlenmemiş düzenlemeler.", "rcfilters-filtergroup-changetype": "Değişiklik türü", "rcfilters-filter-pageedits-label": "Sayfa düzenlemeleri", - "rcfilters-filter-pageedits-description": "Viki içeriği, tartışmalar, kategori açıklamalarındaki düzenlemeler....", + "rcfilters-filter-pageedits-description": "Viki içeriği, tartışmalar, kategori açıklamalarındaki düzenlemeler...", "rcfilters-filter-newpages-label": "Sayfa oluşturmalar", "rcfilters-filter-newpages-description": "Yeni sayfa oluşturan düzenlemeler.", "rcfilters-filter-categorization-label": "Kategori değişiklikleri", "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-logactions-description": "Hizmetli işlemleri, hesap oluşturmalar, sayfa silmeler, yüklemeler...", + "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", + "rcfilters-watchlist-markseen-button": "Tüm değişiklileri görüldü olarak işaretle", "rcnotefrom": "<strong>$3, $4</strong> tarihinden itibaren yapılan {{PLURAL:$5|değişiklik|değişiklik}} aşağıdadır (<strong>$1</strong> tarhine kadar olanlar gösterilmektedir).", "rclistfrom": "$3 $2 tarihinden itibaren yeni değişiklikleri göster", "rcshowhideminor": "Küçük değişiklikleri $1", @@ -1433,6 +1453,7 @@ "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", + "recentchanges-page-removed-from-category": "[[:$1]] kategoriden çıkarıldı", "upload": "Dosya yükle", "uploadbtn": "Dosya yükle", "reuploaddesc": "Yükleme formuna geri dön.", @@ -1634,7 +1655,7 @@ "listfiles_size": "Boyut (bayt)", "listfiles_description": "Tanım", "listfiles_count": "Sürümler", - "listfiles-show-all": "Görüntülerin eski sürümlerini içer", + "listfiles-show-all": "Dosyaların eski sürümlerini dahil et", "listfiles-latestversion": "Geçerli sürüm", "listfiles-latestversion-yes": "Evet", "listfiles-latestversion-no": "Hayır", @@ -2029,6 +2050,7 @@ "enotif_lastdiff": "Bu değişikliği görmek için, $1 sayfasına bakınız.", "enotif_anon_editor": "anonim kullanıcı $1", "enotif_body": "Sayın $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nEditörün girdiği özet: $PAGESUMMARY $PAGEMINOREDIT\n\nEditörün iletişim bilgileri:\ne-posta: $PAGEEDITOR_EMAIL\nviki: $PAGEEDITOR_WIKI\n\nBahsi geçen sayfayı oturum açarak ziyaret edinceye kadar sayfayla ilgili başka bildirim gönderilmeyecektir. Ayrıca izleme listenizdeki tüm sayfaların bildirim durumlarını sıfırlayabilirsiniz.\n\n{{SITENAME}} bildirim sistemi\n\n--\nE-posta bildirim ayarlarınızı değiştirmek için aşağıdaki sayfayı ziyaret ediniz:\n{{canonicalurl:{{#special:Preferences}}}}\n\nÄ°zleme listesi ayarlarınızı değiştirmek için aşağıdaki sayfayı ziyaret ediniz:\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nSayfayı izleme listenizden silmek için aşağıdaki sayfayı ziyaret ediniz:\n$UNWATCHURL\n\nGeri bildirim ve daha fazla yardım için:\n$HELPPAGE", + "enotif_minoredit": "Bu küçük bir değişiklik", "created": "oluşturuldu", "changed": "değiştirildi", "deletepage": "Sayfayı sil", diff --git a/languages/i18n/tt-cyrl.json b/languages/i18n/tt-cyrl.json index 6bd38392d8..aac0e59fc1 100644 --- a/languages/i18n/tt-cyrl.json +++ b/languages/i18n/tt-cyrl.json @@ -1779,6 +1779,8 @@ "pageinfo-watchers": "Күзәтүчеләр саны", "pageinfo-few-watchers": "$1 әзрәк {{PLURAL:$1|күзәтүче}}", "pageinfo-redirects-name": "Бу биткә юнәлтүләр саны", + "pageinfo-subpages-name": "Бу битнең асбитләре", + "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|юнәлтү}}; $3 {{PLURAL:$3|гади}})", "pageinfo-firstuser": "Битне төзүче", "pageinfo-firsttime": "Битне төзү датасы", "pageinfo-lastuser": "Соңгы мөхәррирләүче", @@ -1787,9 +1789,13 @@ "pageinfo-authors": "Гомуми авторлар саны", "pageinfo-recent-edits": "Соңгы вакытта үзгәртүләр ($1 эчендә)", "pageinfo-recent-authors": "Кабатланмас авторлар саны", + "pageinfo-magic-words": "{{PLURAL:$1|1=Тылсымлы сүз|Тылсымлы сүзләр}} ($1)", + "pageinfo-hidden-categories": "{{PLURAL:$1|1=Яшерен төркем|Яшерен төркемнәр}} ($1)", + "pageinfo-templates": "{{PLURAL:$1|1=Калып|Калыплар}} ($1)", "pageinfo-toolboxlink": "Бит турында мәгълүмат", "pageinfo-redirectsto": "Юнәлтү", "pageinfo-redirectsto-info": "мәгълүмат", + "pageinfo-contentpage": "Эчтәлек бите дип исәпләнә", "pageinfo-contentpage-yes": "Әйе", "pageinfo-protect-cascading-yes": "Әйе", "markaspatrolledtext": "Бу мәкаләне тикшерелгән дип тамгалау", @@ -1811,6 +1817,7 @@ "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|бит|битләр|бит}}", "file-info": "файл зурлыгы: $1, MIME-тип: $2", "file-info-size": "$1 × $2 нокта, файлның зурлыгы: $3, MIME тибы: $4", + "file-info-size-pages": "$1 × $2 пиксель, файл күләме: $3, MIME-тибы: $4, $5 {{PLURAL:$5|бит}}", "file-nohires": "Югары ачыклык белән юрама юк.", "svg-long-desc": "SVG файлы, шартлы $1 × $2 нокта, файлның зурлыгы: $3", "show-big-image": "Төп файл", @@ -2144,6 +2151,7 @@ "htmlform-time-placeholder": "СС:ММ:СС", "htmlform-datetime-placeholder": "ЕЕЕЕ-АА-КК СС:ММ:СС", "logentry-delete-delete": "$1 $3 битен {{GENDER:$2|бетерә}}", + "logentry-delete-restore": "$1 $3 ($4) битен {{GENDER:$2|торгызды}}", "revdelete-content-hid": "эчтәлек яшерелгән", "revdelete-summary-hid": "төзәтмәнең тасвирламасы яшерелгән", "revdelete-uname-hid": "кулланучының исеме яшерелгән", @@ -2228,6 +2236,7 @@ "special-characters-group-lao": "Лаос", "special-characters-group-khmer": "Кһмер", "special-characters-group-canadianaboriginal": "Канада иҗек язуы", + "randomrootpage": "Очраклы төп бит", "log-action-filter-all": "Барысы", "log-action-filter-block-block": "Тыю", "authmanager-email-label": "Электрон почта", diff --git a/languages/i18n/uk.json b/languages/i18n/uk.json index 4841bfb9d3..88b33aca07 100644 --- a/languages/i18n/uk.json +++ b/languages/i18n/uk.json @@ -71,7 +71,9 @@ "Similartothissimilartothat", "Bunyk", "Choomaq", - "SimondR" + "SimondR", + "Renamerr", + "Avatar6" ] }, "tog-underline": "Підкреслювання посилань:", @@ -228,7 +230,7 @@ "tagline": "Матеріал з {{grammar:genitive|{{SITENAME}}}}", "help": "Довідка", "search": "Пошук", - "search-ignored-headings": " #<!-- залиште цей рядок точно таким, яким він є --> <pre>\n# Заголовки, які будуть ігноруватися при пошуці.\n# Зміни, які набирають сили при індексуванні сторінки з заголовком.\n# Ви можете примусити переіндексувати сторінку з нульовим редагуванням.\n# Синтаксис наступний:\n# * Усе, що починається з символу \"#\" до кінця рядка, є коментарем\n# * Кожний непорожній рядок є точним заголовком для ігнорування\nПосилання\nЗовнішні посилання\nДив. також\n #</pre> <!-- залиште цей рядок точно таким, яким він є -->", + "search-ignored-headings": " #<!-- залиште цей рядок точно таким, яким він є --> <pre>\n# Заголовки, які будуть ігноруватися при пошуці.\n# Зміни, які набирають сили при індексуванні сторінки з заголовком.\n# Ви можете примусити переіндексувати сторінку з нульовим редагуванням.\n# Синтаксис наступний:\n# * Усе, що починається з символу \"#\" до кінця рядка, є коментарем\n# * Кожний непорожній рядок є точним заголовком для ігнорування\nПримітки\nПосилання\nДив. також\n #</pre> <!-- залиште цей рядок точно таким, яким він є -->", "searchbutton": "Пошук", "go": "Перейти", "searcharticle": "Перейти", @@ -345,7 +347,7 @@ "red-link-title": "$1 (такої сторінки не існує)", "sort-descending": "Сортувати за спаданням", "sort-ascending": "Сортувати за зростанням", - "nstab-main": "Стаття", + "nstab-main": "Сторінка", "nstab-user": "Сторінка користувача", "nstab-media": "Медіа-сторінка", "nstab-special": "Спеціальна сторінка", @@ -429,8 +431,8 @@ "ns-specialprotected": "Спеціальні сторінки не можна редагувати.", "titleprotected": "Створення сторінки з такою назвою було заборонене користувачем [[User:$1|$1]].\nЗазначена така причина: <em>$2</em>.", "filereadonlyerror": "Неможливо змінити файл «$1» тому, що файловий архів «$2» перебуває в режимі «лише для читання».\n\nАдміністратор, що заблокував його, залишив таке пояснення: «''$3''».", - "invalidtitle-knownnamespace": "Неприйнятна назва у просторі імен «$2» і текстом «$3»", - "invalidtitle-unknownnamespace": "Неправильний заголовок з невідомим номером простору імен ($1) і текстом: «$2»", + "invalidtitle-knownnamespace": "Неприйнятна назва у просторі назв «$2» і текстом «$3»", + "invalidtitle-unknownnamespace": "Невалідний заголовок з невідомим номером простору назв ($1) і текстом: «$2»", "exception-nologin": "Не виконано вхід", "exception-nologin-text": "Необхідно увійти, щоб мати доступ до цієї сторінки або дії.", "exception-nologin-text-manual": "Потрібно $1, щоб мати доступ до цієї сторінки або дії.", @@ -441,7 +443,7 @@ "cannotlogoutnow-title": "Неможливо вийти прямо зараз", "cannotlogoutnow-text": "Неможливо вийти із системи під час використання $1.", "welcomeuser": "Вітаємо, $1!", - "welcomecreation-msg": "Ваш обліковий запис створено.\nТепер маєте змогу за бажанням змінювати ваші [[Special:Preferences|налаштування у {{GRAMMAR:genitive|{{SITENAME}}}}]].", + "welcomecreation-msg": "Ваш обліковий запис створено.\nТепер є можливість за Вашим бажанням змінювати [[Special:Preferences|персональні налаштування у {{GRAMMAR:genitive|{{SITENAME}}}}]].", "yourname": "Ім'я користувача:", "userlogin-yourname": "Ім'я користувача", "userlogin-yourname-ph": "Введіть ім'я користувача", @@ -514,7 +516,7 @@ "nosuchusershort": "Користувача з іменем «$1» не існує.\nПеревірте правильність написання імені.", "nouserspecified": "Ви повинні зазначити ім'я користувача.", "login-userblocked": "Цей користувач заблокований. Вхід в систему не дозволений.", - "wrongpassword": "Ви ввели хибний пароль. Спробуйте ще раз.", + "wrongpassword": "Ви ввели хибне ім'я користувача або пароль. Будь ласка, спробуйте знову.", "wrongpasswordempty": "Ви не ввели пароль. Будь ласка, спробуйте ще раз.", "passwordtooshort": "Ваш пароль закороткий, він має містити принаймні $1 {{PLURAL:$1|символ|символи|символів}}.", "passwordtoolong": "Пароль не може бути довшим ніж {{PLURAL:$1|1 символ|$1 символи|$1 символів}}.", @@ -587,11 +589,11 @@ "botpasswords-insert-failed": "Не вдалось додати бота з іменем «$1». Можливо, він вже був доданий?", "botpasswords-update-failed": "Не вдалось оновити бота з іменем «$1». Можливо, він був видалений?", "botpasswords-created-title": "Пароль бота створено", - "botpasswords-created-body": "Пароль бота з ім'ям «$1» користувача «$2» було створено.", + "botpasswords-created-body": "Пароль бота з ім'ям «$1» {{GENDER:$2|користувача|користувачки}} «$2» було створено.", "botpasswords-updated-title": "Пароль бота оновлено", - "botpasswords-updated-body": "Пароль бота з ім'ям «$1» користувача «$2» було оновлено.", + "botpasswords-updated-body": "Пароль бота з ім'ям «$1» {{GENDER:$2|користувача|користувачки}} «$2» було оновлено.", "botpasswords-deleted-title": "Пароль бота видалено", - "botpasswords-deleted-body": "Пароль бота з ім'ям «$1» користувача «$2» було видалено", + "botpasswords-deleted-body": "Пароль бота з ім'ям «$1» {{GENDER:$2|користувача|користувачки}} «$2» було вилучено", "botpasswords-newpassword": "Новий пароль для входу під <strong>$1</strong> — <strong>$2</strong>. <em>Запишіть його для подальшого використання.</em> <br> (Для старих ботів, які вимагають, щоб логін був такий же, як і ім'я користувача, Ви також можете використовувати <strong>$3</strong> як ім'я користувача і <strong>$4</strong> як пароль.)", "botpasswords-no-provider": "BotPasswordsSessionProvider не доступний.", "botpasswords-restriction-failed": "Вхід не було здійснено через обмеження для паролю бота.", @@ -658,7 +660,7 @@ "extlink_tip": "Зовнішнє посилання (не забудьте про префікс http://)", "headline_sample": "Текст заголовка", "headline_tip": "Заголовок 2-го рівня", - "nowiki_sample": "Вставити сюди неформатований текст.", + "nowiki_sample": "Додайте сюди неформатований текст.", "nowiki_tip": "Ігнорувати вікі-форматування", "image_sample": "Example.jpg", "image_tip": "Файл", @@ -748,7 +750,7 @@ "cascadeprotectedwarning": "<strong>Попередження:</strong> Цю сторінку можуть редагувати лише користувачі зі [[Special:ListGroupRights|специфічними правами]], оскільки вона включена на {{PLURAL:$1|1=сторінці|сторінках}}, де встановлено каскадний захист:", "titleprotectedwarning": "'''Попередження. Ця сторінка була захищена так, що для її створення потрібні [[Special:ListGroupRights|особливі права]].'''\nОстанній запис журналу наведений нижче для довідки:", "templatesused": "{{PLURAL:$1|1=Шаблон, використаний|Шаблони, використані}} на цій сторінці:", - "templatesusedpreview": "{{PLURAL:$1|1=Шаблон, використаний|Шаблони, використані}} у цьому попередньому перегляді:", + "templatesusedpreview": "{{PLURAL:$1|1=Шаблон, використаний|Шаблони, використані}} в цьому попередньому перегляді:", "templatesusedsection": "{{PLURAL:$1|1=Шаблон, використаний|Шаблони, використані}} в цьому розділі:", "template-protected": "(захищено)", "template-semiprotected": "(частково захищено)", @@ -961,6 +963,8 @@ "diff-multi-sameuser": "(Не {{PLURAL:$1|показано одну проміжну версію|показані $1 проміжні версії|показано $1 проміжних версій}} цього користувача)", "diff-multi-otherusers": "(Не {{PLURAL:$1|показана $1 проміжна версія|показано $1 проміжні версії|показані $1 проміжних версій}} {{PLURAL:$2|ще одного користувача|$2 користувачів}})", "diff-multi-manyusers": "({{PLURAL:$1|не показана $1 проміжна версія|не показані $1 проміжні версії|не показано $1 проміжних версій}}, зроблених більш, ніж {{PLURAL:$2|1=$1 користувачем|$2 користувачами}})", + "diff-paragraph-moved-tonew": "Абзац було переміщено. Натисніть щоб перестрибнути до нового розташування.", + "diff-paragraph-moved-toold": "Абзац було переміщено. Натисніть щоб перестрибнути до старого розташування.", "difference-missing-revision": "{{PLURAL:$2|$2 версія|$2 версії|$2 версій}} для цього порівняння ($1) не {{PLURAL:$2|1=знайдена|знайдені}}.\n\nІмовірно, ви перейшли за застарілим посиланням на порівняння версій вилученої сторінки.\nПодробиці можна дізнатися з [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журналу вилучень].", "searchresults": "Результати пошуку", "searchresults-title": "Результати пошуку для «$1»", @@ -1036,7 +1040,7 @@ "prefs-watchlist-days-max": "Максимум $1 {{PLURAL:$1|день|дні|днів}}", "prefs-watchlist-edits": "Максимальна кількість змін, яку можна виводити у списку спостереження:", "prefs-watchlist-edits-max": "Максимально: 1000", - "prefs-watchlist-token": "Мітка списку спостереження:", + "prefs-watchlist-token": "Токен списку спостереження:", "prefs-misc": "Інші налаштування", "prefs-resetpass": "Змінити пароль", "prefs-changeemail": "Змінити або вилучити адресу електронної пошти", @@ -1054,7 +1058,7 @@ "recentchangesdays-max": "(максимум $1 {{PLURAL:$1|день|дні|днів}})", "recentchangescount": "Кількість редагувань для показу за замовчуванням:", "prefs-help-recentchangescount": "Це стосується нових редагувань, історій сторінок і журналів.", - "prefs-help-watchlist-token2": "Це секретний ключ до веб-каналу вашого списку спостереження.\nБудь-хто, хто його знає, матиме можливість читати ваш список спостереження, тому не поширюйте його.\n[[Special:ResetTokens|Натисніть тут, якщо вам потрібно скинути його]].", + "prefs-help-watchlist-token2": "Це секретний ключ до веб-каналу вашого списку спостереження.\nБудь-хто, хто його знає, матиме можливість читати ваш список спостереження, тому не поширюйте його.\nЯкщо вам потрібно, [[Special:ResetTokens|ви можете скинути його]].", "savedprefs": "Ваші налаштування збережено.", "savedrights": "Групи {{GENDER:$1|користувача $1|користувачки $1}} було збережено.", "timezonelegend": "Часовий пояс:", @@ -1074,6 +1078,7 @@ "timezoneregion-indian": "Індійський океан", "timezoneregion-pacific": "Тихий океан", "allowemail": "Дозволити електронну пошту від інших користувачів", + "email-allow-new-users-label": "Дозволити електронні листи від новозареєстрованих користувачів.", "email-blacklist-label": "Заборонити цим користувачам надсилати мені електронну пошту:", "prefs-searchoptions": "Пошук", "prefs-namespaces": "Простори назв", @@ -1246,6 +1251,7 @@ "right-siteadmin": "Блокування і розблокування бази даних", "right-override-export-depth": "експорт сторінок, включаючи пов'язані сторінки з глибиною до 5", "right-sendemail": "надсилання електронної пошти іншим користувачам", + "right-sendemail-new-users": "надсилати електронні листи до користувачів без логованих дій", "right-managechangetags": "створення та (де)активування [[Special:Tags|міток]]", "right-applychangetags": "додавання [[Special:Tags|міток]] разом зі змінами", "right-changetags": "додавання або вилучення будь-яких [[Special:Tags|міток]] для певних версій сторінок або записів журналів", @@ -1346,6 +1352,8 @@ "recentchanges-summary": "Відстеження останніх змін на сторінках {{grammar:genitive|{{SITENAME}}}}.", "recentchanges-noresult": "Немає змін за даний період, що відповідають цим критеріям.", "recentchanges-timeout": "Час, відведений на цей пошук, вичерпано. Можливо, Ви захочете спробувати інші пошукові параметри.", + "recentchanges-network": "Через технічну помилку не вдалось завантажити результати. Будь ласка, спробуйте перезавантажити сторінку.", + "recentchanges-notargetpage": "Щоб побачити зміни пов'язані зі сторінкою, уведіть її назву вище.", "recentchanges-feed-description": "Відстежувати останні зміни у вікі в цьому потоці.", "recentchanges-label-newpage": "Цим редагуванням створена нова сторінка", "recentchanges-label-minor": "Це незначна зміна", @@ -1362,7 +1370,9 @@ "rcfilters-group-results-by-page": "Групувати результати за сторінкою", "rcfilters-activefilters": "Активні фільтри", "rcfilters-advancedfilters": "Розширені фільтри", - "rcfilters-limit-title": "Зміни, які слід показати", + "rcfilters-limit-title": "Результати, які треба показати", + "rcfilters-limit-and-date-label": "{{PLURAL:$1|одна зміна|$1 змін}}, $2", + "rcfilters-date-popup-title": "Період часу для пошуку", "rcfilters-days-title": "Останні дні", "rcfilters-hours-title": "Останні години", "rcfilters-days-show-days": "$1 {{PLURAL:$1|день|дні|днів}}", @@ -1382,10 +1392,11 @@ "rcfilters-savedqueries-apply-and-setdefault-label": "Створити стандартний фільтр", "rcfilters-savedqueries-cancel-label": "Скасувати", "rcfilters-savedqueries-add-new-title": "Зберегти поточні налаштування фільтрів", + "rcfilters-savedqueries-already-saved": "Ці фільтри вже збережено. Змініть свої налаштування щоб створити новий Збережений фільтр.", "rcfilters-restore-default-filters": "Відновити стандартні фільтри", "rcfilters-clear-all-filters": "Очистити фільтри", "rcfilters-show-new-changes": "Переглянути найновіші зміни", - "rcfilters-search-placeholder": "Фільтруйте нові редагування (перегляньте або почніть вводити)", + "rcfilters-search-placeholder": "Фільтруйте редагування (використовуйте меню, або скористайтесь пошуком фільтру за назвою)", "rcfilters-invalid-filter": "Недійсний фільтр", "rcfilters-empty-filter": "Без фільтрів. Показано всі зміни.", "rcfilters-filterlist-title": "Фільтри", @@ -1475,6 +1486,11 @@ "rcfilters-watchlist-showupdated": "Зміни до сторінок, які Ви не відвідували з моменту здійснення змін, виділені <strong>жирним</strong>, із цілісними маркерами.", "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": "Нижче знаходяться {{PLURAL:$5|редагування}} з <strong>$3, $4</strong> (відображено до <strong>$1</strong>).", "rclistfromreset": "Скинути вибір дати", "rclistfrom": "Показати редагування починаючи з $3 $2.", @@ -1521,7 +1537,7 @@ "recentchangeslinked-feed": "Пов'язані редгування", "recentchangeslinked-toolbox": "Пов'язані редагування", "recentchangeslinked-title": "Пов'язані редагування для «$1»", - "recentchangeslinked-summary": "Це список нещодавніх змін на сторінках, на які посилається зазначена сторінка (або на сторінках, що містяться в цій категорії).\nСторінки з [[Special:Watchlist|Вашого списку спостереження]] виділено '''жирним шрифтом'''.", + "recentchangeslinked-summary": "Уведіть назву сторінки щоб побачити зміни на сторінках які посилаються на неї, або на які вона сама посилається. (Для перегляду членів категорії вводьте {{ns:14}}:Назва категорії). Зміни на сторінках з [[Special:Watchlist|Вашого Списку спостереження]] виділені <strong>жирним</strong>.", "recentchangeslinked-page": "Назва сторінки:", "recentchangeslinked-to": "Показати зміни на сторінках, пов'язаних з даною", "recentchanges-page-added-to-category": "[[:$1]] Додано до категорії", @@ -1714,6 +1730,25 @@ "uploadstash-refresh": "Оновити список файлів", "uploadstash-thumbnail": "перегляд мініатюри", "uploadstash-exception": "Не вдалося зберегти завантаження у сховку ($1): «$2».", + "uploadstash-bad-path": "Шлях не існує.", + "uploadstash-bad-path-invalid": "Неправильний шлях.", + "uploadstash-bad-path-unknown-type": "Невідомий тип «$1»", + "uploadstash-bad-path-unrecognized-thumb-name": "Нерозпізнана назва мініатюри.", + "uploadstash-bad-path-no-handler": "Не знайдено обробник для mime-типу «$1» файлу «$2».", + "uploadstash-bad-path-bad-format": "Ключ «$1» в неправильному форматі.", + "uploadstash-file-not-found": "Ключ «$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": "Відсутній заголовок 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": "Розширення є порожнім.", + "uploadstash-zero-length": "Файл нульової довжини.", "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 Відкриття доступу до зображень]", @@ -2667,6 +2702,8 @@ "import-mapping-namespace": "Імпортувати до простору назв:", "import-mapping-subpage": "Імпортувати як підсторінки такої сторінки:", "import-upload-filename": "Назва файлу:", + "import-upload-username-prefix": "Інтервікі-префікс:", + "import-assign-known-users": "Призначити редагування до локальних користувачів де користувачі з такими іменами існують локально.", "import-comment": "Примітка:", "importtext": "Будь ласка, експортуйте сторінку з іншої вікі, використовуючи [[Special:Export|засіб експорту]], збережіть файл, а потім завантажте його сюди.", "importstart": "Імпорт сторінок…", @@ -2675,6 +2712,7 @@ "imported-log-entries": "{{PLURAL:$1|Заімпортований $1 запис журналу|Заімпортовані $1 записи журналу|Заімпортовані $1 записів журналу}}.", "importfailed": "Не вдалося імпортувати: $1", "importunknownsource": "Невідомий тип імпортованої сторінки", + "importnoprefix": "Не вказано інтервікі-префікс", "importcantopen": "Неможливо відкрити файл імпорту", "importbadinterwiki": "Невірне інтервікі-посилання", "importsuccess": "Імпорт виконано!", @@ -3428,6 +3466,8 @@ "autosumm-blank": "Сторінка очищена", "autosumm-replace": "Замінено вміст на «$1»", "autoredircomment": "Перенаправлено на [[$1]]", + "autosumm-removed-redirect": "Вилучено перенаправлення на [[$1]]", + "autosumm-changed-redirect-target": "Змінено ціль перенаправлення з [[$1]] на [[$2]]", "autosumm-new": "Створена сторінка: $1", "autosumm-newblank": "Створити порожню сторінку", "size-bytes": "$1 {{PLURAL:$1|байт|байти|байтів}}", @@ -3611,8 +3651,22 @@ "tag-filter": "Фільтр [[Special:Tags|міток]]:", "tag-filter-submit": "Відфільтрувати", "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Мітка|Мітки}}]]: $2)", - "tag-mw-contentmodelchange": "зміна контентної моделі", - "tag-mw-contentmodelchange-description": "Редагування, якими була здійснена [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel зміна контентної моделі] сторінки", + "tag-mw-contentmodelchange": "зміна моделі вмісту", + "tag-mw-contentmodelchange-description": "Редагування, які змінюють [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel модель вмісту] сторінки", + "tag-mw-new-redirect": "Нове перенаправлення", + "tag-mw-new-redirect-description": "Редагування, що створюють нове перенаправлення, або заміняють сторінку перенаправленням", + "tag-mw-removed-redirect": "Вилучено перенаправлення", + "tag-mw-removed-redirect-description": "Редагування, що змінюють дійсне перенаправлення на не-перенаправлення", + "tag-mw-changed-redirect-target": "Змінено ціль перенаправлення", + "tag-mw-changed-redirect-target-description": "Редагування, що змінюють ціль перенаправлення", + "tag-mw-blank": "Очищення", + "tag-mw-blank-description": "Редагування, що очищують сторінку", + "tag-mw-replace": "Замінено", + "tag-mw-replace-description": "Редагування, що вилучають понад 90% вмісту сторінки", + "tag-mw-rollback": "Відкіт", + "tag-mw-rollback-description": "Редагування, що відкидають попередні правки використовуючи посилання відкоту", + "tag-mw-undo": "Скасування", + "tag-mw-undo-description": "Редагування, що скасовують попередні правки використовуючи посилання скасування", "tags-title": "Мітки", "tags-intro": "На цій сторінці наведений список міток, якими програмне забезпечення помічає редагування, а також значення цих міток.", "tags-tag": "Назва мітки", diff --git a/languages/i18n/ur.json b/languages/i18n/ur.json index 979ce435fe..aa8d509daa 100644 --- a/languages/i18n/ur.json +++ b/languages/i18n/ur.json @@ -824,7 +824,7 @@ "rev-deleted-diff-view": "اس فرق کی کسی ایک ترمیم کو <strong>حذف کر دیا گیا ہے</strong>۔\nآپ اس فرق کو دیکھ سکتے ہیں؛ مزید تفصیلات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔", "rev-suppressed-diff-view": "اس فرق کی کسی ایک ترمیم کو <strong>پوشیدہ کر دیا گیا ہے</strong>۔\nآپ اس فرق کو دیکھ سکتے ہیں؛ مزید تفصیلات [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} نوشتہ پوشیدگی] میں دیکھی جا سکتی ہیں۔", "rev-delundel": "مرئیت تبدیل کریں", - "rev-showdeleted": "دکھاؤ", + "rev-showdeleted": "دکھائیں", "revisiondelete": "نظرثانی حذف کریں/واپس لائیں", "revdelete-nooldid-title": "ناقص مقصود نظرثانی", "revdelete-nooldid-text": "اس فنکشن کو جس نسخے پر انجام دینا ہے اسے آپ نے منتخب نہیں کیا، یا منتخب کردہ نسخہ موجود نہیں، یا آپ موجودہ نسخہ کو پوشیدہ کرنے کی کوشش کر رہے ہیں۔", @@ -911,7 +911,7 @@ "difference-multipage": "(فرق مابین صفحات)", "lineno": "سطر $1:", "compareselectedversions": "منتخب متـن کا موازنہ", - "showhideselectedversions": "منتخب نسخوں کی مرئیت تبدیل کریں", + "showhideselectedversions": "منتخب نسخوں کی رویت تبدیل کریں", "editundo": "رد ترمیم", "diff-empty": "(کوئی فرق نہیں)", "diff-multi-sameuser": "(ایک ہی صارف کا {{PLURAL: $1 |ایک درمیانی نسخہ نہیں دکھایا گیا| $1 درمیانی نسخے نہیں دکھائے گئے}})", @@ -1100,8 +1100,8 @@ "userrights-reason": "وجہ:", "userrights-no-interwiki": "دوسرے ویکیوں پر حقوقِ صارف میں ترمیم کی آپ کو اجازت نہیں ہے.", "userrights-nodatabase": "ڈیٹابیس $1 موجود نہیں یا مقامی نہیں۔", - "userrights-changeable-col": "مجموعات جو آپ تبدیل کرسکتے ہیں", - "userrights-unchangeable-col": "مجموعات جو آپ تبدیل نہیں کرسکتے", + "userrights-changeable-col": "جن حلقوں کو آپ تبدیل کر سکتے ہیں", + "userrights-unchangeable-col": "جن حلقوں کو آپ تبدیل نہیں کر سکتے", "userrights-expiry-current": "وقت اختتام $1", "userrights-expiry-none": "لامحدود", "userrights-expiry": "اختتام:", @@ -2235,7 +2235,7 @@ "undeleterevision-duplicate-revid": "{{PLURAL:$1|ایک نسخہ بحال نہیں کیا جا سکا|$1 نسخے بحال نہیں کیے جا سکے}}، کیونکہ {{PLURAL:$1|اس کا|ان کے}} <code>rev_id</code> زیر استعمال ہے۔", "undelete-nodiff": "کوئی پرانا نسخہ نہیں ملا۔", "undeletebtn": "بحال", - "undeletelink": "دیکھو/بحال کرو", + "undeletelink": "دیکھیں/بحال کریں", "undeleteviewlink": "دکھاؤ", "undeleteinvert": "انتخاب بالعکس", "undeletecomment": "وجہ:", @@ -2454,7 +2454,7 @@ "newtitle": "نـیــا عـنــوان:", "move-watch": "اصل اور ہدف صفحہ کو زیر نظر کریں", "movepagebtn": "مـنـتـقـل", - "pagemovedsub": "انتقال کامیاب", + "pagemovedsub": "منتقلی کامیاب", "movepage-moved": "<strong>\"$1\" کو \"$2\" کی جانب منتقل کر دیا گیا</strong>", "movepage-moved-redirect": "رجوع مکرر تخلیق کر دیا گیا۔", "movepage-moved-noredirect": "رجوع مکرر کو بننے سے روک دیا گیا ہے۔", @@ -2844,7 +2844,7 @@ "exif-referenceblackwhite": "سیاہ و سفید جوالے کی قدروں کی جوڑی", "exif-datetime": "فائل کی تبدیلی کی تاریخ اور وقت", "exif-imagedescription": "تصویر کا عنوان", - "exif-make": "کیمرہ ساز کمپنی", + "exif-make": "کیمرا ساز کمپنی", "exif-model": "کیمرے کا ماڈل", "exif-software": "مستعمل سافٹ ویئر", "exif-artist": "مصنف", @@ -3225,6 +3225,8 @@ "autosumm-blank": "تمام مندرجات حذف", "autosumm-replace": "\"$1\" سے مواد کی تبدیلی", "autoredircomment": "[[$1]] سے رجوع مکرر", + "autosumm-removed-redirect": "[[$1]] سے رجوع مکرر ہٹایا", + "autosumm-changed-redirect-target": "رجوع مکرر [[$1]] کو [[$2]] سے تبدیل کیا", "autosumm-new": "«$1» مواد پر مشتمل نیا صفحہ بنایا", "autosumm-newblank": "خالی صفحہ بنایا", "size-bytes": "$1 بائٹ", @@ -3376,6 +3378,12 @@ "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ}}]]: $2)", "tag-mw-contentmodelchange": "مواد کے ماڈل میں تبدیلی", "tag-mw-contentmodelchange-description": "ترامیم جو صفحہ کے [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel مواد کے ماڈل کو تبدیل کرتی ہیں]", + "tag-mw-new-redirect": "نیا رجوع مکرر", + "tag-mw-removed-redirect": "رجوع مکرر ہٹایا", + "tag-mw-changed-redirect-target": "ہدف رجوع مکرر کی تبدیلی", + "tag-mw-blank": "صفحہ سے تمام مواد حذف", + "tag-mw-replace": "مواد کی تبدیلی", + "tag-mw-rollback": "استرجع", "tags-title": "ٹیگ", "tags-intro": "اس صفحہ میں ان تمام ٹیگوں کی فہرست درج ہے، جنہیں سافٹ ویئر کسی ترمیم پر مفہوم کے ساتھ نشان زد کرتا ہے۔", "tags-tag": "ٹیگ کا نام", @@ -3513,7 +3521,7 @@ "htmlform-title-not-exists": "$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": "$1 نے صفحہ $3 {{GENDER:$2|حذف کیا}}", "logentry-delete-delete_redir": "$1 نے بر تحریر کرتے ہوئے $3 رجوع مکرر کو {{GENDER:$2|حذف کیا}}", "logentry-delete-restore": "$1 نے صفحہ $3 کو {{GENDER:$2|بحال کیا}}", "logentry-delete-restore-nocount": "$1 نے صفحہ $3 کو {{GENDER:$2|بحال کیا}}", diff --git a/languages/i18n/vi.json b/languages/i18n/vi.json index 3f4ed587ec..9d344ff148 100644 --- a/languages/i18n/vi.json +++ b/languages/i18n/vi.json @@ -38,7 +38,8 @@ "Asmen", "Stephanecbisson", "Quoclinh94", - "Tmp1109" + "Tmp1109", + "Phjtieudoc" ] }, "tog-underline": "Gạch chân liên kết:", @@ -476,7 +477,7 @@ "nosuchusershort": "Không có thành viên nào có tên “$1”. Xin hãy kiểm tra lại chính tả.", "nouserspecified": "Bạn phải chỉ định một tên người dùng.", "login-userblocked": "Thành viên này đã bị cấm. Không cho phép đăng nhập.", - "wrongpassword": "Mật khẩu sai. Xin vui lòng nhập lại.", + "wrongpassword": "Tên đăng nhập hoặc mật khẩu sai. Xin vui lòng nhập lại.", "wrongpasswordempty": "Bạn chÆ°a gõ vào mật khẩu. Xin thá»­ lần nữa.", "passwordtooshort": "Mật khẩu phải có ít nhất {{PLURAL:$1|1 ký tá»±|$1 ký tá»±}}.", "passwordtoolong": "Mật khẩu không thể dài hÆ¡n {{PLURAL:$1|1 ký tá»±|$1 ký tá»±}}.", @@ -549,11 +550,11 @@ "botpasswords-insert-failed": "Không thể thêm tên bot \"$1\". Nó đã được thêm vào chÆ°a?", "botpasswords-update-failed": "Không thể khi cập nhật bot có tên “$1”. Có phải nó đã bị xóa?", "botpasswords-created-title": "Mật khẩu bot đã được tạo", - "botpasswords-created-body": "Đã tạo mật khẩu cho bot “$1” của người dùng “$2”.", + "botpasswords-created-body": "Đã tạo mật khẩu cho bot tên \"$1\" của {{GENDER:$2|thành viên}} \"$2\".", "botpasswords-updated-title": "Mật khẩu Bot đã được cập nhật", - "botpasswords-updated-body": "Đã cập nhật mật khẩu cho bot “$1” của người dùng “$2”.", + "botpasswords-updated-body": "Đã cập nhật mật khẩu cho bot tên \"$1\" của {{GENDER:$2|thành viên}} \"$2\".", "botpasswords-deleted-title": "Mật khẩu bot đã bị xóa", - "botpasswords-deleted-body": "Đã xóa mật khẩu cho bot “$1” của người dùng “$2”.", + "botpasswords-deleted-body": "Đã xóa mật khẩu cho bot tên \"$1\" của {{GENDER:$2|thành viên}} \"$2\".", "botpasswords-newpassword": "Mật khẩu mới để đăng nhập nhÆ° <strong>$1</strong> là <strong>$2</strong>. <em>Xin hãy ghi lại mật khẩu này để mai mốt tham khảo.</em> <br> (Các bot cÅ© cần tên đăng nhập khớp với tên người dùng cuối cùng có thể sá»­ dụng tên người dùng <strong>$3</strong> và mật khẩu <strong>$4</strong>.)", "botpasswords-no-provider": "BotPasswordsSessionProvider không có sẵn.", "botpasswords-restriction-failed": "Mật khẩu bot giới hạn ngăn chặn đăng nhập này.", diff --git a/languages/i18n/zh-hans.json b/languages/i18n/zh-hans.json index 8fb4a565c5..5262c8c4a3 100644 --- a/languages/i18n/zh-hans.json +++ b/languages/i18n/zh-hans.json @@ -1098,6 +1098,7 @@ "timezoneregion-indian": "印度洋", "timezoneregion-pacific": "太平洋", "allowemail": "允许其他用户向我发送电子邮件", + "email-allow-new-users-label": "允许来自新用户的电子邮件", "email-blacklist-label": "禁止这些用户给我发送电子邮件:", "prefs-searchoptions": "搜索", "prefs-namespaces": "名字空间", @@ -1267,6 +1268,7 @@ "right-siteadmin": "锁定和解锁数据库", "right-override-export-depth": "导出页面,包括最多5层链接", "right-sendemail": "发送电子邮件给其他用户", + "right-sendemail-new-users": "发送电子邮件至没有日志记载操作的用户", "right-managechangetags": "创建和(取消)激活[[Special:Tags|标签]]", "right-applychangetags": "连同某人的更改一起应用[[Special:Tags|标签]]", "right-changetags": "在个别修订和日志记录中添加和移除任意[[Special:Tags|标签]]", @@ -1368,6 +1370,7 @@ "recentchanges-noresult": "在此期间没有符合这些条件的编辑", "recentchanges-timeout": "此次搜索已超时。您可以尝试不同的搜索参数。", "recentchanges-network": "由于技术错误,没有加载任何结果。请尝试刷新页面。", + "recentchanges-notargetpage": "在上方输入页面名以查看与该页面相关的更改。", "recentchanges-feed-description": "用这个源跟踪本wiki的最近更改。", "recentchanges-label-newpage": "该编辑创建了新页面", "recentchanges-label-minor": "该编辑为小编辑", @@ -1500,11 +1503,11 @@ "rcfilters-watchlist-showupdated": "自更改发生以来,对您尚未访问的页面做出的更改以<strong>粗体</strong>显示,并带有实心圆形标记。", "rcfilters-preference-label": "隐藏改进的最近更改版本", "rcfilters-preference-help": "返回到2017年界面重新设计版ï¼Œå¹¶é‡æ–°æ·»åŠ è¿™ä»¥åŽæ–°å¢žçš„工具。", - "rcfilters-filter-showlinkedfrom-label": "显示链接自该页面的页面上的更改:", - "rcfilters-filter-showlinkedfrom-option-label": "显示链接自某一页面的页面上的更改", - "rcfilters-filter-showlinkedto-label": "显示链接到该页面的页面上的更改:", - "rcfilters-filter-showlinkedto-option-label": "显示链接到某一页面的页面上的更改", - "rcfilters-target-page-placeholder": "选择页面", + "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": "下面{{PLURAL:$5|是}}<strong>$3 $4</strong>之后的更改(最多显示<strong>$1</strong>个)。", "rclistfromreset": "重置时间选择", "rclistfrom": "显示$3 $2之后的新更改", @@ -1549,7 +1552,7 @@ "recentchangeslinked-feed": "相关更改", "recentchangeslinked-toolbox": "相关更改", "recentchangeslinked-title": "与“$1”有关的更改", - "recentchangeslinked-summary": "这是链接自指定页面(或至指定分类的成员)的页面的最近更改的列表。[[Special:Watchlist|您的监视列表]]中的页面以<strong>粗体</strong>显示。", + "recentchangeslinked-summary": "输入页面名称以查看链入(或链自)相关页面的更改。(要查看分类的成员,请输入Category:分类名称)。对[[Special:Watchlist|您的监视列表]]上页面的更改以<strong>粗体</strong>显示。", "recentchangeslinked-page": "页面名称:", "recentchangeslinked-to": "显示链到所给出的页面", "recentchanges-page-added-to-category": "[[:$1]]已添加至分类", @@ -3010,14 +3013,15 @@ "variantname-zh": "不转换", "variantname-gan-hans": "赣语(简体)", "variantname-gan-hant": "赣语(繁体)", - "variantname-kk-cyrl": "kk-cyrl", - "variantname-crh-latn": "克里米亚鞑靼文(拉丁)", - "variantname-crh-cyrl": "克里米亚鞑靼文(西里尔)", + "variantname-kk-cyrl": "哈萨克语(西里尔字母)", + "variantname-crh-latn": "克里米亚鞑靼语(拉丁)", + "variantname-crh-cyrl": "克里米亚鞑靼语(西里尔)", "metadata": "元数据", "metadata-help": "此文件中包含有额外的信息。这些信息可能是由数码相机或扫描仪在创建或数字化过程中所添加的。如果文件自初始状态已受到修改,一些详细说明可能无法反映修改后的文件。", "metadata-expand": "显示详细资料", "metadata-collapse": "隐藏详细资料", "metadata-fields": "在本信息中所列出的 EXIF 元数据域将包含在图片显示页面,当元数据表损坏时只显示以下信息。其他的元数据默认为隐藏。\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", "exif-imagewidth": "宽度", "exif-imagelength": "高度", "exif-bitspersample": "每像素字节数", @@ -3194,6 +3198,7 @@ "exif-personinimage": "描绘的人物", "exif-originalimageheight": "裁剪前的图像高度", "exif-originalimagewidth": "裁剪前的图像宽度", + "exif-subjectnewscode-value": "$2($1)", "exif-compression-1": "未压缩", "exif-compression-2": "CCITT第3组一维修改霍夫曼游程编码", "exif-compression-3": "CCITT第3组传真编码", @@ -3201,6 +3206,7 @@ "exif-compression-6": "JPEG(旧)", "exif-copyrighted-true": "受版权保护", "exif-copyrighted-false": "版权状态未设定", + "exif-photometricinterpretation-0": "黑白(白为0)", "exif-photometricinterpretation-1": "黑白(黑为0)", "exif-unknowndate": "未知日期", "exif-orientation-1": "标准", @@ -3412,6 +3418,7 @@ "imgmultipagenext": "下一页 →", "imgmultigo": "提交!", "imgmultigoto": "前往第$1页", + "img-lang-opt": "$2($1)", "img-lang-default": "(默认语言)", "img-lang-info": "以$1生成本图像。$2", "img-lang-go": "提交", @@ -3432,7 +3439,8 @@ "autosumm-changed-redirect-target": "已将重定向目标从[[$1]]更改为[[$2]]", "autosumm-new": "创建页面,内容为“$1”", "autosumm-newblank": "创建空白页面", - "size-bytes": "$1字节", + "size-bytes": "$1{{PLURAL:$1|字节}}", + "size-pixel": "$1{{PLURAL:$1|像素}}", "lag-warn-normal": "过去$1秒内的更改未必会在这个列表中显示。", "lag-warn-high": "由于数据库的过度延迟,$1秒内的更改未必会在这个列表中显示。", "watchlistedit-normal-title": "编辑监视列表", @@ -3875,6 +3883,7 @@ "default-skin-not-found-row-disabled": "* <code>$1</code> / $2(<strong>已禁用</strong>)", "mediastatistics": "媒体统计", "mediastatistics-summary": "有关上传文件类型的统计。这只包含文件的最新版本,旧版本或删除版本则不会包括。", + "mediastatistics-nfiles": "$1($2%)", "mediastatistics-nbytes": "{{PLURAL:$1|$1字节}}($2;$3%)", "mediastatistics-bytespertype": "此段落的总文件大小:{{PLURAL:$1|$1字节}}($2;$3%)。", "mediastatistics-allbytes": "所有文件的总文件大小:{{PLURAL:$1|$1字节}}($2)。", @@ -4025,8 +4034,10 @@ "authmanager-provider-password-domain": "基于密码和域的身份验证", "authmanager-provider-temporarypassword": "临时密码", "authprovider-confirmlink-message": "基于您最近的登录尝试,以下账户可被链接至您的wiki账户。链接它们会启用通过这些账户的登录。请选择应链接的账户。", + "authprovider-confirmlink-option": "$1($2)", "authprovider-confirmlink-request-label": "应被链接的账户", "authprovider-confirmlink-success-line": "$1:已成功连接。", + "authprovider-confirmlink-failed-line": "$1:$2", "authprovider-confirmlink-failed": "账户链接未完全成功:$1", "authprovider-confirmlink-ok-help": "在显示链接失败消息后继续。", "authprovider-resetpass-skip-label": "跳过", diff --git a/languages/i18n/zh-hant.json b/languages/i18n/zh-hant.json index 7c527ca701..e10dc54e09 100644 --- a/languages/i18n/zh-hant.json +++ b/languages/i18n/zh-hant.json @@ -1481,6 +1481,10 @@ "rcfilters-watchlist-edit-watchlist-button": "編輯您的監視頁面列表", "rcfilters-watchlist-showupdated": "自更改發生以來,對您尚未訪問的頁面做出的更改以<strong>粗體</strong>顯示,並帶有實心圓形標記。", "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>某一頁面的頁面上的更改", "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。", "rclistfromreset": "重設日期選擇", "rclistfrom": "顯示自 $3 $2 以來的新變更", @@ -1525,7 +1529,7 @@ "recentchangeslinked-feed": "相關變更", "recentchangeslinked-toolbox": "相關變更", "recentchangeslinked-title": "與 \"$1\" 相關的變更", - "recentchangeslinked-summary": "此頁面列出指定頁面連結到的所有頁面中 (或指定分類中的所有成員) 有做變更的頁面清單。\n有在 [[Special:Watchlist|您的監視清單]] 中的頁面會以 <strong>粗體</strong> 標示。", + "recentchangeslinked-summary": "輸入頁面名稱,來查看頁面所連入或連出頁面的變更。(要查看分類成員的話,請輸入 Category:分類名稱)。會對在[[Special:Watchlist|您的監視清單]]上頁面更改為<strong>粗體</strong>顯示。", "recentchangeslinked-page": "頁面名稱:", "recentchangeslinked-to": "改顯示連結至指定頁面的變更", "recentchanges-page-added-to-category": "[[:$1]] 已加入至分類", @@ -2655,7 +2659,7 @@ "import": "匯入頁面", "importinterwiki": "從其他 wiki 匯入", "import-interwiki-text": "請選擇一個 Wiki 與頁面標題以進行匯入。\n會同時記錄修訂日期和編輯者的名稱。\n所有的從跨 Wiki 匯入操作都會被記錄在 [[Special:Log/import|匯入日誌]]。", - "import-interwiki-sourcewiki": "來源 Wiki:", + "import-interwiki-sourcewiki": "原始 wiki:", "import-interwiki-sourcepage": "來源頁面:", "import-interwiki-history": "複製此頁面的所有歷史修訂", "import-interwiki-templates": "包含所有模板", @@ -3330,7 +3334,7 @@ "confirm-purge-title": "清除此頁快取", "confirm_purge_button": "確定", "confirm-purge-top": "要清除此頁面的快取嗎?", - "confirm-purge-bottom": "清除頁面會清除快取紀錄並強制顯示最近的頁面修訂。", + "confirm-purge-bottom": "清除頁面會清除快取並強制顯示最近的頁面修訂。", "confirm-watch-button": "確定", "confirm-watch-top": "新增此頁面至您的監視清單?", "confirm-unwatch-button": "確定", @@ -3491,6 +3495,18 @@ "tag-list-wrapper": "([[Special:Tags|$1 個標籤]]:$2)", "tag-mw-contentmodelchange": "內容模型變更", "tag-mw-contentmodelchange-description": "編輯 [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel 更改頁面的內容模型]。", + "tag-mw-new-redirect": "新重新導向", + "tag-mw-new-redirect-description": "建立新重新導向或更改頁面為重新導向的編輯", + "tag-mw-removed-redirect": "移除重新導向", + "tag-mw-removed-redirect-description": "將現有重新導向更改為非重新導向的編輯", + "tag-mw-changed-redirect-target": "重新導向目標更改", + "tag-mw-changed-redirect-target-description": "更改重新導向目標的編輯", + "tag-mw-blank": "清空", + "tag-mw-blank-description": "清空頁面的編輯", + "tag-mw-replace": "替換", + "tag-mw-replace-description": "移除超過90%頁面內容的編輯", + "tag-mw-rollback": "回退", + "tag-mw-rollback-description": "使用回退連結回退上一編輯的編輯", "tags-title": "標籤", "tags-intro": "此頁面列出所有可用來標示編輯內容的標籤以及這些標籤所代表的意思。", "tags-tag": "標籤名稱", diff --git a/languages/messages/MessagesKo.php b/languages/messages/MessagesKo.php index 3146a36b5e..42cc676e1f 100644 --- a/languages/messages/MessagesKo.php +++ b/languages/messages/MessagesKo.php @@ -82,8 +82,11 @@ $specialPageAliases = [ 'Blankpage' => [ '빈문서' ], 'Block' => [ '차단', 'IP차단', '사용자차단' ], 'Booksources' => [ '책찾기' ], + 'BotPasswords' => [ '봇비밀번호' ], 'BrokenRedirects' => [ '끊긴넘겨주기' ], 'Categories' => [ '분류' ], + 'ChangeContentModel' => [ '콘텐츠모델바꾸기', '콘텐츠모델변경' ], + 'ChangeCredentials' => [ '자격증명바꾸기', '자격증명변경' ], 'ChangeEmail' => [ '이메일바꾸기', '이메일변경' ], 'ChangePassword' => [ '비밀번호바꾸기', '비밀번호변경' ], 'ComparePages' => [ '문서비교' ], @@ -94,6 +97,7 @@ $specialPageAliases = [ 'DeletedContributions' => [ '삭제된기여' ], 'Diff' => [ '차이' ], 'DoubleRedirects' => [ '이중넘겨주기' ], + 'EditTags' => [ '태그편집' ], 'EditWatchlist' => [ '주시문서목록편집' ], 'Emailuser' => [ '이메일보내기', '이메일' ], 'ExpandTemplates' => [ '틀전개' ], @@ -101,6 +105,7 @@ $specialPageAliases = [ 'Fewestrevisions' => [ '역사짧은문서' ], 'FileDuplicateSearch' => [ '중복파일검색', '중복파일찾기' ], 'Filepath' => [ '파일경로', '그림경로' ], + 'GoToInterwiki' => [ '인터위키가기' ], 'Import' => [ '가져오기' ], 'Invalidateemail' => [ '이메일인증취소', '이메일인증해제' ], 'JavaScriptTest' => [ '자바스크립트시험', '자바스크립트테스트' ], @@ -109,7 +114,8 @@ $specialPageAliases = [ 'Listadmins' => [ '관리자', '관리자목록' ], 'Listbots' => [ '봇', '봇목록' ], 'Listfiles' => [ '파일', '그림', '파일목록', '그림목록' ], - 'Listgrouprights' => [ '사용자권한', '권한목록' ], + 'Listgrouprights' => [ '사용자권한목록', '사용자권한', '권한목록' ], + 'Listgrants' => [ '권한부여목록' ], 'Listredirects' => [ '넘겨주기목록' ], 'ListDuplicatedFiles' => [ '중복된파일목록' ], 'Listusers' => [ '사용자', '사용자목록' ], diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 255892b52d..a3e0ffd917 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -662,7 +662,7 @@ abstract class Maintenance { global $IP, $wgCommandLineMode, $wgRequestTime; # Abort if called from a web server - if ( isset( $_SERVER ) && isset( $_SERVER['REQUEST_METHOD'] ) ) { + if ( PHP_SAPI !== 'cli' ) { $this->fatalError( 'This script must be run from the command line' ); } diff --git a/maintenance/backup.inc b/maintenance/backup.inc index 341a2992ff..00dbd00c86 100644 --- a/maintenance/backup.inc +++ b/maintenance/backup.inc @@ -25,7 +25,6 @@ */ require_once __DIR__ . '/Maintenance.php'; -require_once __DIR__ . '/../includes/export/DumpFilter.php'; use Wikimedia\Rdbms\LoadBalancer; use Wikimedia\Rdbms\IDatabase; @@ -420,20 +419,3 @@ class BackupDumper extends Maintenance { } } } - -class ExportProgressFilter extends DumpFilter { - function __construct( &$sink, &$progress ) { - parent::__construct( $sink ); - $this->progress = $progress; - } - - function writeClosePage( $string ) { - parent::writeClosePage( $string ); - $this->progress->reportPage(); - } - - function writeRevision( $rev, $string ) { - parent::writeRevision( $rev, $string ); - $this->progress->revCount(); - } -} diff --git a/maintenance/benchmarks/Benchmarker.php b/maintenance/benchmarks/Benchmarker.php index 832da4db79..ffb8cb353f 100644 --- a/maintenance/benchmarks/Benchmarker.php +++ b/maintenance/benchmarks/Benchmarker.php @@ -26,7 +26,9 @@ * @ingroup Benchmark */ +// @codeCoverageIgnoreStart require_once __DIR__ . '/../Maintenance.php'; +// @codeCoverageIgnoreEnd /** * Base class for benchmark scripts. diff --git a/maintenance/cleanupBlocks.php b/maintenance/cleanupBlocks.php index 5c5c88f0b0..41d492d4d8 100644 --- a/maintenance/cleanupBlocks.php +++ b/maintenance/cleanupBlocks.php @@ -55,8 +55,8 @@ class CleanupBlocks extends Maintenance { 'ipblocks', [ 'ipb_user' ], [ - "ipb_user >= $from", - "ipb_user <= $to", + "ipb_user >= " . (int)$from, + "ipb_user <= " . (int)$to, ], __METHOD__, [ @@ -128,8 +128,8 @@ class CleanupBlocks extends Maintenance { [ 'ipb_id', 'user_name' ], [ 'ipb_user = user_id', - "ipb_user >= $from", - "ipb_user <= $to", + "ipb_user >= " . (int)$from, + "ipb_user <= " . (int)$to, 'ipb_address != user_name', ], __METHOD__ diff --git a/maintenance/dumpIterator.php b/maintenance/dumpIterator.php index 254f368042..707f4b3c8c 100644 --- a/maintenance/dumpIterator.php +++ b/maintenance/dumpIterator.php @@ -77,6 +77,9 @@ abstract class DumpIterator extends Maintenance { $importer->setRevisionCallback( [ $this, 'handleRevision' ] ); + $importer->setNoticeCallback( function ( $msg, $params ) { + echo wfMessage( $msg, $params )->text() . "\n"; + } ); $this->from = $this->getOption( 'from', null ); $this->count = 0; diff --git a/maintenance/generateLocalAutoload.php b/maintenance/generateLocalAutoload.php index 0c278bc18e..bec11a0de1 100644 --- a/maintenance/generateLocalAutoload.php +++ b/maintenance/generateLocalAutoload.php @@ -4,12 +4,14 @@ if ( PHP_SAPI != 'cli' ) { die( "This script can only be run from the command line.\n" ); } +require_once __DIR__ . '/../includes/AutoLoader.php'; require_once __DIR__ . '/../includes/utils/AutoloadGenerator.php'; // Mediawiki installation directory $base = dirname( __DIR__ ); $generator = new AutoloadGenerator( $base, 'local' ); +$generator->setExcludePaths( array_values( AutoLoader::getAutoloadNamespaces() ) ); $generator->initMediaWikiDefault(); // Write out the autoload diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 2923b381dc..918c1ab98b 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -322,6 +322,9 @@ TEXT $this->pageCount = $nthPage - 1; } $importer->setPageCallback( [ $this, 'reportPage' ] ); + $importer->setNoticeCallback( function ( $msg, $params ) { + echo wfMessage( $msg, $params )->text() . "\n"; + } ); $this->importCallback = $importer->setRevisionCallback( [ $this, 'handleRevision' ] ); $this->uploadCallback = $importer->setUploadCallback( diff --git a/maintenance/language/zhtable/toCN.manual b/maintenance/language/zhtable/toCN.manual index a63149e6a4..4715cecc7a 100644 --- a/maintenance/language/zhtable/toCN.manual +++ b/maintenance/language/zhtable/toCN.manual @@ -2682,3 +2682,4 @@ A型肝炎 甲型肝炎 塑膠袋 塑料袋 烏龍麵 乌冬面 披索 比索 +真人騷 真人秀 diff --git a/maintenance/language/zhtable/toHK.manual b/maintenance/language/zhtable/toHK.manual index b71764ad7c..efcfcb6be0 100644 --- a/maintenance/language/zhtable/toHK.manual +++ b/maintenance/language/zhtable/toHK.manual @@ -3053,3 +3053,4 @@ IP地址 IP位址 人行道 行人路 塑料袋 膠袋 烏龍麵 烏冬麵 +真人秀 真人騷 diff --git a/maintenance/language/zhtable/toTW.manual b/maintenance/language/zhtable/toTW.manual index 16e275168d..6b5a857d73 100644 --- a/maintenance/language/zhtable/toTW.manual +++ b/maintenance/language/zhtable/toTW.manual @@ -750,6 +750,8 @@ IP地址 IP位址 數碼技術 數位技術 数字信号 數位訊號 數碼訊號 數位訊號 +数字音乐 數位音樂 +數碼音樂 數位音樂 数字化 數位化 數碼化 數位化 移动网络 行動網路 @@ -788,3 +790,4 @@ IP地址 IP位址 塑料袋 塑膠袋 触摸屏 觸控螢幕 乌冬面 烏龍麵 +真人騷 真人秀 diff --git a/maintenance/language/zhtable/tradphrases.manual b/maintenance/language/zhtable/tradphrases.manual index c5d5fd7350..ba2f5e8e12 100644 --- a/maintenance/language/zhtable/tradphrases.manual +++ b/maintenance/language/zhtable/tradphrases.manual @@ -2171,6 +2171,7 @@ 埃及歷史 大明歷史 大歷史 +大歷險 大衍歷史 太初歷史 官歷史 @@ -2981,6 +2982,8 @@ 編碼表 字碼表 電碼表 +碼碼表 +碼表示 科斗 灕水 這只不 diff --git a/maintenance/language/zhtable/tradphrases_exclude.manual b/maintenance/language/zhtable/tradphrases_exclude.manual index 3ab14eb199..ce1c524ca6 100644 --- a/maintenance/language/zhtable/tradphrases_exclude.manual +++ b/maintenance/language/zhtable/tradphrases_exclude.manual @@ -778,3 +778,4 @@ 箇舊 條幹 檯布 +髮姐 diff --git a/maintenance/migrateUserGroup.php b/maintenance/migrateUserGroup.php index 81c2353b14..703d97f9b5 100644 --- a/maintenance/migrateUserGroup.php +++ b/maintenance/migrateUserGroup.php @@ -63,7 +63,7 @@ class MigrateUserGroup extends Maintenance { $dbw->update( 'user_groups', [ 'ug_group' => $newGroup ], [ 'ug_group' => $oldGroup, - "ug_user BETWEEN $blockStart AND $blockEnd" ], + "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ], __METHOD__, [ 'IGNORE' ] ); @@ -74,7 +74,7 @@ class MigrateUserGroup extends Maintenance { // user/group is UNIQUE. $dbw->delete( 'user_groups', [ 'ug_group' => $oldGroup, - "ug_user BETWEEN $blockStart AND $blockEnd" ], + "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ], __METHOD__ ); $affected += $dbw->affectedRows(); @@ -86,7 +86,7 @@ class MigrateUserGroup extends Maintenance { // were in the new group and not in the group. $res = $dbw->select( 'user_groups', 'ug_user', [ 'ug_group' => $newGroup, - "ug_user BETWEEN $blockStart AND $blockEnd" ], + "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ], __METHOD__ ); if ( $res !== false ) { diff --git a/maintenance/oracle/archives/patch-auto_increment_triggers.sql b/maintenance/oracle/archives/patch-auto_increment_triggers.sql index 6b471b04b7..62a2f4fbbe 100644 --- a/maintenance/oracle/archives/patch-auto_increment_triggers.sql +++ b/maintenance/oracle/archives/patch-auto_increment_triggers.sql @@ -24,7 +24,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.mwuser_default_user_id BEFORE INSERT ON &mw_prefix.mwuser +CREATE TRIGGER &mw_prefix.mwuser_seq_trg BEFORE INSERT ON &mw_prefix.mwuser FOR EACH ROW WHEN (new.user_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(user_user_id_seq.nextval, :new.user_id); @@ -32,7 +32,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.page_default_page_id BEFORE INSERT ON &mw_prefix.page +CREATE TRIGGER &mw_prefix.page_seq_trg BEFORE INSERT ON &mw_prefix.page FOR EACH ROW WHEN (new.page_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(page_page_id_seq.nextval, :new.page_id); @@ -40,7 +40,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.revision_default_rev_id BEFORE INSERT ON &mw_prefix.revision +CREATE TRIGGER &mw_prefix.revision_seq_trg BEFORE INSERT ON &mw_prefix.revision FOR EACH ROW WHEN (new.rev_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(revision_rev_id_seq.nextval, :new.rev_id); @@ -48,7 +48,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.text_default_old_id BEFORE INSERT ON &mw_prefix.text +CREATE TRIGGER &mw_prefix.pagecontent_seq_trg BEFORE INSERT ON &mw_prefix.pagecontent FOR EACH ROW WHEN (new.old_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(text_old_id_seq.nextval, :new.old_id); @@ -56,7 +56,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.archive_default_ar_id BEFORE INSERT ON &mw_prefix.archive +CREATE TRIGGER &mw_prefix.archive_seq_trg BEFORE INSERT ON &mw_prefix.archive FOR EACH ROW WHEN (new.ar_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(archive_ar_id_seq.nextval, :new.ar_id); @@ -64,7 +64,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.category_default_cat_id BEFORE INSERT ON &mw_prefix.category +CREATE TRIGGER &mw_prefix.category_seq_trg BEFORE INSERT ON &mw_prefix.category FOR EACH ROW WHEN (new.cat_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(category_cat_id_seq.nextval, :new.cat_id); @@ -72,7 +72,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.externallinks_default_el_id BEFORE INSERT ON &mw_prefix.externallinks +CREATE TRIGGER &mw_prefix.externallinks_seq_trg BEFORE INSERT ON &mw_prefix.externallinks FOR EACH ROW WHEN (new.el_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(externallinks_el_id_seq.nextval, :new.el_id); @@ -80,7 +80,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.ipblocks_default_ipb_id BEFORE INSERT ON &mw_prefix.ipblocks +CREATE TRIGGER &mw_prefix.ipblocks_seq_trg BEFORE INSERT ON &mw_prefix.ipblocks FOR EACH ROW WHEN (new.ipb_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(ipblocks_ipb_id_seq.nextval, :new.ipb_id); @@ -88,7 +88,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.filearchive_default_fa_id BEFORE INSERT ON &mw_prefix.filearchive +CREATE TRIGGER &mw_prefix.filearchive_seq_trg BEFORE INSERT ON &mw_prefix.filearchive FOR EACH ROW WHEN (new.fa_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(filearchive_fa_id_seq.nextval, :new.fa_id); @@ -96,7 +96,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.uploadstash_default_us_id BEFORE INSERT ON &mw_prefix.uploadstash +CREATE TRIGGER &mw_prefix.uploadstash_seq_trg BEFORE INSERT ON &mw_prefix.uploadstash FOR EACH ROW WHEN (new.us_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(uploadstash_us_id_seq.nextval, :new.us_id); @@ -104,7 +104,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.recentchanges_default_rc_id BEFORE INSERT ON &mw_prefix.recentchanges +CREATE TRIGGER &mw_prefix.recentchanges_seq_trg BEFORE INSERT ON &mw_prefix.recentchanges FOR EACH ROW WHEN (new.rc_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(recentchanges_rc_id_seq.nextval, :new.rc_id); @@ -112,7 +112,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.logging_default_log_id BEFORE INSERT ON &mw_prefix.logging +CREATE TRIGGER &mw_prefix.logging_seq_trg BEFORE INSERT ON &mw_prefix.logging FOR EACH ROW WHEN (new.log_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(logging_log_id_seq.nextval, :new.log_id); @@ -120,7 +120,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.job_default_job_id BEFORE INSERT ON &mw_prefix.job +CREATE TRIGGER &mw_prefix.job_seq_trg BEFORE INSERT ON &mw_prefix.job FOR EACH ROW WHEN (new.job_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(job_job_id_seq.nextval, :new.job_id); @@ -128,7 +128,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.page_restrictions_default_pr_id BEFORE INSERT ON &mw_prefix.page_restrictions +CREATE TRIGGER &mw_prefix.page_restrictions_seq_trg BEFORE INSERT ON &mw_prefix.page_restrictions FOR EACH ROW WHEN (new.pr_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(page_restrictions_pr_id_seq.nextval, :new.pr_id); @@ -136,7 +136,7 @@ END; /*$mw$*/ /*$mw$*/ -CREATE TRIGGER &mw_prefix.sites_default_site_id BEFORE INSERT ON &mw_prefix.sites +CREATE TRIGGER &mw_prefix.sites_seq_trg BEFORE INSERT ON &mw_prefix.sites FOR EACH ROW WHEN (new.site_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(sites_site_id_seq.nextval, :new.site_id); diff --git a/maintenance/oracle/archives/patch-externallinks-el_index_60.sql b/maintenance/oracle/archives/patch-externallinks-el_index_60.sql index c4b906d1a7..39680ef3b4 100644 --- a/maintenance/oracle/archives/patch-externallinks-el_index_60.sql +++ b/maintenance/oracle/archives/patch-externallinks-el_index_60.sql @@ -1,5 +1,5 @@ define mw_prefix='{$wgDBprefix}'; -ALTER TABLE &mw_prefix.externallinks ADD el_index_60 VARBINARY(60) NOT NULL DEFAULT ''; +ALTER TABLE &mw_prefix.externallinks ADD el_index_60 VARCHAR2(60); CREATE INDEX &mw_prefix.externallinks_i04 ON &mw_prefix.externallinks (el_index_60, el_id); CREATE INDEX &mw_prefix.externallinks_i05 ON &mw_prefix.externallinks (el_from, el_index_60, el_id); diff --git a/maintenance/oracle/tables.sql b/maintenance/oracle/tables.sql index e6e2e5657c..d588e3a67c 100644 --- a/maintenance/oracle/tables.sql +++ b/maintenance/oracle/tables.sql @@ -48,7 +48,7 @@ CREATE UNIQUE INDEX &mw_prefix.mwuser_u01 ON &mw_prefix.mwuser (user_name); CREATE INDEX &mw_prefix.mwuser_i01 ON &mw_prefix.mwuser (user_email_token); CREATE INDEX &mw_prefix.mwuser_i02 ON &mw_prefix.mwuser (user_email, user_name); /*$mw$*/ -CREATE TRIGGER &mw_prefix.mwuser_default_user_id BEFORE INSERT ON &mw_prefix.mwuser +CREATE TRIGGER &mw_prefix.mwuser_seq_trg BEFORE INSERT ON &mw_prefix.mwuser FOR EACH ROW WHEN (new.user_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(user_user_id_seq.nextval, :new.user_id); @@ -116,7 +116,7 @@ CREATE INDEX &mw_prefix.page_i01 ON &mw_prefix.page (page_random); CREATE INDEX &mw_prefix.page_i02 ON &mw_prefix.page (page_len); CREATE INDEX &mw_prefix.page_i03 ON &mw_prefix.page (page_is_redirect, page_namespace, page_len); /*$mw$*/ -CREATE TRIGGER &mw_prefix.page_default_page_id BEFORE INSERT ON &mw_prefix.page +CREATE TRIGGER &mw_prefix.page_seq_trg BEFORE INSERT ON &mw_prefix.page FOR EACH ROW WHEN (new.page_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(page_page_id_seq.nextval, :new.page_id); @@ -162,7 +162,7 @@ CREATE INDEX &mw_prefix.revision_i03 ON &mw_prefix.revision (rev_user,rev_timest CREATE INDEX &mw_prefix.revision_i04 ON &mw_prefix.revision (rev_user_text,rev_timestamp); CREATE INDEX &mw_prefix.revision_i05 ON &mw_prefix.revision (rev_page,rev_user,rev_timestamp); /*$mw$*/ -CREATE TRIGGER &mw_prefix.revision_default_rev_id BEFORE INSERT ON &mw_prefix.revision +CREATE TRIGGER &mw_prefix.revision_seq_trg BEFORE INSERT ON &mw_prefix.revision FOR EACH ROW WHEN (new.rev_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(revision_rev_id_seq.nextval, :new.rev_id); @@ -177,7 +177,7 @@ CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text' ); ALTER TABLE &mw_prefix.pagecontent ADD CONSTRAINT &mw_prefix.pagecontent_pk PRIMARY KEY (old_id); /*$mw$*/ -CREATE TRIGGER &mw_prefix.text_default_old_id BEFORE INSERT ON &mw_prefix.text +CREATE TRIGGER &mw_prefix.pagecontent_seq_trg BEFORE INSERT ON &mw_prefix.pagecontent FOR EACH ROW WHEN (new.old_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(text_old_id_seq.nextval, :new.old_id); @@ -212,7 +212,7 @@ CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp); CREATE INDEX &mw_prefix.archive_i03 ON &mw_prefix.archive (ar_rev_id); /*$mw$*/ -CREATE TRIGGER &mw_prefix.archive_default_ar_id BEFORE INSERT ON &mw_prefix.archive +CREATE TRIGGER &mw_prefix.archive_seq_trg BEFORE INSERT ON &mw_prefix.archive FOR EACH ROW WHEN (new.ar_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(archive_ar_id_seq.nextval, :new.ar_id); @@ -273,7 +273,7 @@ ALTER TABLE &mw_prefix.category ADD CONSTRAINT &mw_prefix.category_pk PRIMARY KE CREATE UNIQUE INDEX &mw_prefix.category_u01 ON &mw_prefix.category (cat_title); CREATE INDEX &mw_prefix.category_i01 ON &mw_prefix.category (cat_pages); /*$mw$*/ -CREATE TRIGGER &mw_prefix.category_default_cat_id BEFORE INSERT ON &mw_prefix.category +CREATE TRIGGER &mw_prefix.category_seq_trg BEFORE INSERT ON &mw_prefix.category FOR EACH ROW WHEN (new.cat_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(category_cat_id_seq.nextval, :new.cat_id); @@ -286,7 +286,7 @@ CREATE TABLE &mw_prefix.externallinks ( el_from NUMBER NOT NULL, el_to VARCHAR2(2048) NOT NULL, el_index VARCHAR2(2048) NOT NULL, - el_index_60 VARBINARY(60) NOT NULL DEFAULT '' + el_index_60 VARCHAR2(60) ); ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id); ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_fk1 FOREIGN KEY (el_from) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; @@ -296,7 +296,7 @@ CREATE INDEX &mw_prefix.externallinks_i03 ON &mw_prefix.externallinks (el_index) CREATE INDEX &mw_prefix.externallinks_i04 ON &mw_prefix.externallinks (el_index_60, el_id); CREATE INDEX &mw_prefix.externallinks_i05 ON &mw_prefix.externallinks (el_from, el_index_60, el_id); /*$mw$*/ -CREATE TRIGGER &mw_prefix.externallinks_default_el_id BEFORE INSERT ON &mw_prefix.externallinks +CREATE TRIGGER &mw_prefix.externallinks_seq_trg BEFORE INSERT ON &mw_prefix.externallinks FOR EACH ROW WHEN (new.el_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(externallinks_el_id_seq.nextval, :new.el_id); @@ -361,7 +361,7 @@ CREATE INDEX &mw_prefix.ipblocks_i03 ON &mw_prefix.ipblocks (ipb_timestamp); CREATE INDEX &mw_prefix.ipblocks_i04 ON &mw_prefix.ipblocks (ipb_expiry); CREATE INDEX &mw_prefix.ipblocks_i05 ON &mw_prefix.ipblocks (ipb_parent_block_id); /*$mw$*/ -CREATE TRIGGER &mw_prefix.ipblocks_default_ipb_id BEFORE INSERT ON &mw_prefix.ipblocks +CREATE TRIGGER &mw_prefix.ipblocks_seq_trg BEFORE INSERT ON &mw_prefix.ipblocks FOR EACH ROW WHEN (new.ipb_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(ipblocks_ipb_id_seq.nextval, :new.ipb_id); @@ -452,7 +452,7 @@ CREATE INDEX &mw_prefix.filearchive_i03 ON &mw_prefix.filearchive (fa_deleted_ti CREATE INDEX &mw_prefix.filearchive_i04 ON &mw_prefix.filearchive (fa_user_text,fa_timestamp); CREATE INDEX &mw_prefix.filearchive_i05 ON &mw_prefix.filearchive (fa_sha1); /*$mw$*/ -CREATE TRIGGER &mw_prefix.filearchive_default_fa_id BEFORE INSERT ON &mw_prefix.filearchive +CREATE TRIGGER &mw_prefix.filearchive_seq_trg BEFORE INSERT ON &mw_prefix.filearchive FOR EACH ROW WHEN (new.fa_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(filearchive_fa_id_seq.nextval, :new.fa_id); @@ -485,7 +485,7 @@ CREATE INDEX &mw_prefix.uploadstash_i01 ON &mw_prefix.uploadstash (us_user); CREATE INDEX &mw_prefix.uploadstash_i02 ON &mw_prefix.uploadstash (us_timestamp); CREATE UNIQUE INDEX &mw_prefix.uploadstash_u01 ON &mw_prefix.uploadstash (us_key); /*$mw$*/ -CREATE TRIGGER &mw_prefix.uploadstash_default_us_id BEFORE INSERT ON &mw_prefix.uploadstash +CREATE TRIGGER &mw_prefix.uploadstash_seq_trg BEFORE INSERT ON &mw_prefix.uploadstash FOR EACH ROW WHEN (new.us_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(uploadstash_us_id_seq.nextval, :new.us_id); @@ -532,7 +532,7 @@ CREATE INDEX &mw_prefix.recentchanges_i06 ON &mw_prefix.recentchanges (rc_namesp CREATE INDEX &mw_prefix.recentchanges_i07 ON &mw_prefix.recentchanges (rc_user_text, 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_default_rc_id BEFORE INSERT ON &mw_prefix.recentchanges +CREATE TRIGGER &mw_prefix.recentchanges_seq_trg BEFORE INSERT ON &mw_prefix.recentchanges FOR EACH ROW WHEN (new.rc_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(recentchanges_rc_id_seq.nextval, :new.rc_id); @@ -617,7 +617,7 @@ CREATE INDEX &mw_prefix.logging_i05 ON &mw_prefix.logging (log_type, log_action, 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); /*$mw$*/ -CREATE TRIGGER &mw_prefix.logging_default_log_id BEFORE INSERT ON &mw_prefix.logging +CREATE TRIGGER &mw_prefix.logging_seq_trg BEFORE INSERT ON &mw_prefix.logging FOR EACH ROW WHEN (new.log_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(logging_log_id_seq.nextval, :new.log_id); @@ -654,7 +654,7 @@ CREATE INDEX &mw_prefix.job_i03 ON &mw_prefix.job (job_sha1); CREATE INDEX &mw_prefix.job_i04 ON &mw_prefix.job (job_cmd,job_token,job_random); CREATE INDEX &mw_prefix.job_i05 ON &mw_prefix.job (job_attempts); /*$mw$*/ -CREATE TRIGGER &mw_prefix.job_default_job_id BEFORE INSERT ON &mw_prefix.job +CREATE TRIGGER &mw_prefix.job_seq_trg BEFORE INSERT ON &mw_prefix.job FOR EACH ROW WHEN (new.job_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(job_job_id_seq.nextval, :new.job_id); @@ -706,7 +706,7 @@ CREATE INDEX &mw_prefix.page_restrictions_i01 ON &mw_prefix.page_restrictions (p CREATE INDEX &mw_prefix.page_restrictions_i02 ON &mw_prefix.page_restrictions (pr_level); CREATE INDEX &mw_prefix.page_restrictions_i03 ON &mw_prefix.page_restrictions (pr_cascade); /*$mw$*/ -CREATE TRIGGER &mw_prefix.page_restrictions_default_pr_id BEFORE INSERT ON &mw_prefix.page_restrictions +CREATE TRIGGER &mw_prefix.page_restrictions_seq_trg BEFORE INSERT ON &mw_prefix.page_restrictions FOR EACH ROW WHEN (new.pr_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(page_restrictions_pr_id_seq.nextval, :new.pr_id); @@ -821,7 +821,7 @@ CREATE INDEX &mw_prefix.sites_i05 ON &mw_prefix.sites (site_protocol); CREATE INDEX &mw_prefix.sites_i06 ON &mw_prefix.sites (site_domain); CREATE INDEX &mw_prefix.sites_i07 ON &mw_prefix.sites (site_forward); /*$mw$*/ -CREATE TRIGGER &mw_prefix.sites_default_site_id BEFORE INSERT ON &mw_prefix.sites +CREATE TRIGGER &mw_prefix.sites_seq_trg BEFORE INSERT ON &mw_prefix.sites FOR EACH ROW WHEN (new.site_id IS NULL) BEGIN &mw_prefix.lastval_pkg.setLastval(sites_site_id_seq.nextval, :new.site_id); diff --git a/maintenance/oracle/user.sql b/maintenance/oracle/user.sql index 57688eaeeb..0a36ac4c91 100644 --- a/maintenance/oracle/user.sql +++ b/maintenance/oracle/user.sql @@ -14,3 +14,5 @@ grant create synonym to &wiki_user.; grant create table to &wiki_user.; grant create sequence to &wiki_user.; grant create trigger to &wiki_user.; +grant create type to &wiki_user.; +grant create procedure to &wiki_user.; diff --git a/maintenance/orphans.php b/maintenance/orphans.php index 644fb958df..54a68aeb36 100644 --- a/maintenance/orphans.php +++ b/maintenance/orphans.php @@ -202,8 +202,8 @@ class Orphans extends Maintenance { $result2 = $dbw->query( " SELECT MAX(rev_timestamp) as max_timestamp FROM $revision - WHERE rev_page=$row->page_id - " ); + WHERE rev_page=" . (int)( $row->page_id ) + ); $row2 = $dbw->fetchObject( $result2 ); if ( $row2 ) { if ( $row->rev_timestamp != $row2->max_timestamp ) { diff --git a/maintenance/populateBacklinkNamespace.php b/maintenance/populateBacklinkNamespace.php index eb8280613f..f5f1de69d3 100644 --- a/maintenance/populateBacklinkNamespace.php +++ b/maintenance/populateBacklinkNamespace.php @@ -67,7 +67,7 @@ class PopulateBacklinkNamespace extends LoggedUpdateMaintenance { $blockEnd = $start + $batchSize - 1; while ( $blockEnd <= $end ) { $this->output( "...doing page_id from $blockStart to $blockEnd\n" ); - $cond = "page_id BETWEEN $blockStart AND $blockEnd"; + $cond = "page_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd; $res = $db->select( 'page', [ 'page_id', 'page_namespace' ], $cond, __METHOD__ ); foreach ( $res as $row ) { $db->update( 'pagelinks', diff --git a/maintenance/populateIpChanges.php b/maintenance/populateIpChanges.php index 4becf6dd4d..216582086c 100644 --- a/maintenance/populateIpChanges.php +++ b/maintenance/populateIpChanges.php @@ -87,7 +87,7 @@ TEXT $rows = $dbr->select( 'revision', [ 'rev_id', 'rev_timestamp', 'rev_user_text' ], - [ "rev_id BETWEEN $blockStart AND $blockEnd", 'rev_user' => 0 ], + [ "rev_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd, 'rev_user' => 0 ], __METHOD__ ); diff --git a/maintenance/populateLogSearch.php b/maintenance/populateLogSearch.php index 113cc63950..979a28a20e 100644 --- a/maintenance/populateLogSearch.php +++ b/maintenance/populateLogSearch.php @@ -76,7 +76,7 @@ class PopulateLogSearch extends LoggedUpdateMaintenance { $delTypes = [ 'delete', 'suppress' ]; // revisiondelete types while ( $blockEnd <= $end ) { $this->output( "...doing log_id from $blockStart to $blockEnd\n" ); - $cond = "log_id BETWEEN $blockStart AND $blockEnd"; + $cond = "log_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd; $res = $db->select( 'logging', [ 'log_id', 'log_type', 'log_action', 'log_params' ], $cond, __FUNCTION__ ); diff --git a/maintenance/populateLogUsertext.php b/maintenance/populateLogUsertext.php index c5c079ac0a..6e8261a612 100644 --- a/maintenance/populateLogUsertext.php +++ b/maintenance/populateLogUsertext.php @@ -64,7 +64,8 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance { $blockEnd = $start + $batchSize - 1; while ( $blockEnd <= $end ) { $this->output( "...doing log_id from $blockStart to $blockEnd\n" ); - $cond = "log_id BETWEEN $blockStart AND $blockEnd AND log_user = user_id"; + $cond = "log_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd . + " AND log_user = user_id"; $res = $db->select( [ 'logging', 'user' ], [ 'log_id', 'user_name' ], $cond, __METHOD__ ); diff --git a/maintenance/populateRecentChangesSource.php b/maintenance/populateRecentChangesSource.php index 04ad255cc3..0a53dd614b 100644 --- a/maintenance/populateRecentChangesSource.php +++ b/maintenance/populateRecentChangesSource.php @@ -65,7 +65,7 @@ class PopulateRecentChangesSource extends LoggedUpdateMaintenance { [ $updatedValues ], [ "rc_source = ''", - "rc_id BETWEEN $blockStart AND $blockEnd" + "rc_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ], __METHOD__ ); diff --git a/maintenance/populateRevisionSha1.php b/maintenance/populateRevisionSha1.php index f3506ec70f..7ced779bfd 100644 --- a/maintenance/populateRevisionSha1.php +++ b/maintenance/populateRevisionSha1.php @@ -93,8 +93,8 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance { $blockEnd = $start + $batchSize - 1; while ( $blockEnd <= $end ) { $this->output( "...doing $idCol from $blockStart to $blockEnd\n" ); - $cond = "$idCol BETWEEN $blockStart AND $blockEnd - AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''"; + $cond = "$idCol BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd . + " AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''"; $res = $db->select( $queryInfo['tables'], $queryInfo['fields'], $cond, __METHOD__, [], $queryInfo['joins'] ); diff --git a/maintenance/rebuildFileCache.php b/maintenance/rebuildFileCache.php index 0b5b9b04e3..1fc1daf5a4 100644 --- a/maintenance/rebuildFileCache.php +++ b/maintenance/rebuildFileCache.php @@ -104,7 +104,7 @@ class RebuildFileCache extends Maintenance { $res = $dbr->select( 'page', [ 'page_namespace', 'page_title', 'page_id' ], [ 'page_namespace' => MWNamespace::getContentNamespaces(), - "page_id BETWEEN $blockStart AND $blockEnd" ], + "page_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ], __METHOD__, [ 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ] ); diff --git a/maintenance/recountCategories.php b/maintenance/recountCategories.php index ed6a357b36..6a00d570e8 100644 --- a/maintenance/recountCategories.php +++ b/maintenance/recountCategories.php @@ -156,7 +156,7 @@ TEXT [ "cat_{$this->mode}" => $row->count ], [ 'cat_id' => $row->cat_id, - "cat_{$this->mode} != {$row->count}", + "cat_{$this->mode} != " . (int)( $row->count ), ], __METHOD__ ); $affectedRows += $dbw->affectedRows(); diff --git a/maintenance/renderDump.php b/maintenance/renderDump.php index 68a371c3b2..458556ffce 100644 --- a/maintenance/renderDump.php +++ b/maintenance/renderDump.php @@ -66,6 +66,9 @@ class DumpRenderer extends Maintenance { $importer->setRevisionCallback( [ $this, 'handleRevision' ] ); + $importer->setNoticeCallback( function ( $msg, $params ) { + echo wfMessage( $msg, $params )->text() . "\n"; + } ); $importer->doImport(); diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php index acf0103bfa..8f55b88215 100644 --- a/maintenance/storage/checkStorage.php +++ b/maintenance/storage/checkStorage.php @@ -100,8 +100,12 @@ class CheckStorage { $missingTextRows = array_flip( $this->oldIdMap ); $externalRevs = []; $objectRevs = []; - $res = $dbr->select( 'text', [ 'old_id', 'old_flags' ], - 'old_id IN (' . implode( ',', $this->oldIdMap ) . ')', __METHOD__ ); + $res = $dbr->select( + 'text', + [ 'old_id', 'old_flags' ], + [ 'old_id' => $this->oldIdMap ], + __METHOD__ + ); foreach ( $res as $row ) { /** * @var $flags int @@ -158,8 +162,12 @@ class CheckStorage { $externalConcatBlobs = []; $externalNormalBlobs = []; if ( count( $externalRevs ) ) { - $res = $dbr->select( 'text', [ 'old_id', 'old_flags', 'old_text' ], - [ 'old_id IN (' . implode( ',', $externalRevs ) . ')' ], __METHOD__ ); + $res = $dbr->select( + 'text', + [ 'old_id', 'old_flags', 'old_text' ], + [ 'old_id' => $externalRevs ], + __METHOD__ + ); foreach ( $res as $row ) { $urlParts = explode( '://', $row->old_text, 2 ); if ( count( $urlParts ) !== 2 || $urlParts[1] == '' ) { @@ -200,7 +208,9 @@ class CheckStorage { $blobsTable = $this->dbStore->getTable( $extDb ); $res = $extDb->select( $blobsTable, [ 'blob_id' ], - [ 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ], __METHOD__ ); + [ 'blob_id' => $blobIds ], + __METHOD__ + ); foreach ( $res as $row ) { unset( $xBlobIds[$row->blob_id] ); } @@ -224,7 +234,7 @@ class CheckStorage { $res = $dbr->select( 'text', [ 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ], - [ 'old_id IN (' . implode( ',', $objectRevs ) . ')' ], + [ 'old_id' => $objectRevs ], __METHOD__ ); foreach ( $res as $row ) { @@ -283,7 +293,7 @@ class CheckStorage { $res = $dbr->select( 'text', [ 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ], - [ 'old_id IN (' . implode( ',', array_keys( $concatBlobs ) ) . ')' ], + [ 'old_id' => array_keys( $concatBlobs ) ], __METHOD__ ); foreach ( $res as $row ) { @@ -402,7 +412,9 @@ class CheckStorage { $headerLength = strlen( self::CONCAT_HEADER ); $res = $extDb->select( $blobsTable, [ 'blob_id', "LEFT(blob_text, $headerLength) AS header" ], - [ 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ], __METHOD__ ); + [ 'blob_id' => $blobIds ], + __METHOD__ + ); foreach ( $res as $row ) { if ( strcasecmp( $row->header, self::CONCAT_HEADER ) ) { $this->addError( @@ -481,6 +493,9 @@ class CheckStorage { MediaWikiServices::getInstance()->getMainConfig() ); $importer->setRevisionCallback( [ $this, 'importRevision' ] ); + $importer->setNoticeCallback( function ( $msg, $params ) { + echo wfMessage( $msg, $params )->text() . "\n"; + } ); $importer->doImport(); } diff --git a/maintenance/updateRestrictions.php b/maintenance/updateRestrictions.php index c0b7b10f9b..c4dccbc65f 100644 --- a/maintenance/updateRestrictions.php +++ b/maintenance/updateRestrictions.php @@ -59,7 +59,8 @@ class UpdateRestrictions extends Maintenance { $encodedExpiry = 'infinity'; while ( $blockEnd <= $end ) { $this->output( "...doing page_id from $blockStart to $blockEnd out of $end\n" ); - $cond = "page_id BETWEEN $blockStart AND $blockEnd AND page_restrictions !=''"; + $cond = "page_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd . + " AND page_restrictions !=''"; $res = $db->select( 'page', [ 'page_id', 'page_namespace', 'page_restrictions' ], diff --git a/resources/Resources.php b/resources/Resources.php index fbc4dbb56d..7d37b5023b 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1792,6 +1792,7 @@ return [ 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagItemWidget.js', 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js', 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js', + 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MainWrapperWidget.js', 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ViewSwitchWidget.js', 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js', 'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js', @@ -1841,9 +1842,13 @@ return [ 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.LiveUpdateButtonWidget.less', 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RcTopSectionWidget.less', 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclToOrFromWidget.less', + 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclTargetPageWidget.less', 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.WatchlistTopSectionWidget.less', ], 'skinStyles' => [ + 'vector' => [ + 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.vector.less', + ], 'monobook' => [ 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.monobook.less', 'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.CapsuleItemWidget.monobook.less', @@ -2116,6 +2121,7 @@ return [ 'resources/src/mediawiki.special/mediawiki.special.preferences.convertmessagebox.js', 'resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js', 'resources/src/mediawiki.special/mediawiki.special.preferences.timezone.js', + 'resources/src/mediawiki.special/mediawiki.special.preferences.personalEmail.js', ], 'messages' => [ 'prefs-tabs-navigation-hint', diff --git a/resources/lib/jquery.chosen/LICENSE b/resources/lib/jquery.chosen/LICENSE index 0675dc5264..5e1332c127 100644 --- a/resources/lib/jquery.chosen/LICENSE +++ b/resources/lib/jquery.chosen/LICENSE @@ -1,10 +1,9 @@ -# Chosen, a Select Box Enhancer for jQuery and Protoype -## by Patrick Filler for [Harvest](http://getharvest.com) +#### Chosen +- by Patrick Filler for [Harvest](http://getharvest.com) +- Copyright (c) 2011-2016 by Harvest Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License) -Copyright (c) 2011-2013 by Harvest - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/resources/lib/jquery.chosen/README.md b/resources/lib/jquery.chosen/README.md new file mode 100644 index 0000000000..5b212563bd --- /dev/null +++ b/resources/lib/jquery.chosen/README.md @@ -0,0 +1,48 @@ +# Chosen + +Chosen is a library for making long, unwieldy select boxes more user friendly. + +- jQuery support: 1.7+ +- Prototype support: 1.7+ + +For **documentation**, usage, and examples, see: +http://harvesthq.github.io/chosen/ + +For **downloads**, see: +https://github.com/harvesthq/chosen/releases/ + +### Package managers + +Chosen is available through [Bower](https://bower.io/) and [npm](https://www.npmjs.com), +_however, the package names are not the same_. + +To install with Bower: + +``` +bower install chosen +``` + +To install with npm: + +``` +npm install chosen-js +``` + +The compiled files for these packages are automatically generated and stored in a [2nd Chosen repository](https://github.com/harvesthq/chosen-package). No pull requests will be accepted to that repository. + +### Contributing to this project + +We welcome all to participate in making Chosen the best software it can be. The repository is maintained by only a few people, but has accepted contributions from over 50 authors after reviewing hundreds of pull requests related to thousands of issues. You can help reduce the maintainers' workload (and increase your chance of having an accepted contribution to Chosen) by following the +[guidelines for contributing](contributing.md). + +* [Bug reports](contributing.md#bugs) +* [Feature requests](contributing.md#features) +* [Pull requests](contributing.md#pull-requests) + +### Chosen Credits + +- Concept and development by [Patrick Filler](http://patrickfiller.com) for [Harvest](http://getharvest.com/). +- Design and CSS by [Matthew Lettini](http://matthewlettini.com/) +- Repository maintained by [@pfiller](http://github.com/pfiller), [@kenearley](http://github.com/kenearley), [@stof](http://github.com/stof), [@koenpunt](http://github.com/koenpunt), and [@tjschuck](http://github.com/tjschuck). +- Chosen includes [contributions by many fine folks](https://github.com/harvesthq/chosen/contributors). + diff --git a/resources/lib/jquery.chosen/chosen-sprite.png b/resources/lib/jquery.chosen/chosen-sprite.png index 3611ae4ace..c57da70b4b 100644 Binary files a/resources/lib/jquery.chosen/chosen-sprite.png and b/resources/lib/jquery.chosen/chosen-sprite.png differ diff --git a/resources/lib/jquery.chosen/chosen-sprite@2x.png b/resources/lib/jquery.chosen/chosen-sprite@2x.png index bd61d9638f..6b50545202 100644 Binary files a/resources/lib/jquery.chosen/chosen-sprite@2x.png and b/resources/lib/jquery.chosen/chosen-sprite@2x.png differ diff --git a/resources/lib/jquery.chosen/chosen.css b/resources/lib/jquery.chosen/chosen.css index 17793ed742..d4219b4916 100644 --- a/resources/lib/jquery.chosen/chosen.css +++ b/resources/lib/jquery.chosen/chosen.css @@ -1,440 +1,490 @@ +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.8.2 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011-2017 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + /* @group Base */ -.chzn-container { - font-size: 13px; +.chosen-container { position: relative; display: inline-block; vertical-align: middle; - zoom: 1; - *display: inline; + font-size: 13px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } -.chzn-container .chzn-drop { - background: #fff; - border: 1px solid #aaa; - border-top: 0; + +.chosen-container * { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.chosen-container .chosen-drop { position: absolute; top: 100%; - left: -9999px; - -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15); - -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15); - box-shadow : 0 4px 5px rgba(0,0,0,.15); z-index: 1010; width: 100%; - -moz-box-sizing : border-box; - -ms-box-sizing : border-box; - -webkit-box-sizing: border-box; - -khtml-box-sizing : border-box; - box-sizing : border-box; + border: 1px solid #aaa; + border-top: 0; + background: #fff; + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); + box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); + clip: rect(0, 0, 0, 0); } -.chzn-container.chzn-with-drop .chzn-drop { - left: 0; +.chosen-container.chosen-with-drop .chosen-drop { + clip: auto; } -/* @end */ +.chosen-container a { + cursor: pointer; +} -/* @group Single Chosen */ -.chzn-container-single .chzn-single { - background-color: #ffffff; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); - background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - -webkit-border-radius: 5px; - -moz-border-radius : 5px; - border-radius : 5px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - border: 1px solid #aaaaaa; - -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1); - display: block; +.chosen-container .search-choice .group-name, .chosen-container .chosen-single .group-name { + margin-right: 4px; overflow: hidden; white-space: nowrap; + text-overflow: ellipsis; + font-weight: normal; + color: #999999; +} + +.chosen-container .search-choice .group-name:after, .chosen-container .chosen-single .group-name:after { + content: ":"; + padding-left: 2px; + vertical-align: top; +} + +/* @end */ +/* @group Single Chosen */ +.chosen-container-single .chosen-single { position: relative; - height: 23px; - line-height: 24px; + display: block; + overflow: hidden; padding: 0 0 0 8px; - color: #444444; + height: 25px; + border: 1px solid #aaa; + border-radius: 5px; + background-color: #fff; + background: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #fff), color-stop(50%, #f6f6f6), color-stop(52%, #eee), to(#f4f4f4)); + background: linear-gradient(#fff 20%, #f6f6f6 50%, #eee 52%, #f4f4f4 100%); + background-clip: padding-box; + -webkit-box-shadow: 0 0 3px #fff inset, 0 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 3px #fff inset, 0 1px 1px rgba(0, 0, 0, 0.1); + color: #444; text-decoration: none; + white-space: nowrap; + line-height: 24px; } -.chzn-container-single .chzn-default { + +.chosen-container-single .chosen-default { color: #999; } -.chzn-container-single .chzn-single span { - margin-right: 26px; + +.chosen-container-single .chosen-single span { display: block; overflow: hidden; - white-space: nowrap; - -o-text-overflow: ellipsis; - -ms-text-overflow: ellipsis; + margin-right: 26px; text-overflow: ellipsis; + white-space: nowrap; } -.chzn-container-single .chzn-single abbr { - display: block; + +.chosen-container-single .chosen-single-with-deselect span { + margin-right: 38px; +} + +.chosen-container-single .chosen-single abbr { position: absolute; - right: 26px; top: 6px; + right: 26px; + display: block; width: 12px; height: 12px; + background: url("chosen-sprite.png") -42px 1px no-repeat; font-size: 1px; - background: url('chosen-sprite.png') -42px 1px no-repeat; } -.chzn-container-single .chzn-single abbr:hover { + +.chosen-container-single .chosen-single abbr:hover { background-position: -42px -10px; } -.chzn-container-single.chzn-disabled .chzn-single abbr:hover { + +.chosen-container-single.chosen-disabled .chosen-single abbr:hover { background-position: -42px -10px; } -.chzn-container-single .chzn-single div { + +.chosen-container-single .chosen-single div { position: absolute; - right: 0; top: 0; + right: 0; display: block; - height: 100%; width: 18px; + height: 100%; } -.chzn-container-single .chzn-single div b { - background: url('chosen-sprite.png') no-repeat 0px 2px; + +.chosen-container-single .chosen-single div b { display: block; width: 100%; height: 100%; + background: url("chosen-sprite.png") no-repeat 0px 2px; } -.chzn-container-single .chzn-search { - padding: 3px 4px; + +.chosen-container-single .chosen-search { position: relative; + z-index: 1010; margin: 0; + padding: 3px 4px; white-space: nowrap; - z-index: 1010; } -.chzn-container-single .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat 100% -20px; - background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + +.chosen-container-single .chosen-search input[type="text"] { margin: 1px 0; padding: 4px 20px 4px 5px; + width: 100%; + height: auto; outline: 0; border: 1px solid #aaa; - font-family: sans-serif; + background: url("chosen-sprite.png") no-repeat 100% -20px; font-size: 1em; - width: 100%; - -moz-box-sizing : border-box; - -ms-box-sizing : border-box; - -webkit-box-sizing: border-box; - -khtml-box-sizing : border-box; - box-sizing : border-box; + font-family: sans-serif; + line-height: normal; + border-radius: 0; } -.chzn-container-single .chzn-drop { + +.chosen-container-single .chosen-drop { margin-top: -1px; - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius : 0 0 4px 4px; - border-radius : 0 0 4px 4px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; -} -.chzn-container-single-nosearch .chzn-search { + border-radius: 0 0 4px 4px; + background-clip: padding-box; +} + +.chosen-container-single.chosen-container-single-nosearch .chosen-search { position: absolute; - left: -9999px; + clip: rect(0, 0, 0, 0); } + /* @end */ +/* @group Results */ +.chosen-container .chosen-results { + color: #444; + position: relative; + overflow-x: hidden; + overflow-y: auto; + margin: 0 4px 4px 0; + padding: 0 0 0 4px; + max-height: 240px; + -webkit-overflow-scrolling: touch; +} -/* @group Multi Chosen */ -.chzn-container-multi .chzn-choices { - background-color: #fff; - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); - border: 1px solid #aaa; +.chosen-container .chosen-results li { + display: none; margin: 0; - padding: 0; - cursor: text; - overflow: hidden; - height: auto !important; - height: 1%; + padding: 5px 6px; + list-style: none; + line-height: 15px; + word-wrap: break-word; + -webkit-touch-callout: none; +} + +.chosen-container .chosen-results li.active-result { + display: list-item; + cursor: pointer; +} + +.chosen-container .chosen-results li.disabled-result { + display: list-item; + color: #ccc; + cursor: default; +} + +.chosen-container .chosen-results li.highlighted { + background-color: #3875d7; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); + background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); + color: #fff; +} + +.chosen-container .chosen-results li.no-results { + color: #777; + display: list-item; + background: #f4f4f4; +} + +.chosen-container .chosen-results li.group-result { + display: list-item; + font-weight: bold; + cursor: default; +} + +.chosen-container .chosen-results li.group-option { + padding-left: 15px; +} + +.chosen-container .chosen-results li em { + font-style: normal; + text-decoration: underline; +} + +/* @end */ +/* @group Multi Chosen */ +.chosen-container-multi .chosen-choices { position: relative; + overflow: hidden; + margin: 0; + padding: 0 5px; width: 100%; - -moz-box-sizing : border-box; - -ms-box-sizing : border-box; - -webkit-box-sizing: border-box; - -khtml-box-sizing : border-box; - box-sizing : border-box; + height: auto; + border: 1px solid #aaa; + background-color: #fff; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(1%, #eee), color-stop(15%, #fff)); + background-image: linear-gradient(#eee 1%, #fff 15%); + cursor: text; } -.chzn-container-multi .chzn-choices li { + +.chosen-container-multi .chosen-choices li { float: left; list-style: none; } -.chzn-container-multi .chzn-choices .search-field { - white-space: nowrap; + +.chosen-container-multi .chosen-choices li.search-field { margin: 0; padding: 0; + white-space: nowrap; } -.chzn-container-multi .chzn-choices .search-field input { - color: #666; - background: transparent !important; - border: 0 !important; - font-family: sans-serif; - font-size: 100%; - height: 15px; - padding: 5px; + +.chosen-container-multi .chosen-choices li.search-field input[type="text"] { margin: 1px 0; + padding: 0; + height: 25px; outline: 0; + border: 0 !important; + background: transparent !important; -webkit-box-shadow: none; - -moz-box-shadow : none; - box-shadow : none; -} -.chzn-container-multi .chzn-choices .search-field .default { + box-shadow: none; color: #999; + font-size: 100%; + font-family: sans-serif; + line-height: normal; + border-radius: 0; + width: 25px; } -.chzn-container-multi .chzn-choices .search-choice { - -webkit-border-radius: 3px; - -moz-border-radius : 3px; - border-radius : 3px; - -moz-background-clip : padding; - -webkit-background-clip: padding-box; - background-clip : padding-box; - background-color: #e4e4e4; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); - box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + +.chosen-container-multi .chosen-choices li.search-choice { + position: relative; + margin: 3px 5px 3px 0; + padding: 3px 20px 3px 5px; + border: 1px solid #aaa; + max-width: 100%; + border-radius: 3px; + background-color: #eeeeee; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), to(#eee)); + background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); + background-size: 100% 19px; + background-repeat: repeat-x; + background-clip: padding-box; + -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); + box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); color: #333; - border: 1px solid #aaaaaa; line-height: 13px; - padding: 3px 20px 3px 5px; - margin: 3px 0 3px 5px; - position: relative; cursor: default; } -.chzn-container-multi .chzn-choices .search-choice.search-choice-disabled { - background-color: #e4e4e4; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - color: #666; - border: 1px solid #cccccc; - padding-right: 5px; -} -.chzn-container-multi .chzn-choices .search-choice-focus { - background: #d4d4d4; + +.chosen-container-multi .chosen-choices li.search-choice span { + word-wrap: break-word; } -.chzn-container-multi .chzn-choices .search-choice .search-choice-close { - display: block; + +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close { position: absolute; - right: 3px; top: 4px; + right: 3px; + display: block; width: 12px; height: 12px; + background: url("chosen-sprite.png") -42px 1px no-repeat; font-size: 1px; - background: url('chosen-sprite.png') -42px 1px no-repeat; } -.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover { + +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover { background-position: -42px -10px; } -.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close { - background-position: -42px -10px; + +.chosen-container-multi .chosen-choices li.search-choice-disabled { + padding-right: 5px; + border: 1px solid #ccc; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), to(#eee)); + background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); + color: #666; } -/* @end */ -/* @group Results */ -.chzn-container .chzn-results { - margin: 0 4px 4px 0; - max-height: 240px; - padding: 0 0 0 4px; - position: relative; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; +.chosen-container-multi .chosen-choices li.search-choice-focus { + background: #d4d4d4; } -.chzn-container-multi .chzn-results { - margin: 0; - padding: 0; + +.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close { + background-position: -42px -10px; } -.chzn-container .chzn-results li { - display: none; - line-height: 15px; - padding: 5px 6px; + +.chosen-container-multi .chosen-results { margin: 0; - list-style: none; -} -.chzn-container .chzn-results .active-result { - cursor: pointer; - display: list-item; -} -.chzn-container .chzn-results .highlighted { - background-color: #3875d7; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); - background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%); - background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); - color: #fff; -} -.chzn-container .chzn-results li em { - background: #feffde; - font-style: normal; -} -.chzn-container .chzn-results .highlighted em { - background: transparent; + padding: 0; } -.chzn-container .chzn-results .no-results { - background: #f4f4f4; + +.chosen-container-multi .chosen-drop .result-selected { display: list-item; -} -.chzn-container .chzn-results .group-result { + color: #ccc; cursor: default; - color: #999; - font-weight: bold; -} -.chzn-container .chzn-results .group-option { - padding-left: 15px; -} -.chzn-container-multi .chzn-drop .result-selected { - display: none; -} -.chzn-container .chzn-results-scroll { - background: white; - margin: 0 4px; - position: absolute; - text-align: center; - width: 321px; /* This should by dynamic with js */ - z-index: 1; -} -.chzn-container .chzn-results-scroll span { - display: inline-block; - height: 17px; - text-indent: -5000px; - width: 9px; -} -.chzn-container .chzn-results-scroll-down { - bottom: 0; -} -.chzn-container .chzn-results-scroll-down span { - background: url('chosen-sprite.png') no-repeat -4px -3px; -} -.chzn-container .chzn-results-scroll-up span { - background: url('chosen-sprite.png') no-repeat -22px -3px; } -/* @end */ +/* @end */ /* @group Active */ -.chzn-container-active .chzn-single { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); +.chosen-container-active .chosen-single { border: 1px solid #5897fb; + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); } -.chzn-container-active.chzn-with-drop .chzn-single { + +.chosen-container-active.chosen-with-drop .chosen-single { border: 1px solid #aaa; - -webkit-box-shadow: 0 1px 0 #fff inset; - -moz-box-shadow : 0 1px 0 #fff inset; - box-shadow : 0 1px 0 #fff inset; - background-color: #eee; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 ); - background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); - background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%); - background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); - -webkit-border-bottom-left-radius : 0; - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomleft : 0; - -moz-border-radius-bottomright: 0; - border-bottom-left-radius : 0; border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #eee), color-stop(80%, #fff)); + background-image: linear-gradient(#eee 20%, #fff 80%); + -webkit-box-shadow: 0 1px 0 #fff inset; + box-shadow: 0 1px 0 #fff inset; } -.chzn-container-active.chzn-with-drop .chzn-single div { - background: transparent; + +.chosen-container-active.chosen-with-drop .chosen-single div { border-left: none; + background: transparent; } -.chzn-container-active.chzn-with-drop .chzn-single div b { + +.chosen-container-active.chosen-with-drop .chosen-single div b { background-position: -18px 2px; } -.chzn-container-active .chzn-choices { - -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); - -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); - box-shadow : 0 0 5px rgba(0,0,0,.3); + +.chosen-container-active .chosen-choices { border: 1px solid #5897fb; + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); } -.chzn-container-active .chzn-choices .search-field input { - color: #111 !important; + +.chosen-container-active .chosen-choices li.search-field input[type="text"] { + color: #222 !important; } -/* @end */ +/* @end */ /* @group Disabled Support */ -.chzn-disabled { +.chosen-disabled { + opacity: 0.5 !important; cursor: default; - opacity:0.5 !important; } -.chzn-disabled .chzn-single { + +.chosen-disabled .chosen-single { cursor: default; } -.chzn-disabled .chzn-choices .search-choice .search-choice-close { + +.chosen-disabled .chosen-choices .search-choice .search-choice-close { cursor: default; } +/* @end */ /* @group Right to Left */ -.chzn-rtl { text-align: right; } -.chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; } -.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; } +.chosen-rtl { + text-align: right; +} + +.chosen-rtl .chosen-single { + overflow: visible; + padding: 0 8px 0 0; +} -.chzn-rtl .chzn-single div { left: 3px; right: auto; } -.chzn-rtl .chzn-single abbr { +.chosen-rtl .chosen-single span { + margin-right: 0; + margin-left: 26px; + direction: rtl; +} + +.chosen-rtl .chosen-single-with-deselect span { + margin-left: 38px; +} + +.chosen-rtl .chosen-single div { + right: auto; + left: 3px; +} + +.chosen-rtl .chosen-single abbr { + right: auto; left: 26px; +} + +.chosen-rtl .chosen-choices li { + float: right; +} + +.chosen-rtl .chosen-choices li.search-field input[type="text"] { + direction: rtl; +} + +.chosen-rtl .chosen-choices li.search-choice { + margin: 3px 5px 3px 0; + padding: 3px 5px 3px 19px; +} + +.chosen-rtl .chosen-choices li.search-choice .search-choice-close { right: auto; + left: 4px; +} + +.chosen-rtl.chosen-container-single .chosen-results { + margin: 0 0 4px 4px; + padding: 0 4px 0 0; +} + +.chosen-rtl .chosen-results li.group-option { + padding-right: 15px; + padding-left: 0; } -.chzn-rtl .chzn-choices .search-field input { direction: rtl; } -.chzn-rtl .chzn-choices li { float: right; } -.chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; } -.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; } -.chzn-rtl .chzn-search { left: 9999px; } -.chzn-rtl.chzn-with-drop .chzn-search { left: 0px; } -.chzn-rtl .chzn-drop { left: 9999px; } -.chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; } -.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; } -.chzn-rtl.chzn-container-active.chzn-with-drop .chzn-single div { border-right: none; } -.chzn-rtl .chzn-search input { - background: #fff url('chosen-sprite.png') no-repeat -30px -20px; - background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + +.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div { + border-right: none; +} + +.chosen-rtl .chosen-search input[type="text"] { padding: 4px 5px 4px 20px; + background: url("chosen-sprite.png") no-repeat -30px -20px; direction: rtl; } -.chzn-container-single.chzn-rtl .chzn-single div b { + +.chosen-rtl.chosen-container-single .chosen-single div b { background-position: 6px 2px; } -.chzn-container-single.chzn-rtl.chzn-with-drop .chzn-single div b { + +.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b { background-position: -12px 2px; } -/* @end */ +/* @end */ /* @group Retina compatibility */ -@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) { - .chzn-rtl .chzn-search input, .chzn-container-single .chzn-single abbr, .chzn-container-single .chzn-single div b, .chzn-container-single .chzn-search input, .chzn-container-multi .chzn-choices .search-choice .search-choice-close, .chzn-container .chzn-results-scroll-down span, .chzn-container .chzn-results-scroll-up span { - background-image: url('chosen-sprite@2x.png') !important; - background-repeat: no-repeat !important; - background-size: 52px 37px !important; +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { + .chosen-rtl .chosen-search input[type="text"], + .chosen-container-single .chosen-single abbr, + .chosen-container-single .chosen-single div b, + .chosen-container-single .chosen-search input[type="text"], + .chosen-container-multi .chosen-choices .search-choice .search-choice-close, + .chosen-container .chosen-results-scroll-down span, + .chosen-container .chosen-results-scroll-up span { + background-image: url("chosen-sprite@2x.png") !important; + background-size: 52px 37px !important; + background-repeat: no-repeat !important; } } + /* @end */ diff --git a/resources/lib/jquery.chosen/chosen.jquery.js b/resources/lib/jquery.chosen/chosen.jquery.js index 745174f768..b8e20eb732 100644 --- a/resources/lib/jquery.chosen/chosen.jquery.js +++ b/resources/lib/jquery.chosen/chosen.jquery.js @@ -1,17 +1,22 @@ -// Chosen, a Select Box Enhancer for jQuery and Protoype -// by Patrick Filler for Harvest, http://getharvest.com -// -// Version 0.9.14 -// Full source at https://github.com/harvesthq/chosen -// Copyright (c) 2011 Harvest http://getharvest.com - -// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -// This file is generated by `cake build`, do not edit it by hand. +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.8.2 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011-2017 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + (function() { - var SelectParser; + var $, AbstractChosen, Chosen, SelectParser, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; SelectParser = (function() { - function SelectParser() { this.options_index = 0; this.parsed = []; @@ -26,22 +31,24 @@ }; SelectParser.prototype.add_group = function(group) { - var group_position, option, _i, _len, _ref, _results; + var group_position, i, len, option, ref, results1; group_position = this.parsed.length; this.parsed.push({ array_index: group_position, group: true, label: group.label, + title: group.title ? group.title : void 0, children: 0, - disabled: group.disabled + disabled: group.disabled, + classes: group.className }); - _ref = group.childNodes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - option = _ref[_i]; - _results.push(this.add_option(option, group_position, group.disabled)); + ref = group.childNodes; + results1 = []; + for (i = 0, len = ref.length; i < len; i++) { + option = ref[i]; + results1.push(this.add_option(option, group_position, group.disabled)); } - return _results; + return results1; }; SelectParser.prototype.add_option = function(option, group_position, group_disabled) { @@ -56,9 +63,11 @@ value: option.value, text: option.text, html: option.innerHTML, + title: option.title ? option.title : void 0, selected: option.selected, disabled: group_disabled === true ? group_disabled : option.disabled, group_array_index: group_position, + group_label: group_position != null ? this.parsed[group_position].label : null, classes: option.className, style: option.style.cssText }); @@ -78,36 +87,21 @@ })(); SelectParser.select_to_array = function(select) { - var child, parser, _i, _len, _ref; + var child, i, len, parser, ref; parser = new SelectParser(); - _ref = select.childNodes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child = _ref[_i]; + ref = select.childNodes; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; parser.add_node(child); } return parser.parsed; }; - this.SelectParser = SelectParser; - -}).call(this); - -/* -Chosen source: generate output using 'cake build' -Copyright (c) 2011 by Harvest -*/ - - -(function() { - var AbstractChosen, root; - - root = this; - AbstractChosen = (function() { - - function AbstractChosen(form_field, options) { + function AbstractChosen(form_field, options1) { this.form_field = form_field; - this.options = options != null ? options : {}; + this.options = options1 != null ? options1 : {}; + this.label_click_handler = bind(this.label_click_handler, this); if (!AbstractChosen.browser_is_supported()) { return; } @@ -117,31 +111,40 @@ Copyright (c) 2011 by Harvest this.setup(); this.set_up_html(); this.register_observers(); - this.finish_setup(); + this.on_ready(); } AbstractChosen.prototype.set_default_values = function() { - var _this = this; - this.click_test_action = function(evt) { - return _this.test_active_click(evt); - }; - this.activate_action = function(evt) { - return _this.activate_field(evt); - }; + this.click_test_action = (function(_this) { + return function(evt) { + return _this.test_active_click(evt); + }; + })(this); + this.activate_action = (function(_this) { + return function(evt) { + return _this.activate_field(evt); + }; + })(this); this.active_field = false; this.mouse_on_container = false; this.results_showing = false; this.result_highlighted = null; - this.result_single_selected = null; + this.is_rtl = this.options.rtl || /\bchosen-rtl\b/.test(this.form_field.className); this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false; this.disable_search_threshold = this.options.disable_search_threshold || 0; this.disable_search = this.options.disable_search || false; this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true; + this.group_search = this.options.group_search != null ? this.options.group_search : true; this.search_contains = this.options.search_contains || false; - this.choices = 0; - this.single_backstroke_delete = this.options.single_backstroke_delete || false; + this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true; this.max_selected_options = this.options.max_selected_options || Infinity; - return this.inherit_select_classes = this.options.inherit_select_classes || false; + this.inherit_select_classes = this.options.inherit_select_classes || false; + this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true; + this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true; + this.include_group_label_in_selected = this.options.include_group_label_in_selected || false; + this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY; + this.case_sensitive_search = this.options.case_sensitive_search || false; + return this.hide_results_on_select = this.options.hide_results_on_select != null ? this.options.hide_results_on_select : true; }; AbstractChosen.prototype.set_default_text = function() { @@ -152,9 +155,18 @@ Copyright (c) 2011 by Harvest } else { this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text; } + this.default_text = this.escape_html(this.default_text); return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text; }; + AbstractChosen.prototype.choice_label = function(item) { + if (this.include_group_label_in_selected && (item.group_label != null)) { + return "<b class='group-name'>" + item.group_label + "</b>" + item.html; + } else { + return item.html; + } + }; + AbstractChosen.prototype.mouse_enter = function() { return this.mouse_on_container = true; }; @@ -164,12 +176,13 @@ Copyright (c) 2011 by Harvest }; AbstractChosen.prototype.input_focus = function(evt) { - var _this = this; if (this.is_multiple) { if (!this.active_field) { - return setTimeout((function() { - return _this.container_mousedown(); - }), 50); + return setTimeout(((function(_this) { + return function() { + return _this.container_mousedown(); + }; + })(this)), 50); } } else { if (!this.active_field) { @@ -179,34 +192,110 @@ Copyright (c) 2011 by Harvest }; AbstractChosen.prototype.input_blur = function(evt) { - var _this = this; if (!this.mouse_on_container) { this.active_field = false; - return setTimeout((function() { - return _this.blur_test(); - }), 100); + return setTimeout(((function(_this) { + return function() { + return _this.blur_test(); + }; + })(this)), 100); } }; - AbstractChosen.prototype.result_add_option = function(option) { - var classes, style; - if (!option.disabled) { - option.dom_id = this.container_id + "_o_" + option.array_index; - classes = option.selected && this.is_multiple ? [] : ["active-result"]; - if (option.selected) { - classes.push("result-selected"); + AbstractChosen.prototype.label_click_handler = function(evt) { + if (this.is_multiple) { + return this.container_mousedown(evt); + } else { + return this.activate_field(); + } + }; + + AbstractChosen.prototype.results_option_build = function(options) { + var content, data, data_content, i, len, ref, shown_results; + content = ''; + shown_results = 0; + ref = this.results_data; + for (i = 0, len = ref.length; i < len; i++) { + data = ref[i]; + data_content = ''; + if (data.group) { + data_content = this.result_add_group(data); + } else { + data_content = this.result_add_option(data); + } + if (data_content !== '') { + shown_results++; + content += data_content; } - if (option.group_array_index != null) { - classes.push("group-option"); + if (options != null ? options.first : void 0) { + if (data.selected && this.is_multiple) { + this.choice_build(data); + } else if (data.selected && !this.is_multiple) { + this.single_set_selected_text(this.choice_label(data)); + } } - if (option.classes !== "") { - classes.push(option.classes); + if (shown_results >= this.max_shown_results) { + break; } - style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : ""; - return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>'; - } else { - return ""; } + return content; + }; + + AbstractChosen.prototype.result_add_option = function(option) { + var classes, option_el; + if (!option.search_match) { + return ''; + } + if (!this.include_option_in_results(option)) { + return ''; + } + classes = []; + if (!option.disabled && !(option.selected && this.is_multiple)) { + classes.push("active-result"); + } + if (option.disabled && !(option.selected && this.is_multiple)) { + classes.push("disabled-result"); + } + if (option.selected) { + classes.push("result-selected"); + } + if (option.group_array_index != null) { + classes.push("group-option"); + } + if (option.classes !== "") { + classes.push(option.classes); + } + option_el = document.createElement("li"); + option_el.className = classes.join(" "); + option_el.style.cssText = option.style; + option_el.setAttribute("data-option-array-index", option.array_index); + option_el.innerHTML = option.highlighted_html || option.html; + if (option.title) { + option_el.title = option.title; + } + return this.outerHTML(option_el); + }; + + AbstractChosen.prototype.result_add_group = function(group) { + var classes, group_el; + if (!(group.search_match || group.group_match)) { + return ''; + } + if (!(group.active_options > 0)) { + return ''; + } + classes = []; + classes.push("group-result"); + if (group.classes) { + classes.push(group.classes); + } + group_el = document.createElement("li"); + group_el.className = classes.join(" "); + group_el.innerHTML = group.highlighted_html || this.escape_html(group.label); + if (group.title) { + group_el.title = group.title; + } + return this.outerHTML(group_el); }; AbstractChosen.prototype.results_update_field = function() { @@ -215,8 +304,25 @@ Copyright (c) 2011 by Harvest this.results_reset_cleanup(); } this.result_clear_highlight(); - this.result_single_selected = null; - return this.results_build(); + this.results_build(); + if (this.results_showing) { + return this.winnow_results(); + } + }; + + AbstractChosen.prototype.reset_single_select_options = function() { + var i, len, ref, result, results1; + ref = this.results_data; + results1 = []; + for (i = 0, len = ref.length; i < len; i++) { + result = ref[i]; + if (result.selected) { + results1.push(result.selected = false); + } else { + results1.push(void 0); + } + } + return results1; }; AbstractChosen.prototype.results_toggle = function() { @@ -235,106 +341,279 @@ Copyright (c) 2011 by Harvest } }; + AbstractChosen.prototype.winnow_results = function() { + var escapedQuery, fix, i, len, option, prefix, query, ref, regex, results, results_group, search_match, startpos, suffix, text; + this.no_results_clear(); + results = 0; + query = this.get_search_text(); + escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + regex = this.get_search_regex(escapedQuery); + ref = this.results_data; + for (i = 0, len = ref.length; i < len; i++) { + option = ref[i]; + option.search_match = false; + results_group = null; + search_match = null; + option.highlighted_html = ''; + if (this.include_option_in_results(option)) { + if (option.group) { + option.group_match = false; + option.active_options = 0; + } + if ((option.group_array_index != null) && this.results_data[option.group_array_index]) { + results_group = this.results_data[option.group_array_index]; + if (results_group.active_options === 0 && results_group.search_match) { + results += 1; + } + results_group.active_options += 1; + } + text = option.group ? option.label : option.text; + if (!(option.group && !this.group_search)) { + search_match = this.search_string_match(text, regex); + option.search_match = search_match != null; + if (option.search_match && !option.group) { + results += 1; + } + if (option.search_match) { + if (query.length) { + startpos = search_match.index; + prefix = text.slice(0, startpos); + fix = text.slice(startpos, startpos + query.length); + suffix = text.slice(startpos + query.length); + option.highlighted_html = (this.escape_html(prefix)) + "<em>" + (this.escape_html(fix)) + "</em>" + (this.escape_html(suffix)); + } + if (results_group != null) { + results_group.group_match = true; + } + } else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) { + option.search_match = true; + } + } + } + } + this.result_clear_highlight(); + if (results < 1 && query.length) { + this.update_results_content(""); + return this.no_results(query); + } else { + this.update_results_content(this.results_option_build()); + return this.winnow_results_set_highlight(); + } + }; + + AbstractChosen.prototype.get_search_regex = function(escaped_search_string) { + var regex_flag, regex_string; + regex_string = this.search_contains ? escaped_search_string : "(^|\\s|\\b)" + escaped_search_string + "[^\\s]*"; + if (!(this.enable_split_word_search || this.search_contains)) { + regex_string = "^" + regex_string; + } + regex_flag = this.case_sensitive_search ? "" : "i"; + return new RegExp(regex_string, regex_flag); + }; + + AbstractChosen.prototype.search_string_match = function(search_string, regex) { + var match; + match = regex.exec(search_string); + if (!this.search_contains && (match != null ? match[1] : void 0)) { + match.index += 1; + } + return match; + }; + + AbstractChosen.prototype.choices_count = function() { + var i, len, option, ref; + if (this.selected_option_count != null) { + return this.selected_option_count; + } + this.selected_option_count = 0; + ref = this.form_field.options; + for (i = 0, len = ref.length; i < len; i++) { + option = ref[i]; + if (option.selected) { + this.selected_option_count += 1; + } + } + return this.selected_option_count; + }; + AbstractChosen.prototype.choices_click = function(evt) { evt.preventDefault(); - if (!this.results_showing) { + this.activate_field(); + if (!(this.results_showing || this.is_disabled)) { return this.results_show(); } }; + AbstractChosen.prototype.keydown_checker = function(evt) { + var ref, stroke; + stroke = (ref = evt.which) != null ? ref : evt.keyCode; + this.search_field_scale(); + if (stroke !== 8 && this.pending_backstroke) { + this.clear_backstroke(); + } + switch (stroke) { + case 8: + this.backstroke_length = this.get_search_field_value().length; + break; + case 9: + if (this.results_showing && !this.is_multiple) { + this.result_select(evt); + } + this.mouse_on_container = false; + break; + case 13: + if (this.results_showing) { + evt.preventDefault(); + } + break; + case 27: + if (this.results_showing) { + evt.preventDefault(); + } + break; + case 32: + if (this.disable_search) { + evt.preventDefault(); + } + break; + case 38: + evt.preventDefault(); + this.keyup_arrow(); + break; + case 40: + evt.preventDefault(); + this.keydown_arrow(); + break; + } + }; + AbstractChosen.prototype.keyup_checker = function(evt) { - var stroke, _ref; - stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + var ref, stroke; + stroke = (ref = evt.which) != null ? ref : evt.keyCode; this.search_field_scale(); switch (stroke) { case 8: - if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) { - return this.keydown_backstroke(); + if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) { + this.keydown_backstroke(); } else if (!this.pending_backstroke) { this.result_clear_highlight(); - return this.results_search(); + this.results_search(); } break; case 13: evt.preventDefault(); if (this.results_showing) { - return this.result_select(evt); + this.result_select(evt); } break; case 27: if (this.results_showing) { this.results_hide(); } - return true; + break; case 9: + case 16: + case 17: + case 18: case 38: case 40: - case 16: case 91: - case 17: break; default: - return this.results_search(); + this.results_search(); + break; } }; - AbstractChosen.prototype.generate_field_id = function() { - var new_id; - new_id = this.generate_random_id(); - this.form_field.id = new_id; - return new_id; - }; - - AbstractChosen.prototype.generate_random_char = function() { - var chars, newchar, rand; - chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - rand = Math.floor(Math.random() * chars.length); - return newchar = chars.substring(rand, rand + 1); + AbstractChosen.prototype.clipboard_event_checker = function(evt) { + if (this.is_disabled) { + return; + } + return setTimeout(((function(_this) { + return function() { + return _this.results_search(); + }; + })(this)), 50); }; AbstractChosen.prototype.container_width = function() { - var width; if (this.options.width != null) { return this.options.width; + } else { + return this.form_field.offsetWidth + "px"; } - width = window.getComputedStyle != null ? parseFloat(window.getComputedStyle(this.form_field).getPropertyValue('width')) : (typeof jQuery !== "undefined" && jQuery !== null) && (this.form_field_jq != null) ? this.form_field_jq.outerWidth() : this.form_field.getWidth(); - return width + "px"; }; - AbstractChosen.browser_is_supported = function() { - var _ref; - if (window.navigator.appName === "Microsoft Internet Explorer") { - return (null !== (_ref = document.documentMode) && _ref >= 8); + AbstractChosen.prototype.include_option_in_results = function(option) { + if (this.is_multiple && (!this.display_selected_options && option.selected)) { + return false; + } + if (!this.display_disabled_options && option.disabled) { + return false; + } + if (option.empty) { + return false; } return true; }; - AbstractChosen.default_multiple_text = "Select Some Options"; + AbstractChosen.prototype.search_results_touchstart = function(evt) { + this.touch_started = true; + return this.search_results_mouseover(evt); + }; - AbstractChosen.default_single_text = "Select an Option"; + AbstractChosen.prototype.search_results_touchmove = function(evt) { + this.touch_started = false; + return this.search_results_mouseout(evt); + }; - AbstractChosen.default_no_result_text = "No results match"; + AbstractChosen.prototype.search_results_touchend = function(evt) { + if (this.touch_started) { + return this.search_results_mouseup(evt); + } + }; - return AbstractChosen; + AbstractChosen.prototype.outerHTML = function(element) { + var tmp; + if (element.outerHTML) { + return element.outerHTML; + } + tmp = document.createElement("div"); + tmp.appendChild(element); + return tmp.innerHTML; + }; - })(); + AbstractChosen.prototype.get_single_html = function() { + return "<a class=\"chosen-single chosen-default\">\n <span>" + this.default_text + "</span>\n <div><b></b></div>\n</a>\n<div class=\"chosen-drop\">\n <div class=\"chosen-search\">\n <input class=\"chosen-search-input\" type=\"text\" autocomplete=\"off\" />\n </div>\n <ul class=\"chosen-results\"></ul>\n</div>"; + }; - root.AbstractChosen = AbstractChosen; + AbstractChosen.prototype.get_multi_html = function() { + return "<ul class=\"chosen-choices\">\n <li class=\"search-field\">\n <input class=\"chosen-search-input\" type=\"text\" autocomplete=\"off\" value=\"" + this.default_text + "\" />\n </li>\n</ul>\n<div class=\"chosen-drop\">\n <ul class=\"chosen-results\"></ul>\n</div>"; + }; -}).call(this); + AbstractChosen.prototype.get_no_results_html = function(terms) { + return "<li class=\"no-results\">\n " + this.results_none_found + " <span>" + (this.escape_html(terms)) + "</span>\n</li>"; + }; -/* -Chosen source: generate output using 'cake build' -Copyright (c) 2011 by Harvest -*/ + AbstractChosen.browser_is_supported = function() { + if ("Microsoft Internet Explorer" === window.navigator.appName) { + return document.documentMode >= 8; + } + if (/iP(od|hone)/i.test(window.navigator.userAgent) || /IEMobile/i.test(window.navigator.userAgent) || /Windows Phone/i.test(window.navigator.userAgent) || /BlackBerry/i.test(window.navigator.userAgent) || /BB10/i.test(window.navigator.userAgent) || /Android.*Mobile/i.test(window.navigator.userAgent)) { + return false; + } + return true; + }; + AbstractChosen.default_multiple_text = "Select Some Options"; -(function() { - var $, Chosen, root, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + AbstractChosen.default_single_text = "Select an Option"; - root = this; + AbstractChosen.default_no_result_text = "No results match"; + + return AbstractChosen; + + })(); $ = jQuery; @@ -344,18 +623,24 @@ Copyright (c) 2011 by Harvest return this; } return this.each(function(input_field) { - var $this; + var $this, chosen; $this = $(this); - if (!$this.hasClass("chzn-done")) { - return $this.data('chosen', new Chosen(this, options)); + chosen = $this.data('chosen'); + if (options === 'destroy') { + if (chosen instanceof Chosen) { + chosen.destroy(); + } + return; + } + if (!(chosen instanceof Chosen)) { + $this.data('chosen', new Chosen(this, options)); } }); } }); - Chosen = (function(_super) { - - __extends(Chosen, _super); + Chosen = (function(superClass) { + extend(Chosen, superClass); function Chosen() { return Chosen.__super__.constructor.apply(this, arguments); @@ -363,153 +648,233 @@ Copyright (c) 2011 by Harvest Chosen.prototype.setup = function() { this.form_field_jq = $(this.form_field); - this.current_selectedIndex = this.form_field.selectedIndex; - return this.is_rtl = this.form_field_jq.hasClass("chzn-rtl"); - }; - - Chosen.prototype.finish_setup = function() { - return this.form_field_jq.addClass("chzn-done"); + return this.current_selectedIndex = this.form_field.selectedIndex; }; Chosen.prototype.set_up_html = function() { var container_classes, container_props; - this.container_id = this.form_field.id.length ? this.form_field.id.replace(/[^\w]/g, '_') : this.generate_field_id(); - this.container_id += "_chzn"; - container_classes = ["chzn-container"]; - container_classes.push("chzn-container-" + (this.is_multiple ? "multi" : "single")); + container_classes = ["chosen-container"]; + container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single")); if (this.inherit_select_classes && this.form_field.className) { container_classes.push(this.form_field.className); } if (this.is_rtl) { - container_classes.push("chzn-rtl"); + container_classes.push("chosen-rtl"); } container_props = { - 'id': this.container_id, 'class': container_classes.join(' '), - 'style': "width: " + (this.container_width()) + ";", 'title': this.form_field.title }; + if (this.form_field.id.length) { + container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen"; + } this.container = $("<div />", container_props); + this.container.width(this.container_width()); if (this.is_multiple) { - this.container.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:auto;" /></li></ul><div class="chzn-drop"><ul class="chzn-results"></ul></div>'); + this.container.html(this.get_multi_html()); } else { - this.container.html('<a href="javascript:void(0)" class="chzn-single chzn-default" tabindex="-1"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'); + this.container.html(this.get_single_html()); } this.form_field_jq.hide().after(this.container); - this.dropdown = this.container.find('div.chzn-drop').first(); + this.dropdown = this.container.find('div.chosen-drop').first(); this.search_field = this.container.find('input').first(); - this.search_results = this.container.find('ul.chzn-results').first(); + this.search_results = this.container.find('ul.chosen-results').first(); this.search_field_scale(); this.search_no_results = this.container.find('li.no-results').first(); if (this.is_multiple) { - this.search_choices = this.container.find('ul.chzn-choices').first(); + this.search_choices = this.container.find('ul.chosen-choices').first(); this.search_container = this.container.find('li.search-field').first(); } else { - this.search_container = this.container.find('div.chzn-search').first(); - this.selected_item = this.container.find('.chzn-single').first(); + this.search_container = this.container.find('div.chosen-search').first(); + this.selected_item = this.container.find('.chosen-single').first(); } this.results_build(); this.set_tab_index(); - this.set_label_behavior(); - return this.form_field_jq.trigger("liszt:ready", { + return this.set_label_behavior(); + }; + + Chosen.prototype.on_ready = function() { + return this.form_field_jq.trigger("chosen:ready", { chosen: this }); }; Chosen.prototype.register_observers = function() { - var _this = this; - this.container.mousedown(function(evt) { - _this.container_mousedown(evt); - }); - this.container.mouseup(function(evt) { - _this.container_mouseup(evt); - }); - this.container.mouseenter(function(evt) { - _this.mouse_enter(evt); - }); - this.container.mouseleave(function(evt) { - _this.mouse_leave(evt); - }); - this.search_results.mouseup(function(evt) { - _this.search_results_mouseup(evt); - }); - this.search_results.mouseover(function(evt) { - _this.search_results_mouseover(evt); - }); - this.search_results.mouseout(function(evt) { - _this.search_results_mouseout(evt); - }); - this.search_results.bind('mousewheel DOMMouseScroll', function(evt) { - _this.search_results_mousewheel(evt); - }); - this.form_field_jq.bind("liszt:updated", function(evt) { - _this.results_update_field(evt); - }); - this.form_field_jq.bind("liszt:activate", function(evt) { - _this.activate_field(evt); - }); - this.form_field_jq.bind("liszt:open", function(evt) { - _this.container_mousedown(evt); - }); - this.search_field.blur(function(evt) { - _this.input_blur(evt); - }); - this.search_field.keyup(function(evt) { - _this.keyup_checker(evt); - }); - this.search_field.keydown(function(evt) { - _this.keydown_checker(evt); - }); - this.search_field.focus(function(evt) { - _this.input_focus(evt); - }); + this.container.on('touchstart.chosen', (function(_this) { + return function(evt) { + _this.container_mousedown(evt); + }; + })(this)); + this.container.on('touchend.chosen', (function(_this) { + return function(evt) { + _this.container_mouseup(evt); + }; + })(this)); + this.container.on('mousedown.chosen', (function(_this) { + return function(evt) { + _this.container_mousedown(evt); + }; + })(this)); + this.container.on('mouseup.chosen', (function(_this) { + return function(evt) { + _this.container_mouseup(evt); + }; + })(this)); + this.container.on('mouseenter.chosen', (function(_this) { + return function(evt) { + _this.mouse_enter(evt); + }; + })(this)); + this.container.on('mouseleave.chosen', (function(_this) { + return function(evt) { + _this.mouse_leave(evt); + }; + })(this)); + this.search_results.on('mouseup.chosen', (function(_this) { + return function(evt) { + _this.search_results_mouseup(evt); + }; + })(this)); + this.search_results.on('mouseover.chosen', (function(_this) { + return function(evt) { + _this.search_results_mouseover(evt); + }; + })(this)); + this.search_results.on('mouseout.chosen', (function(_this) { + return function(evt) { + _this.search_results_mouseout(evt); + }; + })(this)); + this.search_results.on('mousewheel.chosen DOMMouseScroll.chosen', (function(_this) { + return function(evt) { + _this.search_results_mousewheel(evt); + }; + })(this)); + this.search_results.on('touchstart.chosen', (function(_this) { + return function(evt) { + _this.search_results_touchstart(evt); + }; + })(this)); + this.search_results.on('touchmove.chosen', (function(_this) { + return function(evt) { + _this.search_results_touchmove(evt); + }; + })(this)); + this.search_results.on('touchend.chosen', (function(_this) { + return function(evt) { + _this.search_results_touchend(evt); + }; + })(this)); + this.form_field_jq.on("chosen:updated.chosen", (function(_this) { + return function(evt) { + _this.results_update_field(evt); + }; + })(this)); + this.form_field_jq.on("chosen:activate.chosen", (function(_this) { + return function(evt) { + _this.activate_field(evt); + }; + })(this)); + this.form_field_jq.on("chosen:open.chosen", (function(_this) { + return function(evt) { + _this.container_mousedown(evt); + }; + })(this)); + this.form_field_jq.on("chosen:close.chosen", (function(_this) { + return function(evt) { + _this.close_field(evt); + }; + })(this)); + this.search_field.on('blur.chosen', (function(_this) { + return function(evt) { + _this.input_blur(evt); + }; + })(this)); + this.search_field.on('keyup.chosen', (function(_this) { + return function(evt) { + _this.keyup_checker(evt); + }; + })(this)); + this.search_field.on('keydown.chosen', (function(_this) { + return function(evt) { + _this.keydown_checker(evt); + }; + })(this)); + this.search_field.on('focus.chosen', (function(_this) { + return function(evt) { + _this.input_focus(evt); + }; + })(this)); + this.search_field.on('cut.chosen', (function(_this) { + return function(evt) { + _this.clipboard_event_checker(evt); + }; + })(this)); + this.search_field.on('paste.chosen', (function(_this) { + return function(evt) { + _this.clipboard_event_checker(evt); + }; + })(this)); if (this.is_multiple) { - return this.search_choices.click(function(evt) { - _this.choices_click(evt); - }); + return this.search_choices.on('click.chosen', (function(_this) { + return function(evt) { + _this.choices_click(evt); + }; + })(this)); } else { - return this.container.click(function(evt) { + return this.container.on('click.chosen', function(evt) { evt.preventDefault(); }); } }; + Chosen.prototype.destroy = function() { + $(this.container[0].ownerDocument).off('click.chosen', this.click_test_action); + if (this.form_field_label.length > 0) { + this.form_field_label.off('click.chosen'); + } + if (this.search_field[0].tabIndex) { + this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex; + } + this.container.remove(); + this.form_field_jq.removeData('chosen'); + return this.form_field_jq.show(); + }; + Chosen.prototype.search_field_disabled = function() { - this.is_disabled = this.form_field_jq[0].disabled; + this.is_disabled = this.form_field.disabled || this.form_field_jq.parents('fieldset').is(':disabled'); + this.container.toggleClass('chosen-disabled', this.is_disabled); + this.search_field[0].disabled = this.is_disabled; + if (!this.is_multiple) { + this.selected_item.off('focus.chosen', this.activate_field); + } if (this.is_disabled) { - this.container.addClass('chzn-disabled'); - this.search_field[0].disabled = true; - if (!this.is_multiple) { - this.selected_item.unbind("focus", this.activate_action); - } return this.close_field(); - } else { - this.container.removeClass('chzn-disabled'); - this.search_field[0].disabled = false; - if (!this.is_multiple) { - return this.selected_item.bind("focus", this.activate_action); - } + } else if (!this.is_multiple) { + return this.selected_item.on('focus.chosen', this.activate_field); } }; Chosen.prototype.container_mousedown = function(evt) { - if (!this.is_disabled) { - if (evt && evt.type === "mousedown" && !this.results_showing) { - evt.preventDefault(); - } - if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) { - if (!this.active_field) { - if (this.is_multiple) { - this.search_field.val(""); - } - $(document).click(this.click_test_action); - this.results_show(); - } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chzn-single").length)) { - evt.preventDefault(); - this.results_toggle(); + var ref; + if (this.is_disabled) { + return; + } + if (evt && ((ref = evt.type) === 'mousedown' || ref === 'touchstart') && !this.results_showing) { + evt.preventDefault(); + } + if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) { + if (!this.active_field) { + if (this.is_multiple) { + this.search_field.val(""); } - return this.activate_field(); + $(this.container[0].ownerDocument).on('click.chosen', this.click_test_action); + this.results_show(); + } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) { + evt.preventDefault(); + this.results_toggle(); } + return this.activate_field(); } }; @@ -520,8 +885,10 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.search_results_mousewheel = function(evt) { - var delta, _ref, _ref1; - delta = -((_ref = evt.originalEvent) != null ? _ref.wheelDelta : void 0) || ((_ref1 = evt.originialEvent) != null ? _ref1.detail : void 0); + var delta; + if (evt.originalEvent) { + delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail; + } if (delta != null) { evt.preventDefault(); if (evt.type === 'DOMMouseScroll') { @@ -532,31 +899,36 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.blur_test = function(evt) { - if (!this.active_field && this.container.hasClass("chzn-container-active")) { + if (!this.active_field && this.container.hasClass("chosen-container-active")) { return this.close_field(); } }; Chosen.prototype.close_field = function() { - $(document).unbind("click", this.click_test_action); + $(this.container[0].ownerDocument).off("click.chosen", this.click_test_action); this.active_field = false; this.results_hide(); - this.container.removeClass("chzn-container-active"); - this.winnow_results_clear(); + this.container.removeClass("chosen-container-active"); this.clear_backstroke(); this.show_search_field_default(); - return this.search_field_scale(); + this.search_field_scale(); + return this.search_field.blur(); }; Chosen.prototype.activate_field = function() { - this.container.addClass("chzn-container-active"); + if (this.is_disabled) { + return; + } + this.container.addClass("chosen-container-active"); this.active_field = true; this.search_field.val(this.search_field.val()); return this.search_field.focus(); }; Chosen.prototype.test_active_click = function(evt) { - if ($(evt.target).parents('#' + this.container_id).length) { + var active_container; + active_container = $(evt.target).closest('.chosen-container'); + if (active_container.length && this.container[0] === active_container[0]) { return this.active_field = true; } else { return this.close_field(); @@ -564,54 +936,30 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.results_build = function() { - var content, data, _i, _len, _ref; this.parsing = true; - this.results_data = root.SelectParser.select_to_array(this.form_field); - if (this.is_multiple && this.choices > 0) { + this.selected_option_count = null; + this.results_data = SelectParser.select_to_array(this.form_field); + if (this.is_multiple) { this.search_choices.find("li.search-choice").remove(); - this.choices = 0; } else if (!this.is_multiple) { - this.selected_item.addClass("chzn-default").find("span").text(this.default_text); + this.single_set_selected_text(); if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) { - this.container.addClass("chzn-container-single-nosearch"); + this.search_field[0].readOnly = true; + this.container.addClass("chosen-container-single-nosearch"); } else { - this.container.removeClass("chzn-container-single-nosearch"); - } - } - content = ''; - _ref = this.results_data; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - data = _ref[_i]; - if (data.group) { - content += this.result_add_group(data); - } else if (!data.empty) { - content += this.result_add_option(data); - if (data.selected && this.is_multiple) { - this.choice_build(data); - } else if (data.selected && !this.is_multiple) { - this.selected_item.removeClass("chzn-default").find("span").text(data.text); - if (this.allow_single_deselect) { - this.single_deselect_control_build(); - } - } + this.search_field[0].readOnly = false; + this.container.removeClass("chosen-container-single-nosearch"); } } + this.update_results_content(this.results_option_build({ + first: true + })); this.search_field_disabled(); this.show_search_field_default(); this.search_field_scale(); - this.search_results.html(content); return this.parsing = false; }; - Chosen.prototype.result_add_group = function(group) { - if (!group.disabled) { - group.dom_id = this.container_id + "_g_" + group.array_index; - return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>'; - } else { - return ""; - } - }; - Chosen.prototype.result_do_highlight = function(el) { var high_bottom, high_top, maxHeight, visible_bottom, visible_top; if (el.length) { @@ -639,61 +987,58 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.results_show = function() { - if (this.result_single_selected != null) { - this.result_do_highlight(this.result_single_selected); - } else if (this.is_multiple && this.max_selected_options <= this.choices) { - this.form_field_jq.trigger("liszt:maxselected", { + if (this.is_multiple && this.max_selected_options <= this.choices_count()) { + this.form_field_jq.trigger("chosen:maxselected", { chosen: this }); return false; } - this.container.addClass("chzn-with-drop"); - this.form_field_jq.trigger("liszt:showing_dropdown", { - chosen: this - }); + this.container.addClass("chosen-with-drop"); this.results_showing = true; this.search_field.focus(); - this.search_field.val(this.search_field.val()); - return this.winnow_results(); + this.search_field.val(this.get_search_field_value()); + this.winnow_results(); + return this.form_field_jq.trigger("chosen:showing_dropdown", { + chosen: this + }); + }; + + Chosen.prototype.update_results_content = function(content) { + return this.search_results.html(content); }; Chosen.prototype.results_hide = function() { - this.result_clear_highlight(); - this.container.removeClass("chzn-with-drop"); - this.form_field_jq.trigger("liszt:hiding_dropdown", { - chosen: this - }); + if (this.results_showing) { + this.result_clear_highlight(); + this.container.removeClass("chosen-with-drop"); + this.form_field_jq.trigger("chosen:hiding_dropdown", { + chosen: this + }); + } return this.results_showing = false; }; Chosen.prototype.set_tab_index = function(el) { var ti; - if (this.form_field_jq.attr("tabindex")) { - ti = this.form_field_jq.attr("tabindex"); - this.form_field_jq.attr("tabindex", -1); - return this.search_field.attr("tabindex", ti); + if (this.form_field.tabIndex) { + ti = this.form_field.tabIndex; + this.form_field.tabIndex = -1; + return this.search_field[0].tabIndex = ti; } }; Chosen.prototype.set_label_behavior = function() { - var _this = this; this.form_field_label = this.form_field_jq.parents("label"); if (!this.form_field_label.length && this.form_field.id.length) { - this.form_field_label = $("label[for=" + this.form_field.id + "]"); + this.form_field_label = $("label[for='" + this.form_field.id + "']"); } if (this.form_field_label.length > 0) { - return this.form_field_label.click(function(evt) { - if (_this.is_multiple) { - return _this.container_mousedown(evt); - } else { - return _this.activate_field(); - } - }); + return this.form_field_label.on('click.chosen', this.label_click_handler); } }; Chosen.prototype.show_search_field_default = function() { - if (this.is_multiple && this.choices < 1 && !this.active_field) { + if (this.is_multiple && this.choices_count() < 1 && !this.active_field) { this.search_field.val(this.default_text); return this.search_field.addClass("default"); } else { @@ -721,32 +1066,31 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.search_results_mouseout = function(evt) { - if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) { + if ($(evt.target).hasClass("active-result") || $(evt.target).parents('.active-result').first()) { return this.result_clear_highlight(); } }; Chosen.prototype.choice_build = function(item) { - var choice_id, html, link, - _this = this; - if (this.is_multiple && this.max_selected_options <= this.choices) { - this.form_field_jq.trigger("liszt:maxselected", { - chosen: this - }); - return false; - } - choice_id = this.container_id + "_c_" + item.array_index; - this.choices += 1; + var choice, close_link; + choice = $('<li />', { + "class": "search-choice" + }).html("<span>" + (this.choice_label(item)) + "</span>"); if (item.disabled) { - html = '<li class="search-choice search-choice-disabled" id="' + choice_id + '"><span>' + item.html + '</span></li>'; + choice.addClass('search-choice-disabled'); } else { - html = '<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>'; + close_link = $('<a />', { + "class": 'search-choice-close', + 'data-option-array-index': item.array_index + }); + close_link.on('click.chosen', (function(_this) { + return function(evt) { + return _this.choice_destroy_link_click(evt); + }; + })(this)); + choice.append(close_link); } - this.search_container.before(html); - link = $('#' + choice_id).find("a").first(); - return link.click(function(evt) { - return _this.choice_destroy_link_click(evt); - }); + return this.search_container.before(choice); }; Chosen.prototype.choice_destroy_link_click = function(evt) { @@ -758,10 +1102,13 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.choice_destroy = function(link) { - if (this.result_deselect(link.attr("rel"))) { - this.choices -= 1; - this.show_search_field_default(); - if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) { + if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) { + if (this.active_field) { + this.search_field.focus(); + } else { + this.show_search_field_default(); + } + if (this.is_multiple && this.choices_count() > 0 && this.get_search_field_value().length < 1) { this.results_hide(); } link.parents('li').first().remove(); @@ -770,14 +1117,12 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.results_reset = function() { + this.reset_single_select_options(); this.form_field.options[0].selected = true; - this.selected_item.find("span").text(this.default_text); - if (!this.is_multiple) { - this.selected_item.addClass("chzn-default"); - } + this.single_set_selected_text(); this.show_search_field_default(); this.results_reset_cleanup(); - this.form_field_jq.trigger("change"); + this.trigger_form_field_change(); if (this.active_field) { return this.results_hide(); } @@ -789,64 +1134,74 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.result_select = function(evt) { - var high, high_id, item, position; + var high, item; if (this.result_highlight) { high = this.result_highlight; - high_id = high.attr("id"); this.result_clear_highlight(); + if (this.is_multiple && this.max_selected_options <= this.choices_count()) { + this.form_field_jq.trigger("chosen:maxselected", { + chosen: this + }); + return false; + } if (this.is_multiple) { - this.result_deactivate(high); + high.removeClass("active-result"); } else { - this.search_results.find(".result-selected").removeClass("result-selected"); - this.result_single_selected = high; - this.selected_item.removeClass("chzn-default"); + this.reset_single_select_options(); } high.addClass("result-selected"); - position = high_id.substr(high_id.lastIndexOf("_") + 1); - item = this.results_data[position]; + item = this.results_data[high[0].getAttribute("data-option-array-index")]; item.selected = true; this.form_field.options[item.options_index].selected = true; + this.selected_option_count = null; + this.search_field.val(""); if (this.is_multiple) { this.choice_build(item); } else { - this.selected_item.find("span").first().text(item.text); - if (this.allow_single_deselect) { - this.single_deselect_control_build(); - } + this.single_set_selected_text(this.choice_label(item)); } - if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) { + if (this.is_multiple && (!this.hide_results_on_select || (evt.metaKey || evt.ctrlKey))) { + this.winnow_results(); + } else { this.results_hide(); + this.show_search_field_default(); } - this.search_field.val(""); if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) { - this.form_field_jq.trigger("change", { - 'selected': this.form_field.options[item.options_index].value + this.trigger_form_field_change({ + selected: this.form_field.options[item.options_index].value }); } this.current_selectedIndex = this.form_field.selectedIndex; + evt.preventDefault(); return this.search_field_scale(); } }; - Chosen.prototype.result_activate = function(el) { - return el.addClass("active-result"); - }; - - Chosen.prototype.result_deactivate = function(el) { - return el.removeClass("active-result"); + Chosen.prototype.single_set_selected_text = function(text) { + if (text == null) { + text = this.default_text; + } + if (text === this.default_text) { + this.selected_item.addClass("chosen-default"); + } else { + this.single_deselect_control_build(); + this.selected_item.removeClass("chosen-default"); + } + return this.selected_item.find("span").html(text); }; Chosen.prototype.result_deselect = function(pos) { - var result, result_data; + var result_data; result_data = this.results_data[pos]; if (!this.form_field.options[result_data.options_index].disabled) { result_data.selected = false; this.form_field.options[result_data.options_index].selected = false; - result = $("#" + this.container_id + "_o_" + pos); - result.removeClass("result-selected").addClass("active-result").show(); + this.selected_option_count = null; this.result_clear_highlight(); - this.winnow_results(); - this.form_field_jq.trigger("change", { + if (this.results_showing) { + this.winnow_results(); + } + this.trigger_form_field_change({ deselected: this.form_field.options[result_data.options_index].value }); this.search_field_scale(); @@ -857,108 +1212,43 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.single_deselect_control_build = function() { - if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) { - return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>"); + if (!this.allow_single_deselect) { + return; + } + if (!this.selected_item.find("abbr").length) { + this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>"); } + return this.selected_item.addClass("chosen-single-with-deselect"); }; - Chosen.prototype.winnow_results = function() { - var found, option, part, parts, regex, regexAnchor, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len1, _ref; - this.no_results_clear(); - results = 0; - searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html(); - regexAnchor = this.search_contains ? "" : "^"; - regex = new RegExp(regexAnchor + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); - zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); - _ref = this.results_data; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - option = _ref[_i]; - if (!option.disabled && !option.empty) { - if (option.group) { - $('#' + option.dom_id).css('display', 'none'); - } else if (!(this.is_multiple && option.selected)) { - found = false; - result_id = option.dom_id; - result = $("#" + result_id); - if (regex.test(option.html)) { - found = true; - results += 1; - } else if (this.enable_split_word_search && (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0)) { - parts = option.html.replace(/\[|\]/g, "").split(" "); - if (parts.length) { - for (_j = 0, _len1 = parts.length; _j < _len1; _j++) { - part = parts[_j]; - if (regex.test(part)) { - found = true; - results += 1; - } - } - } - } - if (found) { - if (searchText.length) { - startpos = option.html.search(zregex); - text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length); - text = text.substr(0, startpos) + '<em>' + text.substr(startpos); - } else { - text = option.html; - } - result.html(text); - this.result_activate(result); - if (option.group_array_index != null) { - $("#" + this.results_data[option.group_array_index].dom_id).css('display', 'list-item'); - } - } else { - if (this.result_highlight && result_id === this.result_highlight.attr('id')) { - this.result_clear_highlight(); - } - this.result_deactivate(result); - } - } - } - } - if (results < 1 && searchText.length) { - return this.no_results(searchText); - } else { - return this.winnow_results_set_highlight(); - } + Chosen.prototype.get_search_field_value = function() { + return this.search_field.val(); }; - Chosen.prototype.winnow_results_clear = function() { - var li, lis, _i, _len, _results; - this.search_field.val(""); - lis = this.search_results.find("li"); - _results = []; - for (_i = 0, _len = lis.length; _i < _len; _i++) { - li = lis[_i]; - li = $(li); - if (li.hasClass("group-result")) { - _results.push(li.css('display', 'auto')); - } else if (!this.is_multiple || !li.hasClass("result-selected")) { - _results.push(this.result_activate(li)); - } else { - _results.push(void 0); - } - } - return _results; + Chosen.prototype.get_search_text = function() { + return $.trim(this.get_search_field_value()); + }; + + Chosen.prototype.escape_html = function(text) { + return $('<div/>').text(text).html(); }; Chosen.prototype.winnow_results_set_highlight = function() { var do_high, selected_results; - if (!this.result_highlight) { - selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : []; - do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first(); - if (do_high != null) { - return this.result_do_highlight(do_high); - } + selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : []; + do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first(); + if (do_high != null) { + return this.result_do_highlight(do_high); } }; Chosen.prototype.no_results = function(terms) { var no_results_html; - no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>'); - no_results_html.find("span").first().html(terms); - return this.search_results.append(no_results_html); + no_results_html = this.get_no_results_html(terms); + this.search_results.append(no_results_html); + return this.form_field_jq.trigger("chosen:no_results", { + chosen: this + }); }; Chosen.prototype.no_results_clear = function() { @@ -966,19 +1256,13 @@ Copyright (c) 2011 by Harvest }; Chosen.prototype.keydown_arrow = function() { - var first_active, next_sib; - if (!this.result_highlight) { - first_active = this.search_results.find("li.active-result").first(); - if (first_active) { - this.result_do_highlight($(first_active)); - } - } else if (this.results_showing) { + var next_sib; + if (this.results_showing && this.result_highlight) { next_sib = this.result_highlight.nextAll("li.active-result").first(); if (next_sib) { - this.result_do_highlight(next_sib); + return this.result_do_highlight(next_sib); } - } - if (!this.results_showing) { + } else { return this.results_show(); } }; @@ -992,7 +1276,7 @@ Copyright (c) 2011 by Harvest if (prev_sibs.length) { return this.result_do_highlight(prev_sibs.first()); } else { - if (this.choices > 0) { + if (this.choices_count() > 0) { this.results_hide(); } return this.result_clear_highlight(); @@ -1025,79 +1309,41 @@ Copyright (c) 2011 by Harvest return this.pending_backstroke = null; }; - Chosen.prototype.keydown_checker = function(evt) { - var stroke, _ref; - stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; - this.search_field_scale(); - if (stroke !== 8 && this.pending_backstroke) { - this.clear_backstroke(); + Chosen.prototype.search_field_scale = function() { + var div, i, len, style, style_block, styles, width; + if (!this.is_multiple) { + return; } - switch (stroke) { - case 8: - this.backstroke_length = this.search_field.val().length; - break; - case 9: - if (this.results_showing && !this.is_multiple) { - this.result_select(evt); - } - this.mouse_on_container = false; - break; - case 13: - evt.preventDefault(); - break; - case 38: - evt.preventDefault(); - this.keyup_arrow(); - break; - case 40: - this.keydown_arrow(); - break; + style_block = { + position: 'absolute', + left: '-1000px', + top: '-1000px', + display: 'none', + whiteSpace: 'pre' + }; + styles = ['fontSize', 'fontStyle', 'fontWeight', 'fontFamily', 'lineHeight', 'textTransform', 'letterSpacing']; + for (i = 0, len = styles.length; i < len; i++) { + style = styles[i]; + style_block[style] = this.search_field.css(style); } - }; - - Chosen.prototype.search_field_scale = function() { - var div, h, style, style_block, styles, w, _i, _len; - if (this.is_multiple) { - h = 0; - w = 0; - style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"; - styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing']; - for (_i = 0, _len = styles.length; _i < _len; _i++) { - style = styles[_i]; - style_block += style + ":" + this.search_field.css(style) + ";"; - } - div = $('<div />', { - 'style': style_block - }); - div.text(this.search_field.val()); - $('body').append(div); - w = div.width() + 25; - div.remove(); - if (!this.f_width) { - this.f_width = this.container.outerWidth(); - } - if (w > this.f_width - 10) { - w = this.f_width - 10; - } - return this.search_field.css({ - 'width': w + 'px' - }); + div = $('<div />').css(style_block); + div.text(this.get_search_field_value()); + $('body').append(div); + width = div.width() + 25; + div.remove(); + if (this.container.is(':visible')) { + width = Math.min(this.container.outerWidth() - 10, width); } + return this.search_field.width(width); }; - Chosen.prototype.generate_random_id = function() { - var string; - string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char(); - while ($("#" + string).length > 0) { - string += this.generate_random_char(); - } - return string; + Chosen.prototype.trigger_form_field_change = function(extra) { + this.form_field_jq.trigger("input", extra); + return this.form_field_jq.trigger("change", extra); }; return Chosen; })(AbstractChosen); - root.Chosen = Chosen; - }).call(this); diff --git a/resources/lib/oojs-ui/oojs-ui-core.js b/resources/lib/oojs-ui/oojs-ui-core.js index e0d165f9b1..b3a901200f 100644 --- a/resources/lib/oojs-ui/oojs-ui-core.js +++ b/resources/lib/oojs-ui/oojs-ui-core.js @@ -4832,7 +4832,7 @@ OO.ui.mixin.ClippableElement.prototype.setIdealSize = function ( width, height ) * @return {string} 'left' or 'right' */ OO.ui.mixin.ClippableElement.prototype.getHorizontalAnchorEdge = function () { - if ( this.computePosition && this.computePosition().right !== '' ) { + if ( this.computePosition && this.positioning && this.computePosition().right !== '' ) { return 'right'; } return 'left'; @@ -4854,7 +4854,7 @@ OO.ui.mixin.ClippableElement.prototype.getHorizontalAnchorEdge = function () { * @return {string} 'top' or 'bottom' */ OO.ui.mixin.ClippableElement.prototype.getVerticalAnchorEdge = function () { - if ( this.computePosition && this.computePosition().bottom !== '' ) { + if ( this.computePosition && this.positioning && this.computePosition().bottom !== '' ) { return 'bottom'; } return 'top'; diff --git a/resources/src/jquery/jquery.tablesorter.less b/resources/src/jquery/jquery.tablesorter.less index 11f472ec01..ce24b0de65 100644 --- a/resources/src/jquery/jquery.tablesorter.less +++ b/resources/src/jquery/jquery.tablesorter.less @@ -2,18 +2,20 @@ /* Table Sorting */ -table.jquery-tablesorter th.headerSort { - .background-image-svg( 'images/sort_both.svg', 'images/sort_both.png' ); - cursor: pointer; - background-repeat: no-repeat; - background-position: center right; - padding-right: 21px; -} +table.jquery-tablesorter { + th.headerSort { + .background-image-svg( 'images/sort_both.svg', 'images/sort_both.png' ); + cursor: pointer; + background-repeat: no-repeat; + background-position: center right; + padding-right: 21px; + } -table.jquery-tablesorter th.headerSortUp { - .background-image-svg( 'images/sort_up.svg', 'images/sort_up.png' ); -} + th.headerSortUp { + .background-image-svg( 'images/sort_up.svg', 'images/sort_up.png' ); + } -table.jquery-tablesorter th.headerSortDown { - .background-image-svg( 'images/sort_down.svg', 'images/sort_down.png' ); + th.headerSortDown { + .background-image-svg( 'images/sort_down.svg', 'images/sort_down.png' ); + } } diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.styles.less b/resources/src/mediawiki.action/mediawiki.action.edit.styles.less index 1d578e47cc..e4443f6384 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.styles.less +++ b/resources/src/mediawiki.action/mediawiki.action.edit.styles.less @@ -57,4 +57,25 @@ .editOptions { border-radius: 0 0 2px 2px; } + + .editButtons .oo-ui-buttonInputWidget, + .cancelLink, + .editHelp { + margin-top: 0.5em; + } + + .cancelLink, + .editHelp { + display: inline-block; + vertical-align: middle; + } + + // FIXME: Remove CSS magic together with DOM element in T183672 + .mw-editButtons-pipe-separator { + display: inline-block; + padding-top: 0.625em; + padding-bottom: 0.546875em; + line-height: 1.172em; + vertical-align: middle; + } } diff --git a/resources/src/mediawiki.language/specialcharacters.json b/resources/src/mediawiki.language/specialcharacters.json index f0a86ffc5c..1c9294c564 100644 --- a/resources/src/mediawiki.language/specialcharacters.json +++ b/resources/src/mediawiki.language/specialcharacters.json @@ -349,6 +349,10 @@ "◌ֿ", "Ö¿" ], + [ + "◌ֽ", + "Ö½" + ], [ "◌׀", "׀" diff --git a/resources/src/mediawiki.legacy/shared.css b/resources/src/mediawiki.legacy/shared.css index 9f48204bb6..19b51eb21b 100644 --- a/resources/src/mediawiki.legacy/shared.css +++ b/resources/src/mediawiki.legacy/shared.css @@ -760,11 +760,6 @@ table.floatleft { vertical-align: baseline; /* Reset line-height; headings tend to have it set to larger values */ line-height: 1em; - /* As .mw-editsection is a <span> (inline element), it is treated as part */ - /* of the heading content when selecting text by multiple clicks and thus */ - /* selected together with heading content, despite the user-select: none; */ - /* rule set above. This enforces non-selection without changing the look. */ - display: inline-block; } /* Correct directionality when page dir is different from site/user dir */ diff --git a/resources/src/mediawiki.legacy/wikibits.js b/resources/src/mediawiki.legacy/wikibits.js index f5bdfd8058..27d049eb3a 100644 --- a/resources/src/mediawiki.legacy/wikibits.js +++ b/resources/src/mediawiki.legacy/wikibits.js @@ -49,7 +49,7 @@ loadedScripts[ url ] = true; s = document.createElement( 'script' ); s.setAttribute( 'src', url ); - document.getElementsByTagName( 'head' )[ 0 ].appendChild( s ); + document.head.appendChild( s ); return s; } @@ -72,7 +72,7 @@ if ( media ) { l.media = media; } - document.getElementsByTagName( 'head' )[ 0 ].appendChild( l ); + document.head.appendChild( l ); return l; } diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js index 15fe334261..96b44100ea 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js @@ -4,17 +4,19 @@ * * @mixins OO.EventEmitter * + * @param {jQuery} $initialFieldset The initial server-generated legacy form content * @constructor */ - mw.rcfilters.dm.ChangesListViewModel = function MwRcfiltersDmChangesListViewModel() { + mw.rcfilters.dm.ChangesListViewModel = function MwRcfiltersDmChangesListViewModel( $initialFieldset ) { // Mixin constructor OO.EventEmitter.call( this ); this.valid = true; this.newChangesExist = false; - this.nextFrom = null; this.liveUpdate = false; this.unseenWatchedChanges = false; + + this.extractNextFrom( $initialFieldset ); }; /* Initialization */ @@ -74,7 +76,6 @@ * @param {jQuery|string} changesListContent * @param {jQuery} $fieldset * @param {string} noResultsDetails Type of no result error - * timeout. * @param {boolean} [isInitialDOM] Using the initial (already attached) DOM elements * @param {boolean} [separateOldAndNew] Whether a logical separation between old and new changes is needed * @fires update @@ -114,7 +115,9 @@ */ mw.rcfilters.dm.ChangesListViewModel.prototype.extractNextFrom = function ( $fieldset ) { var data = $fieldset.find( '.rclistfrom > a, .wlinfo' ).data( 'params' ); - this.nextFrom = data ? data.from : null; + if ( data && data.from ) { + this.nextFrom = data.from; + } }; /** diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js index f4cdae3a6c..bb29b36191 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js @@ -34,6 +34,7 @@ * @cfg {string} [whatsThis.body] The body of the whatsThis popup message * @cfg {string} [whatsThis.url] The url for the link in the whatsThis popup message * @cfg {string} [whatsThis.linkMessage] The text for the link in the whatsThis popup message + * @cfg {boolean} [visible=true] The visibility of the group */ mw.rcfilters.dm.FilterGroup = function MwRcfiltersDmFilterGroup( name, config ) { config = config || {}; @@ -52,6 +53,7 @@ this.numericRange = config.range; this.separator = config.separator || '|'; this.labelPrefixKey = config.labelPrefixKey; + this.visible = config.visible === undefined ? true : !!config.visible; this.currSelected = null; this.active = !!config.active; @@ -944,4 +946,38 @@ return value; }; + + /** + * Toggle the visibility of this group + * + * @param {boolean} [isVisible] Item is visible + */ + mw.rcfilters.dm.FilterGroup.prototype.toggleVisible = function ( isVisible ) { + isVisible = isVisible === undefined ? !this.visible : isVisible; + + if ( this.visible !== isVisible ) { + this.visible = isVisible; + this.emit( 'update' ); + } + }; + + /** + * Check whether the group is visible + * + * @return {boolean} Group is visible + */ + mw.rcfilters.dm.FilterGroup.prototype.isVisible = function () { + return this.visible; + }; + + /** + * Set the visibility of the items under this group by the given items array + * + * @param {mw.rcfilters.dm.ItemModel[]} visibleItems An array of visible items + */ + mw.rcfilters.dm.FilterGroup.prototype.setVisibleItems = function ( visibleItems ) { + this.getItems().forEach( function ( itemModel ) { + itemModel.toggleVisible( visibleItems.indexOf( itemModel ) !== -1 ); + } ); + }; }( mediaWiki ) ); diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js index 4e2079dc40..682a937679 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js @@ -12,6 +12,7 @@ * selected, makes inactive. * @cfg {string[]} [subset] Defining the names of filters that are a subset of this filter * @cfg {Object} [conflicts] Defines the conflicts for this filter + * @cfg {boolean} [visible=true] The visibility of the group */ mw.rcfilters.dm.FilterItem = function MwRcfiltersDmFilterItem( param, groupModel, config ) { config = config || {}; @@ -29,6 +30,7 @@ this.subset = config.subset || []; this.conflicts = config.conflicts || {}; this.superset = []; + this.visible = config.visible === undefined ? true : !!config.visible; // Interaction states this.included = false; @@ -369,4 +371,28 @@ this.emit( 'update' ); } }; + + /** + * Toggle the visibility of this item + * + * @param {boolean} [isVisible] Item is visible + */ + mw.rcfilters.dm.FilterItem.prototype.toggleVisible = function ( isVisible ) { + isVisible = isVisible === undefined ? !this.visible : !!isVisible; + + if ( this.visible !== isVisible ) { + this.visible = isVisible; + this.emit( 'update' ); + } + }; + + /** + * Check whether the item is visible + * + * @return {boolean} Item is visible + */ + mw.rcfilters.dm.FilterItem.prototype.isVisible = function () { + return this.visible; + }; + }( mediaWiki ) ); diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js index 8d22c23e1a..bbc1d7e629 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js @@ -20,6 +20,7 @@ this.views = {}; this.currentView = 'default'; + this.searchQuery = null; // Events this.aggregate( { update: 'filterItemUpdate' } ); @@ -401,7 +402,7 @@ } } ); - this.currentView = 'default'; + this.setSearch( '' ); this.updateHighlightedState(); @@ -710,7 +711,7 @@ $.each( this.groups, function ( name, model ) { if ( model.isSticky() ) { - $.extend( true, result, model.getDefaultParams() ); + $.extend( true, result, model.getParamRepresentation() ); } } ); @@ -1095,19 +1096,6 @@ return allSelected; }; - /** - * Switch the current view - * - * @param {string} view View name - * @fires update - */ - mw.rcfilters.dm.FiltersViewModel.prototype.switchView = function ( view ) { - if ( this.views[ view ] && this.currentView !== view ) { - this.currentView = view; - this.emit( 'update' ); - } - }; - /** * Get the current view * @@ -1147,6 +1135,82 @@ return result; }; + /** + * Return a version of the given string that is without any + * view triggers. + * + * @param {string} str Given string + * @return {string} Result + */ + mw.rcfilters.dm.FiltersViewModel.prototype.removeViewTriggers = function ( str ) { + if ( this.getViewFromString( str ) !== 'default' ) { + str = str.substr( 1 ); + } + + return str; + }; + + /** + * Get the view from the given string by a trigger, if it exists + * + * @param {string} str Given string + * @return {string} View name + */ + mw.rcfilters.dm.FiltersViewModel.prototype.getViewFromString = function ( str ) { + return this.getViewByTrigger( str.substr( 0, 1 ) ); + }; + + /** + * Set the current search for the system. + * This also dictates what items and groups are visible according + * to the search in #findMatches + * + * @param {string} searchQuery Search query, including triggers + * @fires searchChange + */ + mw.rcfilters.dm.FiltersViewModel.prototype.setSearch = function ( searchQuery ) { + var visibleGroups, visibleGroupNames; + + if ( this.searchQuery !== searchQuery ) { + // Check if the view changed + this.switchView( this.getViewFromString( searchQuery ) ); + + visibleGroups = this.findMatches( searchQuery ); + visibleGroupNames = Object.keys( visibleGroups ); + + // Update visibility of items and groups + $.each( this.getFilterGroups(), function ( groupName, groupModel ) { + // Check if the group is visible at all + groupModel.toggleVisible( visibleGroupNames.indexOf( groupName ) !== -1 ); + groupModel.setVisibleItems( visibleGroups[ groupName ] || [] ); + } ); + + this.searchQuery = searchQuery; + this.emit( 'searchChange', this.searchQuery ); + } + }; + + /** + * Get the current search + * + * @return {string} Current search query + */ + mw.rcfilters.dm.FiltersViewModel.prototype.getSearch = function () { + return this.searchQuery; + }; + + /** + * Switch the current view + * + * @private + * @param {string} view View name + */ + mw.rcfilters.dm.FiltersViewModel.prototype.switchView = function ( view ) { + if ( this.views[ view ] && this.currentView !== view ) { + this.currentView = view; + } + }; + /** * Toggle the highlight feature on and off. * Propagate the change to filter items. @@ -1209,18 +1273,4 @@ this.getItemByName( filterName ).clearHighlightColor(); }; - /** - * Return a version of the given string that is without any - * view triggers. - * - * @param {string} str Given string - * @return {string} Result - */ - mw.rcfilters.dm.FiltersViewModel.prototype.removeViewTriggers = function ( str ) { - if ( this.getViewByTrigger( str.substr( 0, 1 ) ) !== 'default' ) { - str = str.substr( 1 ); - } - - return str; - }; }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js index 7b5e11528c..cec570c627 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js @@ -281,6 +281,7 @@ * Extracts information from the changes list DOM * * @param {jQuery} $root Root DOM to find children from + * @param {boolean} [statusCode] Server response status code * @return {Object} Information about changes list * @return {Object|string} return.changes Changes list, or 'NO_RESULTS' if there are no results * (either normally or as an error) @@ -288,10 +289,21 @@ * 'NO_RESULTS_TIMEOUT' for no results due to a timeout, or omitted for more than 0 results * @return {jQuery} return.fieldset Fieldset */ - mw.rcfilters.Controller.prototype._extractChangesListInfo = function ( $root ) { + mw.rcfilters.Controller.prototype._extractChangesListInfo = function ( $root, statusCode ) { var info, $changesListContents = $root.find( '.mw-changeslist' ).first().contents(), - areResults = !!$changesListContents.length; + areResults = !!$changesListContents.length, + checkForLogout = !areResults && statusCode === 200; + + // We check if user logged out on different tab/browser or the session has expired. + // 205 status code returned from the server, which indicates that we need to reload the page + // is not usable on WL page, because we get redirected to login page, which gives 200 OK + // status code (if everything else goes well). + // Bug: T177717 + if ( checkForLogout && !!$root.find( '#wpName1' ).length ) { + location.reload( false ); + return; + } info = { changes: $changesListContents.length ? $changesListContents : 'NO_RESULTS', @@ -393,15 +405,6 @@ } ); }; - /** - * Switch the view of the filters model - * - * @param {string} view Requested view - */ - mw.rcfilters.Controller.prototype.switchView = function ( view ) { - this.filtersModel.switchView( view ); - }; - /** * Reset to default filters */ @@ -611,13 +614,26 @@ } this._checkForNewChanges() - .then( function ( newChanges ) { + .then( function ( statusCode ) { + // no result is 204 with the 'peek' param + // logged out is 205 + var newChanges = statusCode === 200; + if ( !this._shouldCheckForNewChanges() ) { // by the time the response is received, // it may not be appropriate anymore return; } + // 205 is the status code returned from server when user's logged in/out + // status is not matching while fetching live update changes. + // This works only on Recent Changes page. For WL, look _extractChangesListInfo. + // Bug: T177717 + if ( statusCode === 205 ) { + location.reload( false ); + return; + } + if ( newChanges ) { if ( this.changesListModel.getLiveUpdate() ) { return this.updateChangesList( null, this.LIVE_UPDATE ); @@ -653,12 +669,12 @@ var params = { limit: 1, peek: 1, // bypasses ChangesList specific UI - from: this.changesListModel.getNextFrom() + from: this.changesListModel.getNextFrom(), + isAnon: mw.user.isAnon() }; return this._queryChangesList( 'liveUpdate', params ).then( function ( data ) { - // no result is 204 with the 'peek' param - return data.status === 200; + return data.status; } ); }; @@ -1041,7 +1057,7 @@ data ? data.content : '' ) ) ); - return this._extractChangesListInfo( $parsed ); + return this._extractChangesListInfo( $parsed, data.status ); }.bind( this ) ); }; @@ -1144,4 +1160,40 @@ this.updateChangesList( null, 'markSeen' ); }.bind( this ) ); }; + + /** + * Set the current search for the system. + * + * @param {string} searchQuery Search query, including triggers + */ + mw.rcfilters.Controller.prototype.setSearch = function ( searchQuery ) { + this.filtersModel.setSearch( searchQuery ); + }; + + /** + * Switch the view by changing the search query trigger + * without changing the search term + * + * @param {string} view View to change to + */ + mw.rcfilters.Controller.prototype.switchView = function ( view ) { + this.setSearch( + this.filtersModel.getViewTrigger( view ) + + this.filtersModel.removeViewTriggers( this.filtersModel.getSearch() ) + ); + }; + + /** + * Reset the search for a specific view. This means we null the search query + * and replace it with the relevant trigger for the requested view + * + * @param {string} [view='default'] View to change to + */ + mw.rcfilters.Controller.prototype.resetSearchForView = function ( view ) { + view = view || 'default'; + + this.setSearch( + this.filtersModel.getViewTrigger( view ) + ); + }; }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js index 3e1191f392..7bb0a222c4 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js @@ -100,7 +100,8 @@ */ mw.rcfilters.UriProcessor.prototype._normalizeTargetInUri = function ( uri ) { var parts, - re = /^((?:\/.+\/)?.+:.+)\/(.+)$/; // matches [namespace:]Title/Subpage + // matches [/wiki/]SpecialNS:RCL/[Namespace:]Title/Subpage/Subsubpage/etc + re = /^((?:\/.+?\/)?.*?:.*?)\/(.*)$/; // target in title param if ( uri.query.title ) { @@ -112,7 +113,7 @@ } // target in path - parts = uri.path.match( re ); + parts = mw.Uri.decode( uri.path ).match( re ); if ( parts ) { uri.path = parts[ 1 ]; uri.query.target = parts[ 2 ]; diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js index 40b8bd2bb3..fe8bcf419f 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js @@ -8,17 +8,17 @@ * @private */ init: function () { - var $topLinks, - topSection, - $watchlistDetails, - namespaces, + var $topSection, + mainWrapperWidget, conditionalViews = {}, + $initialFieldset = $( 'fieldset.cloptions' ), savedQueriesPreferenceName = mw.config.get( 'wgStructuredChangeFiltersSavedQueriesPreferenceName' ), daysPreferenceName = mw.config.get( 'wgStructuredChangeFiltersDaysPreferenceName' ), limitPreferenceName = mw.config.get( 'wgStructuredChangeFiltersLimitPreferenceName' ), filtersModel = new mw.rcfilters.dm.FiltersViewModel(), - changesListModel = new mw.rcfilters.dm.ChangesListViewModel(), + changesListModel = new mw.rcfilters.dm.ChangesListViewModel( $initialFieldset ), savedQueriesModel = new mw.rcfilters.dm.SavedQueriesModel( filtersModel ), + specialPage = mw.config.get( 'wgCanonicalSpecialPageName' ), controller = new mw.rcfilters.Controller( filtersModel, changesListModel, savedQueriesModel, { @@ -26,23 +26,17 @@ daysPreferenceName: daysPreferenceName, limitPreferenceName: limitPreferenceName } - ), - $overlay = $( '<div>' ) - .addClass( 'mw-rcfilters-ui-overlay' ), - filtersWidget = new mw.rcfilters.ui.FilterWrapperWidget( - controller, filtersModel, savedQueriesModel, changesListModel, { $overlay: $overlay } ), - savedLinksListWidget = new mw.rcfilters.ui.SavedLinksListWidget( - controller, savedQueriesModel, { $overlay: $overlay } - ), - specialPage = mw.config.get( 'wgCanonicalSpecialPageName' ), - $changesListRoot = $( [ - '.mw-changeslist', - '.mw-changeslist-empty', - '.mw-changeslist-timeout', - '.mw-changeslist-notargetpage' - ].join( ', ' ) ); - - if ( specialPage === 'Recentchangeslinked' ) { + ); + + // TODO: The changesListWrapperWidget should be able to initialize + // after the model is ready. + + if ( specialPage === 'Recentchanges' ) { + $topSection = $( '.mw-recentchanges-toplinks' ).detach(); + } else if ( specialPage === 'Watchlist' ) { + $( '#contentSub, form#mw-watchlist-resetbutton' ).remove(); + $topSection = $( '.watchlistDetails' ).detach().contents(); + } else if ( specialPage === 'Recentchangeslinked' ) { conditionalViews.recentChangesLinked = { groups: [ { @@ -50,7 +44,7 @@ type: 'any_value', title: '', hidden: true, - isSticky: false, + sticky: true, filters: [ { name: 'target', @@ -63,7 +57,7 @@ type: 'boolean', title: '', hidden: true, - isSticky: false, + sticky: true, filters: [ { name: 'showlinkedto', @@ -73,40 +67,40 @@ } ] }; - } - // TODO: The changesListWrapperWidget should be able to initialize - // after the model is ready. - - // eslint-disable-next-line no-new - new mw.rcfilters.ui.ChangesListWrapperWidget( - filtersModel, changesListModel, controller, $changesListRoot ); + mainWrapperWidget = new mw.rcfilters.ui.MainWrapperWidget( + controller, + filtersModel, + savedQueriesModel, + changesListModel, + { + $topSection: $topSection, + $filtersContainer: $( '.rcfilters-container' ), + $changesListContainer: $( [ + '.mw-changeslist', + '.mw-changeslist-empty', + '.mw-changeslist-timeout', + '.mw-changeslist-notargetpage' + ].join( ', ' ) ), + $formContainer: $initialFieldset + } + ); // Remove the -loading class that may have been added on the server side. // If we are in fact going to load a default saved query, this .initialize() // call will do that and add the -loading class right back. $( 'body' ).removeClass( 'mw-rcfilters-ui-loading' ); - // Remove Media namespace - namespaces = mw.config.get( 'wgFormattedNamespaces' ); - delete namespaces[ mw.config.get( 'wgNamespaceIds' ).media ]; - controller.initialize( mw.config.get( 'wgStructuredChangeFilters' ), - namespaces, + // All namespaces without Media namespace + rcfilters.getNamespaces( [ 'Media' ] ), mw.config.get( 'wgRCFiltersChangeTags' ), conditionalViews ); - // eslint-disable-next-line no-new - new mw.rcfilters.ui.FormWrapperWidget( - filtersModel, changesListModel, controller, $( 'fieldset.cloptions' ) ); - - $( '.rcfilters-container' ).append( filtersWidget.$element ); - $( 'body' ) - .append( $overlay ) - .addClass( 'mw-rcfilters-ui-initialized' ); + mainWrapperWidget.initFormWidget( specialPage ); $( 'a.mw-helplink' ).attr( 'href', @@ -115,33 +109,7 @@ controller.replaceUrl(); - if ( specialPage === 'Recentchanges' ) { - $topLinks = $( '.mw-recentchanges-toplinks' ).detach(); - - topSection = new mw.rcfilters.ui.RcTopSectionWidget( - savedLinksListWidget, $topLinks - ); - filtersWidget.setTopSection( topSection.$element ); - } // end Recentchanges - - if ( specialPage === 'Recentchangeslinked' ) { - topSection = new mw.rcfilters.ui.RclTopSectionWidget( - savedLinksListWidget, controller, - filtersModel.getGroup( 'toOrFrom' ).getItemByParamName( 'showlinkedto' ), - filtersModel.getGroup( 'page' ).getItemByParamName( 'target' ) - ); - filtersWidget.setTopSection( topSection.$element ); - } // end Recentchangeslinked - - if ( specialPage === 'Watchlist' ) { - $( '#contentSub, form#mw-watchlist-resetbutton' ).detach(); - $watchlistDetails = $( '.watchlistDetails' ).detach().contents(); - - topSection = new mw.rcfilters.ui.WatchlistTopSectionWidget( - controller, changesListModel, savedLinksListWidget, $watchlistDetails - ); - filtersWidget.setTopSection( topSection.$element ); - } // end Watchlist + mainWrapperWidget.setTopSection( specialPage ); /** * Fired when initialization of the filtering interface for changes list is complete. @@ -150,6 +118,29 @@ * @member mw.hook */ mw.hook( 'structuredChangeFilters.ui.initialized' ).fire(); + }, + + /** + * Get list of namespaces and remove unused ones + * + * @member mw.rcfilters + * @private + * + * @param {Array} unusedNamespaces Names of namespaces to remove + * @return {Array} Filtered array of namespaces + */ + getNamespaces: function ( unusedNamespaces ) { + var i, length, name, id, + namespaceIds = mw.config.get( 'wgNamespaceIds' ), + namespaces = mw.config.get( 'wgFormattedNamespaces' ); + + for ( i = 0, length = unusedNamespaces.length; i < length; i++ ) { + name = unusedNamespaces[ i ]; + id = namespaceIds[ name.toLowerCase() ]; + delete namespaces[ id ]; + } + + return namespaces; } }; diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less index ec311dfcf1..c379e2702a 100644 --- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less @@ -12,6 +12,7 @@ } .mw-rcfilters-ui-changesListWrapperWidget { + position: relative; &-newChanges { min-height: 34px; @@ -67,6 +68,22 @@ .mw-changeslist-legend { background-color: @background-color-base; border: 1px solid @colorGray12; + + &:not( .mw-enhanced ) { + // We want to keep the legend accessible when results are overlaid + position: relative; + } + } + + .mw-changeslist-overlay { + position: absolute; + display: none; + width: 100%; + height: 100%; + } + + &--overlaid > .mw-changeslist-overlay { + display: block; } // Correction for Enhanced RC diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.MenuSelectWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.MenuSelectWidget.less index 7dd78e78b9..0906d6811b 100644 --- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.MenuSelectWidget.less +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.MenuSelectWidget.less @@ -10,13 +10,8 @@ } &-noresults { - display: none; padding: 0.5em; color: @colorGray5; - - .oo-ui-menuSelectWidget-invisible & { - display: inline-block; - } } &-body { diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.vector.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.vector.less new file mode 100644 index 0000000000..528707bed1 --- /dev/null +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.vector.less @@ -0,0 +1,4 @@ +// Fix z-index for the overlay in Vector, see T183442 +.mw-rcfilters-ui-overlay { + z-index: 101; +} diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclTargetPageWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclTargetPageWidget.less new file mode 100644 index 0000000000..2d92e27be1 --- /dev/null +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclTargetPageWidget.less @@ -0,0 +1,3 @@ +.mw-rcfilters-ui-rclTargetPageWidget { + min-width: 400px; +} diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclToOrFromWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclToOrFromWidget.less index af01f6879b..d63f35b7c0 100644 --- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclToOrFromWidget.less +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.RclToOrFromWidget.less @@ -1,6 +1,4 @@ .mw-rcfilters-ui-rclToOrFromWidget { - min-width: 340px; - // need to be very specific to override bg-color &.oo-ui-dropdownWidget.oo-ui-widget-enabled { .oo-ui-dropdownWidget-handle { diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js index cd0b8ae2fc..e2092dc6df 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js @@ -180,6 +180,8 @@ } + this.$element.prepend( $( '<div>' ).addClass( 'mw-changeslist-overlay' ) ); + loaderPromise.done( function () { if ( !isInitialDOM && !isEmpty ) { // Make sure enhanced RC re-initializes correctly @@ -190,6 +192,14 @@ } ); }; + /** Toggles overlay class on changes list + * + * @param {boolean} isVisible True if overlay should be visible + */ + mw.rcfilters.ui.ChangesListWrapperWidget.prototype.toggleOverlay = function ( isVisible ) { + this.$element.toggleClass( 'mw-rcfilters-ui-changesListWrapperWidget--overlaid', isVisible ); + }; + /** * Map a reason for having no results to its message key * @@ -206,6 +216,7 @@ }; return reasonMsgKeyMap[ reason ]; }; + /** * Emphasize the elements (or groups) newer than the 'from' parameter * @param {string} from Anything newer than this is considered 'new' diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js index dceb132038..c047e83433 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js @@ -68,9 +68,10 @@ .connect( this, { click: 'onInvertNamespacesButtonClick' } ); this.model.connect( this, { highlightChange: 'onModelHighlightChange', - update: 'onModelUpdate', + searchChange: 'onModelSearchChange', initialize: 'onModelInitialize' } ); + this.view = this.model.getCurrentView(); // Initialize this.$element @@ -127,14 +128,17 @@ /** * Respond to model update event */ - mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onModelUpdate = function () { + mw.rcfilters.ui.FilterMenuHeaderWidget.prototype.onModelSearchChange = function () { var currentView = this.model.getCurrentView(); - this.setLabel( this.model.getViewTitle( currentView ) ); + if ( this.view !== currentView ) { + this.setLabel( this.model.getViewTitle( currentView ) ); - this.invertNamespacesButton.toggle( currentView === 'namespaces' ); - this.backButton.toggle( currentView !== 'default' ); - this.helpIcon.toggle( currentView === 'tags' ); + this.invertNamespacesButton.toggle( currentView === 'namespaces' ); + this.backButton.toggle( currentView !== 'default' ); + this.helpIcon.toggle( currentView === 'tags' ); + this.view = currentView; + } }; /** diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js index e053914e2a..20bf73ff16 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js @@ -86,13 +86,14 @@ } // Events - this.model.connect( this, { update: 'onModelUpdate' } ); + this.model.connect( this, { update: 'updateUiBasedOnState' } ); // Initialize this.$element .addClass( 'mw-rcfilters-ui-filterMenuSectionOptionWidget' ) .addClass( 'mw-rcfilters-ui-filterMenuSectionOptionWidget-name-' + this.model.getName() ) .append( $header ); + this.updateUiBasedOnState(); }; /* Initialize */ @@ -104,11 +105,12 @@ /** * Respond to model update event */ - mw.rcfilters.ui.FilterMenuSectionOptionWidget.prototype.onModelUpdate = function () { + mw.rcfilters.ui.FilterMenuSectionOptionWidget.prototype.updateUiBasedOnState = function () { this.$element.toggleClass( 'mw-rcfilters-ui-filterMenuSectionOptionWidget-active', this.model.isActive() ); + this.toggle( this.model.isVisible() ); }; /** diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js index 91a2d5fbbf..3f47df2ee4 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js @@ -41,6 +41,8 @@ allowReordering: false, $overlay: this.$overlay, menu: { + // Our filtering is done through the model + filterFromInput: false, hideWhenOutOfView: false, hideOnChoose: false, width: 650, @@ -121,6 +123,7 @@ this.model.connect( this, { initialize: 'onModelInitialize', update: 'onModelUpdate', + searchChange: 'onModelSearchChange', itemUpdate: 'onModelItemUpdate', highlightChange: 'onModelHighlightChange' } ); @@ -237,20 +240,24 @@ this.focus(); }; + /** + * Respond to model search change event + * + * @param {string} value Search value + */ + mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onModelSearchChange = function ( value ) { + this.input.setValue( value ); + }; + /** * Respond to input change event * * @param {string} value Value of the input */ mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onInputChange = function ( value ) { - var view; - - value = value.trim(); - - view = this.model.getViewByTrigger( value.substr( 0, 1 ) ); - - this.controller.switchView( view ); + this.controller.setSearch( value ); }; + /** * Respond to query button click */ @@ -304,13 +311,8 @@ // Clear selection this.selectTag( null ); - // Clear input if the only thing in the input is the prefix - if ( - this.input.getValue().trim() === this.model.getViewTrigger( this.model.getCurrentView() ) - ) { - // Clear the input - this.input.setValue( '' ); - } + // Clear the search + this.controller.setSearch( '' ); // Log filter grouping this.controller.trackFilterGroupings( 'filtermenu' ); @@ -508,42 +510,19 @@ * @inheritdoc */ mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onTagSelect = function ( tagItem ) { - var widget = this, - menuOption = this.menu.getItemFromModel( tagItem.getModel() ), - oldInputValue = this.input.getValue().trim(); + var menuOption = this.menu.getItemFromModel( tagItem.getModel() ); this.menu.setUserSelecting( true ); - - // Reset input - this.input.setValue( '' ); - - // Switch view - this.controller.switchView( tagItem.getView() ); - // Parent method mw.rcfilters.ui.FilterTagMultiselectWidget.parent.prototype.onTagSelect.call( this, tagItem ); - this.menu.selectItem( menuOption ); - this.selectTag( tagItem ); + // Switch view + this.controller.resetSearchForView( tagItem.getView() ); - // Scroll to the item - if ( this.model.removeViewTriggers( oldInputValue ) ) { - // We're binding a 'once' to the itemVisibilityChange event - // so this happens when the menu is ready after the items - // are visible again, in case this is done right after the - // user filtered the results - this.getMenu().once( - 'itemVisibilityChange', - function () { - widget.scrollToTop( menuOption.$element ); - widget.menu.setUserSelecting( false ); - } - ); - } else { - this.scrollToTop( menuOption.$element ); - this.menu.setUserSelecting( false ); - } + this.selectTag( tagItem ); + this.scrollToTop( menuOption.$element ); + this.menu.setUserSelecting( false ); }; /** @@ -618,9 +597,7 @@ return new mw.rcfilters.ui.MenuSelectWidget( this.controller, this.model, - $.extend( { - filterFromInput: true - }, menuConfig ) + menuConfig ); }; diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js index a7d2c825e2..1cd7befe6d 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js @@ -55,7 +55,8 @@ icon: 'reload', framed: false, label: mw.msg( 'rcfilters-show-new-changes' ), - flags: [ 'progressive' ] + flags: [ 'progressive' ], + classes: [ 'mw-rcfilters-ui-filterWrapperWidget-showNewChanges' ] } ); // Initialize @@ -74,6 +75,7 @@ } // Events + this.filterTagWidget.menu.connect( this, { toggle: [ 'emit', 'menuToggle' ] } ); this.changesListModel.connect( this, { newChangesExist: 'onNewChangesExist' } ); this.showNewChangesLink.connect( this, { click: 'onShowNewChangesClick' } ); this.showNewChangesLink.toggle( false ); diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js index 51fc9bcaba..15085105f0 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js @@ -117,6 +117,8 @@ this.$element.addClass( classes.join( ' ' ) ); } + + this.updateUiBasedOnState(); }; /* Initialization */ @@ -142,6 +144,7 @@ this.itemModel.isSelected() && this.invertModel.isSelected() ); + this.toggle( this.itemModel.isVisible() ); }; /** diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MainWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MainWrapperWidget.js new file mode 100644 index 0000000000..8002045dc8 --- /dev/null +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MainWrapperWidget.js @@ -0,0 +1,124 @@ +( function ( $, mw ) { + /** + * Wrapper for changes list content + * + * @extends OO.ui.Widget + * + * @constructor + * @param {mw.rcfilters.Controller} controller Controller + * @param {mw.rcfilters.dm.FiltersViewModel} model View model + * @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved queries model + * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel + * @param {Object} config Configuration object + * @cfg {jQuery} $topSection Top section container + * @cfg {jQuery} $filtersContainer + * @cfg {jQuery} $changesListContainer + * @cfg {jQuery} $formContainer + */ + mw.rcfilters.ui.MainWrapperWidget = function MwRcfiltersUiMainWrapperWidget( + controller, model, savedQueriesModel, changesListModel, config + ) { + config = $.extend( {}, config ); + + // Parent + mw.rcfilters.ui.MainWrapperWidget.parent.call( this, config ); + + this.controller = controller; + this.model = model; + this.changesListModel = changesListModel; + this.$topSection = config.$topSection; + this.$filtersContainer = config.$filtersContainer; + this.$changesListContainer = config.$changesListContainer; + this.$formContainer = config.$formContainer; + this.$overlay = $( '<div>' ).addClass( 'mw-rcfilters-ui-overlay' ); + + this.savedLinksListWidget = new mw.rcfilters.ui.SavedLinksListWidget( + controller, savedQueriesModel, { $overlay: this.$overlay } + ); + + this.filtersWidget = new mw.rcfilters.ui.FilterWrapperWidget( + controller, + model, + savedQueriesModel, + changesListModel, + { + $overlay: this.$overlay + } + ); + + this.changesListWidget = new mw.rcfilters.ui.ChangesListWrapperWidget( + model, changesListModel, controller, this.$changesListContainer ); + + /* Events */ + + // Toggle changes list overlay when filters menu opens/closes. We use overlay on changes list + // to prevent users from accidentally clicking on links in results, while menu is opened. + // Overlay on changes list is not the same as this.$overlay + this.filtersWidget.connect( this, { menuToggle: this.onFilterMenuToggle.bind( this ) } ); + + // Initialize + this.$filtersContainer.append( this.filtersWidget.$element ); + $( 'body' ) + .append( this.$overlay ) + .addClass( 'mw-rcfilters-ui-initialized' ); + }; + + /* Initialization */ + + OO.inheritClass( mw.rcfilters.ui.MainWrapperWidget, OO.ui.Widget ); + + /* Methods */ + + /** + * Set the content of the top section, depending on the type of special page. + * + * @param {string} specialPage + */ + mw.rcfilters.ui.MainWrapperWidget.prototype.setTopSection = function ( specialPage ) { + var topSection; + + if ( specialPage === 'Recentchanges' ) { + topSection = new mw.rcfilters.ui.RcTopSectionWidget( + this.savedLinksListWidget, this.$topSection + ); + this.filtersWidget.setTopSection( topSection.$element ); + } + + if ( specialPage === 'Recentchangeslinked' ) { + topSection = new mw.rcfilters.ui.RclTopSectionWidget( + this.savedLinksListWidget, this.controller, + this.model.getGroup( 'toOrFrom' ).getItemByParamName( 'showlinkedto' ), + this.model.getGroup( 'page' ).getItemByParamName( 'target' ) + ); + + this.filtersWidget.setTopSection( topSection.$element ); + } + + if ( specialPage === 'Watchlist' ) { + topSection = new mw.rcfilters.ui.WatchlistTopSectionWidget( + this.controller, this.changesListModel, this.savedLinksListWidget, this.$topSection + ); + + this.filtersWidget.setTopSection( topSection.$element ); + } + }; + + /** + * Filter menu toggle event listener + * + * @param {boolean} isVisible + */ + mw.rcfilters.ui.MainWrapperWidget.prototype.onFilterMenuToggle = function ( isVisible ) { + this.changesListWidget.toggleOverlay( isVisible ); + }; + + /** + * Initialize FormWrapperWidget + * + * @return {mw.rcfilters.ui.FormWrapperWidget} Form wrapper widget + */ + mw.rcfilters.ui.MainWrapperWidget.prototype.initFormWidget = function () { + return new mw.rcfilters.ui.FormWrapperWidget( + this.model, this.changesListModel, this.controller, this.$formContainer ); + }; +}( jQuery, mediaWiki ) ); diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js index 22c176f282..07d8c88430 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js @@ -33,7 +33,6 @@ this.userSelecting = false; this.menuInitialized = false; - this.inputValue = ''; this.$overlay = config.$overlay || this.$element; this.$body = $( '<div>' ).addClass( 'mw-rcfilters-ui-menuSelectWidget-body' ); this.footers = []; @@ -41,7 +40,9 @@ // Parent mw.rcfilters.ui.MenuSelectWidget.parent.call( this, $.extend( { $autoCloseIgnore: this.$overlay, - width: 650 + width: 650, + // Our filtering is done through the model + filterFromInput: false }, config ) ); this.setGroupElement( $( '<div>' ) @@ -65,8 +66,8 @@ // Events this.model.connect( this, { - update: 'onModelUpdate', - initialize: 'onModelInitialize' + initialize: 'onModelInitialize', + searchChange: 'onModelSearchChange' } ); // Initialization @@ -104,7 +105,7 @@ }.bind( this ) ); // Switch to the correct view - this.switchView( this.model.getCurrentView() ); + this.updateView(); }; /* Initialize */ @@ -113,20 +114,9 @@ /* Events */ - /** - * @event itemVisibilityChange - * - * Item visibility has changed - */ - /* Methods */ - - /** - * Respond to model update event - */ - mw.rcfilters.ui.MenuSelectWidget.prototype.onModelUpdate = function () { - // Change view - this.switchView( this.model.getCurrentView() ); + mw.rcfilters.ui.MenuSelectWidget.prototype.onModelSearchChange = function () { + this.updateView(); }; /** @@ -135,6 +125,8 @@ mw.rcfilters.ui.MenuSelectWidget.prototype.toggle = function ( show ) { this.lazyMenuCreation(); mw.rcfilters.ui.MenuSelectWidget.parent.prototype.toggle.call( this, show ); + // Always open this menu downwards. FilterTagMultiselectWidget scrolls it into view. + this.setVerticalPosition( 'below' ); }; /** @@ -142,6 +134,7 @@ */ mw.rcfilters.ui.MenuSelectWidget.prototype.lazyMenuCreation = function () { var widget = this, + items = [], viewGroupCount = {}, groups = this.model.getFilterGroups(); @@ -150,8 +143,6 @@ } this.menuInitialized = true; - // Reset - this.clearItems(); // Count groups per view $.each( groups, function ( groupName, groupModel ) { @@ -200,10 +191,12 @@ // without rebuilding the widgets each time widget.views[ view ] = widget.views[ view ] || []; widget.views[ view ] = widget.views[ view ].concat( currentItems ); + items = items.concat( currentItems ); } } ); - this.switchView( this.model.getCurrentView() ); + this.addItems( items ); + this.updateView(); }; /** @@ -214,16 +207,12 @@ }; /** - * Switch view - * - * @param {string} [viewName] View name. If not given, default is used. + * Update view */ - mw.rcfilters.ui.MenuSelectWidget.prototype.switchView = function ( viewName ) { - viewName = viewName || 'default'; + mw.rcfilters.ui.MenuSelectWidget.prototype.updateView = function () { + var viewName = this.model.getCurrentView(); if ( this.views[ viewName ] && this.currentView !== viewName ) { - this.clearItems(); - this.addItems( this.views[ viewName ] ); this.updateFooterVisibility( viewName ); this.$element @@ -233,8 +222,10 @@ this.currentView = viewName; this.scrollToTop(); - this.clip(); } + + this.postProcessItems(); + this.clip(); }; /** @@ -256,24 +247,18 @@ }; /** - * @fires itemVisibilityChange - * @inheritdoc + * Post-process items after the visibility changed. Make sure + * that we always have an item selected, and that the no-results + * widget appears if the menu is empty. */ - mw.rcfilters.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { + mw.rcfilters.ui.MenuSelectWidget.prototype.postProcessItems = function () { var i, itemWasSelected = false, - inputVal = this.$input.val(), items = this.getItems(); - // Since the method hides/shows items, we don't want to - // call it unless the input actually changed - if ( - !this.userSelecting && - this.inputValue !== inputVal - ) { - // Parent method - mw.rcfilters.ui.MenuSelectWidget.parent.prototype.updateItemVisibility.call( this ); - + // If we are not already selecting an item, always make sure + // that the top item is selected + if ( !this.userSelecting ) { // Select the first item in the list for ( i = 0; i < items.length; i++ ) { if ( @@ -289,11 +274,6 @@ if ( !itemWasSelected ) { this.selectItem( null ); } - - // Cache value - this.inputValue = inputVal; - - this.emit( 'itemVisibilityChange' ); } this.noResults.toggle( !this.getItems().some( function ( item ) { @@ -314,25 +294,12 @@ } )[ 0 ]; }; - /** - * Override the item matcher to use the model's match process - * - * @inheritdoc - */ - mw.rcfilters.ui.MenuSelectWidget.prototype.getItemMatcher = function ( s ) { - var results = this.model.findMatches( s, true ); - - return function ( item ) { - return results.indexOf( item.getModel() ) > -1; - }; - }; - /** * @inheritdoc */ mw.rcfilters.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) { var nextItem, - currentItem = this.getHighlightedItem() || this.getSelectedItem(); + currentItem = this.findHighlightedItem() || this.getSelectedItem(); // Call parent mw.rcfilters.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e ); diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.RclTargetPageWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.RclTargetPageWidget.js index 6673c082d8..527d790d6c 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.RclTargetPageWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.RclTargetPageWidget.js @@ -71,6 +71,9 @@ * Respond to the model being updated */ mw.rcfilters.ui.RclTargetPageWidget.prototype.updateUiBasedOnModel = function () { - this.titleSearch.setValue( this.model.getValue() ); + var title = mw.Title.newFromText( this.model.getValue() ), + text = title ? title.toText() : this.model.getValue(); + this.titleSearch.setValue( text ); + this.titleSearch.setTitle( text ); }; }( mediaWiki ) ); diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.js index bb837e00a4..f546d9748e 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.js @@ -26,7 +26,6 @@ mw.rcfilters.ui.SaveFiltersPopupButtonWidget.parent.call( this, $.extend( { framed: false, icon: 'unClip', - $overlay: this.$overlay, title: mw.msg( 'rcfilters-savedqueries-add-new-title' ), popup: { classes: [ 'mw-rcfilters-ui-saveFiltersPopupButtonWidget-popup' ], diff --git a/resources/src/mediawiki.skinning/elements.css b/resources/src/mediawiki.skinning/elements.css index 58fd500d06..19f35535eb 100644 --- a/resources/src/mediawiki.skinning/elements.css +++ b/resources/src/mediawiki.skinning/elements.css @@ -205,11 +205,7 @@ tt, kbd, samp, .mw-code { - /* - * Some browsers will render the monospace text too small, namely Firefox, Chrome and Safari. - * Specifying any valid, second value will trigger correct behavior without forcing a different font. - * See T176636 - */ + /* Support: Blink, Gecko, Webkit; enable unified font sizes for monospace font. T176636 */ font-family: monospace, monospace; } diff --git a/resources/src/mediawiki.special/mediawiki.special.changeslist.css b/resources/src/mediawiki.special/mediawiki.special.changeslist.css index 9521a2b513..44adbf3dc9 100644 --- a/resources/src/mediawiki.special/mediawiki.special.changeslist.css +++ b/resources/src/mediawiki.special/mediawiki.special.changeslist.css @@ -55,7 +55,7 @@ } /* Absolute positioning to avoid table overlap with floating elements */ -.mw-changeslist-legend.mw-enhanced { +.mw-changeslist > .mw-changeslist-legend.mw-enhanced { background-color: #fff; position: absolute; right: 0; diff --git a/resources/src/mediawiki.special/mediawiki.special.css b/resources/src/mediawiki.special/mediawiki.special.css index 5d0ec49a6b..c25984c6e4 100644 --- a/resources/src/mediawiki.special/mediawiki.special.css +++ b/resources/src/mediawiki.special/mediawiki.special.css @@ -106,6 +106,11 @@ columns: 16em 2; } +.mw-specialpages-list ul { + margin-top: 0; + margin-bottom: 0; +} + /* Special:Statistics */ .mw-statistics-numbers { text-align: right; diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.personalEmail.js b/resources/src/mediawiki.special/mediawiki.special.preferences.personalEmail.js new file mode 100644 index 0000000000..f934d59e1e --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.personalEmail.js @@ -0,0 +1,24 @@ +/*! + * JavaScript for Special:Preferences: Email preferences better UX + */ +( function ( $ ) { + $( function () { + var allowEmail, allowEmailFromNewUsers; + + allowEmail = $( '#wpAllowEmail' ); + allowEmailFromNewUsers = $( '#wpAllowEmailFromNewUsers' ); + + function toggleDisabled() { + if ( allowEmail.is( ':checked' ) && allowEmail.is( ':enabled' ) ) { + allowEmailFromNewUsers.prop( 'disabled', false ); + } else { + allowEmailFromNewUsers.prop( 'disabled', true ); + } + } + + if ( allowEmail ) { + allowEmail.on( 'change', toggleDisabled ); + toggleDisabled(); + } + } ); +}( jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.upload.js b/resources/src/mediawiki.special/mediawiki.special.upload.js index 0ddf7fe3f1..aa003591f3 100644 --- a/resources/src/mediawiki.special/mediawiki.special.upload.js +++ b/resources/src/mediawiki.special/mediawiki.special.upload.js @@ -612,4 +612,43 @@ allowCloseWindow.release(); } ); } ); + + // Add tabindex to mw-editTools + $( function () { + // Function to change tabindex for all links within mw-editTools + function setEditTabindex( $val ) { + $( '.mw-editTools' ).find( 'a' ).each( function () { + $( this ).attr( 'tabindex', $val ); + } ); + } + + // Change tabindex to 0 if user pressed spaced or enter while focused + $( '.mw-editTools' ).on( 'keypress', function ( e ) { + // Don't continue if pressed key was not enter or spacebar + if ( e.which !== 13 && e.which !== 32 ) { + return; + } + + // Change tabindex only when main div has focus + if ( $( this ).is( ':focus' ) ) { + $( this ).find( 'a' ).first().focus(); + setEditTabindex( '0' ); + } + } ); + + // Reset tabindex for elements when user focused out mw-editTools + $( '.mw-editTools' ).on( 'focusout', function ( e ) { + // Don't continue if relatedTarget is within mw-editTools + if ( e.relatedTarget !== null && $( e.relatedTarget ).closest( '.mw-editTools' ).length > 0 ) { + return; + } + + // Reset tabindex back to -1 + setEditTabindex( '-1' ); + } ); + + // Set initial tabindex for mw-editTools to 0 and to -1 for all links + $( '.mw-editTools' ).attr( 'tabindex', '0' ); + setEditTabindex( '-1' ); + } ); }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki/mediawiki.debug.less b/resources/src/mediawiki/mediawiki.debug.less index 00faf84b84..a56e4592a2 100644 --- a/resources/src/mediawiki/mediawiki.debug.less +++ b/resources/src/mediawiki/mediawiki.debug.less @@ -92,7 +92,7 @@ a.mw-debug-panelabel:visited { height: 300px; overflow: scroll; display: none; - font-family: monospace; + font-family: monospace, monospace; font-size: 11px; background-color: #e1eff2; box-sizing: border-box; diff --git a/resources/src/mediawiki/mediawiki.editfont.css b/resources/src/mediawiki/mediawiki.editfont.css index 6228030dd2..fe7f3240df 100644 --- a/resources/src/mediawiki/mediawiki.editfont.css +++ b/resources/src/mediawiki/mediawiki.editfont.css @@ -1,6 +1,6 @@ /* Edit font preference */ .mw-editfont-monospace { - font-family: monospace; + font-family: monospace, monospace; } .mw-editfont-sans-serif { @@ -10,3 +10,10 @@ .mw-editfont-serif { font-family: serif; } + +/* Standardize font size for edit areas using edit-fonts T182320 */ +.mw-editfont-monospace, +.mw-editfont-sans-serif, +.mw-editfont-serif { + font-size: 13px; +} diff --git a/resources/src/mediawiki/mediawiki.feedback.js b/resources/src/mediawiki/mediawiki.feedback.js index 2d55094fdb..bfd5c06045 100644 --- a/resources/src/mediawiki/mediawiki.feedback.js +++ b/resources/src/mediawiki/mediawiki.feedback.js @@ -83,8 +83,7 @@ /** * Respond to dialog submit event. If the information was - * submitted, either successfully or with an error, open - * a MessageDialog to thank the user. + * submitted successfully, open a MessageDialog to thank the user. * * @param {string} [status] A status of the end of operation * of the main feedback dialog. Empty if the dialog was @@ -92,37 +91,36 @@ * to the external task reporting site. */ mw.Feedback.prototype.onDialogSubmit = function ( status ) { - var dialogConfig = {}; - switch ( status ) { - case 'submitted': - dialogConfig = { - title: mw.msg( 'feedback-thanks-title' ), - message: $( '<span>' ).msg( - 'feedback-thanks', - this.feedbackPageTitle.getNameText(), - $( '<a>' ).attr( { - target: '_blank', - href: this.feedbackPageTitle.getUrl() - } ) - ), - actions: [ - { - action: 'accept', - label: mw.msg( 'feedback-close' ), - flags: 'primary' - } - ] - }; - break; + var dialogConfig; + + if ( status !== 'submitted' ) { + return; } + dialogConfig = { + title: mw.msg( 'feedback-thanks-title' ), + message: $( '<span>' ).msg( + 'feedback-thanks', + this.feedbackPageTitle.getNameText(), + $( '<a>' ).attr( { + target: '_blank', + href: this.feedbackPageTitle.getUrl() + } ) + ), + actions: [ + { + action: 'accept', + label: mw.msg( 'feedback-close' ), + flags: 'primary' + } + ] + }; + // Show the message dialog - if ( !$.isEmptyObject( dialogConfig ) ) { - this.constructor.static.windowManager.openWindow( - this.thankYouDialog, - dialogConfig - ); - } + this.constructor.static.windowManager.openWindow( + this.thankYouDialog, + dialogConfig + ); }; /** @@ -421,14 +419,8 @@ * @return {OO.ui.Error} */ mw.Feedback.Dialog.prototype.getErrorMessage = function () { - switch ( this.status ) { - case 'error1': - case 'error2': - case 'error3': - case 'error4': - // Messages: feedback-error1, feedback-error2, feedback-error3, feedback-error4 - return new OO.ui.Error( mw.msg( 'feedback-' + this.status ) ); - } + // Messages: feedback-error1, feedback-error2, feedback-error3, feedback-error4 + return new OO.ui.Error( mw.msg( 'feedback-' + this.status ) ); }; /** diff --git a/resources/src/mediawiki/mediawiki.js b/resources/src/mediawiki/mediawiki.js index a661ae5521..6a218e3d25 100644 --- a/resources/src/mediawiki/mediawiki.js +++ b/resources/src/mediawiki/mediawiki.js @@ -879,8 +879,10 @@ // Cache marker = document.querySelector( 'meta[name="ResourceLoaderDynamicStyles"]' ); if ( !marker ) { - mw.log( 'Create <meta name="ResourceLoaderDynamicStyles"> dynamically' ); - marker = $( '<meta>' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' )[ 0 ]; + mw.log( 'Created ResourceLoaderDynamicStyles marker dynamically' ); + marker = document.createElement( 'meta' ); + marker.name = 'ResourceLoaderDynamicStyles'; + document.head.appendChild( marker ); } } return marker; @@ -902,7 +904,7 @@ if ( nextNode && nextNode.parentNode ) { nextNode.parentNode.insertBefore( s, nextNode ); } else { - document.getElementsByTagName( 'head' )[ 0 ].appendChild( s ); + document.head.appendChild( s ); } return s; @@ -2059,7 +2061,7 @@ l = document.createElement( 'link' ); l.rel = 'stylesheet'; l.href = modules; - $( 'head' ).append( l ); + document.head.appendChild( l ); return; } if ( type === 'text/javascript' || type === undefined ) { @@ -2753,7 +2755,7 @@ // If we have an exception object, log it to the warning channel to trigger // proper stacktraces in browsers that support it. if ( e && console.warn ) { - console.warn( String( e ), e ); + console.warn( e ); } } /* eslint-enable no-console */ diff --git a/resources/src/startup.js b/resources/src/startup.js index b0c15781ee..8e8463d251 100644 --- a/resources/src/startup.js +++ b/resources/src/startup.js @@ -162,5 +162,5 @@ window.isCompatible = function ( str ) { // Callback startUp(); }; - document.getElementsByTagName( 'head' )[ 0 ].appendChild( script ); + document.head.appendChild( script ); }() ); diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php index 44a00a8964..e07d4a0cf3 100644 --- a/tests/parser/ParserTestRunner.php +++ b/tests/parser/ParserTestRunner.php @@ -708,15 +708,15 @@ class ParserTestRunner { public function meetsRequirements( $requirements ) { foreach ( $requirements as $requirement ) { switch ( $requirement['type'] ) { - case 'hook': - $ok = $this->requireHook( $requirement['name'] ); - break; - case 'functionHook': - $ok = $this->requireFunctionHook( $requirement['name'] ); - break; - case 'transparentHook': - $ok = $this->requireTransparentHook( $requirement['name'] ); - break; + case 'hook': + $ok = $this->requireHook( $requirement['name'] ); + break; + case 'functionHook': + $ok = $this->requireFunctionHook( $requirement['name'] ); + break; + case 'transparentHook': + $ok = $this->requireTransparentHook( $requirement['name'] ); + break; } if ( !$ok ) { return false; diff --git a/tests/parser/TestFileEditor.php b/tests/parser/TestFileEditor.php index 7f646710ed..1bee31eaa4 100644 --- a/tests/parser/TestFileEditor.php +++ b/tests/parser/TestFileEditor.php @@ -162,18 +162,18 @@ class TestFileEditor { if ( isset( $changes[$sectionName] ) ) { $change = $changes[$sectionName]; switch ( $change['op'] ) { - case 'rename': - $test[$i]['name'] = $change['value']; - $test[$i]['headingLine'] = "!! {$change['value']}"; - break; - case 'update': - $test[$i]['contents'] = $change['value']; - break; - case 'delete': - $test[$i]['deleted'] = true; - break; - default: - throw new Exception( "Unknown op: ${change['op']}" ); + case 'rename': + $test[$i]['name'] = $change['value']; + $test[$i]['headingLine'] = "!! {$change['value']}"; + break; + case 'update': + $test[$i]['contents'] = $change['value']; + break; + case 'delete': + $test[$i]['deleted'] = true; + break; + default: + throw new Exception( "Unknown op: ${change['op']}" ); } // Acknowledge // Note that we use the old section name for the rename op diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt index 7af3a3655b..840c74dbb9 100644 --- a/tests/parser/parserTests.txt +++ b/tests/parser/parserTests.txt @@ -546,15 +546,19 @@ Extra newlines between heading and content are swallowed Heading with line break in nowiki !! options parsoid=wt2html +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext == A <nowiki>B C</nowiki> == -!! html -<h2><span class="mw-headline" id="A_B.0AC">A B +!! html/php +<h2><span id="A_B.0AC"></span><span class="mw-headline" id="A_B +C">A B C</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: A B C">edit</a><span class="mw-editsection-bracket">]</span></span></h2> !! html/parsoid -<h2 id="A_B.0AC">A <span typeof="mw:Nowiki">B +<h2 id="A_B +C"><span id="A_B.0AC" typeof="mw:FallbackId"></span> A <span typeof="mw:Nowiki">B C</span> </h2> !! end @@ -4851,8 +4855,8 @@ parsoid=wt2html,wt2wt </p> !! html/parsoid <p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo"></a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" title="wikipedia:Foo">Bar</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" title="wikipedia:Foo"><span>Bar</span></a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" title="wikipedia:Foo">Bar</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" title="wikipedia:Foo"><span>Bar</span></a></p> !! end !! test @@ -6715,9 +6719,9 @@ Don't break on | in extension attribute in template <references /> !! html/parsoid -<p><span about="#mwt2" class="mw-ref" id="cite_ref-hi.7Cho_1-0" rel="dc:references" typeof="mw:Transclusion mw:Extension/ref" data-parsoid='{"pi":[[{"k":"1"}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"<ref name=\"hi|ho\">ha</ref>"}},"i":0}}]}'><a href="./Main_Page#cite_note-hi.7Cho-1" style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a></span></p> +<p><span about="#mwt2" class="mw-ref" id="cite_ref-hi|ho_1-0" rel="dc:references" typeof="mw:Transclusion mw:Extension/ref" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"<ref name=\"hi|ho\">ha</ref>"}},"i":0}}]}'><a href="./Main_Page#cite_note-hi|ho-1" style="counter-reset: mw-Ref 1;"><span class="mw-reflink-text">[1]</span></a></span></p> -<ol class="mw-references references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-hi.7Cho-1" id="cite_note-hi.7Cho-1"><a href="./Main_Page#cite_ref-hi.7Cho_1-0" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-hi.7Cho-1" class="mw-reference-text">ha</span></li></ol> +<ol class="mw-references references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-hi|ho-1" id="cite_note-hi|ho-1"><a href="./Main_Page#cite_ref-hi|ho_1-0" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-hi|ho-1" class="mw-reference-text">ha</span></li></ol> !! end ## We don't support roundtripping of these attributes in Parsoid. @@ -7825,13 +7829,15 @@ Link with multiple pipes !! test Anchor containing a #. (T65430) +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext [[Main Page#And#Link]] !! html/php -<p><a href="/wiki/Main_Page#And.23Link" title="Main Page">Main Page#And#Link</a> +<p><a href="/wiki/Main_Page#And#Link" title="Main Page">Main Page#And#Link</a> </p> !! html/parsoid -<p><a rel="mw:WikiLink" href="./Main_Page#And.23Link" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#And.23Link"},"sa":{"href":"Main Page#And#Link"}}'>Main Page#And#Link</a></p> +<p><a rel="mw:WikiLink" href="./Main_Page#And#Link" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#And#Link"},"sa":{"href":"Main Page#And#Link"}}'>Main Page#And#Link</a></p> !! end !! test @@ -7949,13 +7955,27 @@ Link containing % as a double hex sequence interpreted to hex sequence ## Example for such a section: == < == !! test Link containing "#<" and "#>" % as a hex sequences- these are valid section anchors +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext [[%23%3c]][[%23%3e]] !! html/php -<p><a href="#.3C">#<</a><a href="#.3E">#></a> +<p><a href="#<">#<</a><a href="#>">#></a> </p> !! html/parsoid -<p><a rel="mw:WikiLink" href="./Main_Page#.3C" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#.3C"},"sa":{"href":"%23%3c"}}'>#<</a><a rel="mw:WikiLink" href="./Main_Page#.3E" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#.3E"},"sa":{"href":"%23%3e"}}'>#></a></p> +<p><a rel="mw:WikiLink" href="./Main_Page#<" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#<"},"sa":{"href":"%23%3c"}}'>#<</a><a rel="mw:WikiLink" href="./Main_Page#>" title="Main Page" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#>"},"sa":{"href":"%23%3e"}}'>#></a></p> +!! end + +## Example for such a section: == < == +!! test +Link containing "#<" and "#>" % as a hex sequences- these are valid section anchors (legacy) +!! config +wgFragmentMode=[ 'legacy' ] +!! wikitext +[[%23%3c]][[%23%3e]] +!! html/php +<p><a href="#.3C">#<</a><a href="#.3E">#></a> +</p> !! end !! test @@ -8017,7 +8037,7 @@ Link containing double quotes and spaces <p><a href="/index.php?title=Cool_%22Gator%22&action=edit&redlink=1" class="new" title="Cool "Gator" (page does not exist)">Cool "Gator"</a> </p> !! html/parsoid -<p><a rel="mw:WikiLink" href="./Cool_%22Gator%22" title='Cool "Gator"'>Cool "Gator"</a></p> +<p><a rel="mw:WikiLink" href='./Cool_"Gator"' title='Cool "Gator"'>Cool "Gator"</a></p> !! end !! test @@ -8025,7 +8045,7 @@ File containing double quotes and spaces !! wikitext [[File:Cool "Gator".png]] !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Cool_%22Gator%22.png" data-parsoid='{"a":{"href":"./File:Cool_%22Gator%22.png"},"sa":{"href":"File:Cool \"Gator\".png"}}'><img resource='./File:Cool_"Gator".png' src="./Special:FilePath/Cool_%22Gator%22.png" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Cool_\"Gator\".png","height":"220","width":"220","src":"./Special:FilePath/Cool_%22Gator%22.png"},"sa":{"resource":"File:Cool \"Gator\".png","src":"./Special:FilePath/Cool_\"Gator\".png"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Cool_%22Gator%22.png" data-parsoid='{"a":{"href":"./File:Cool_%22Gator%22.png"},"sa":{"href":"File:Cool \"Gator\".png"}}'><img resource='./File:Cool_"Gator".png' src="./Special:FilePath/Cool_%22Gator%22.png" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Cool_\"Gator\".png","height":"220","width":"220","src":"./Special:FilePath/Cool_%22Gator%22.png"},"sa":{"resource":"File:Cool \"Gator\".png","src":"./Special:FilePath/Cool_\"Gator\".png"}}'/></a></figure-inline></p> !! end !! test @@ -8073,7 +8093,7 @@ Link with double quotes in title part (literal) and alternate part (interpreted) </p><p><a href="/index.php?title=%27%27Pentecoste%27%27&action=edit&redlink=1" class="new" title="''Pentecoste'' (page does not exist)"><i>Pentecoste</i></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Denys_Savchenko_''Pentecoste''.jpg"><img resource="./File:Denys_Savchenko_''Pentecoste''.jpg" src="./Special:FilePath/Denys_Savchenko_''Pentecoste''.jpg" height="220" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Denys_Savchenko_''Pentecoste''.jpg"><img resource="./File:Denys_Savchenko_''Pentecoste''.jpg" src="./Special:FilePath/Denys_Savchenko_''Pentecoste''.jpg" height="220" width="220"/></a></figure-inline></p> <p><a rel="mw:WikiLink" href="./''Pentecoste''" title="''Pentecoste''">''Pentecoste''</a></p> <p><a rel="mw:WikiLink" href="./''Pentecoste''" title="''Pentecoste''">Pentecoste</a></p> <p><a rel="mw:WikiLink" href="./''Pentecoste''" title="''Pentecoste''"><i>Pentecoste</i></a></p> @@ -8093,10 +8113,10 @@ Broken image links with HTML captions (T41700) <a href="/index.php?title=Special:Upload&wpDestFile=Nonexistent" class="new" title="File:Nonexistent">abc</a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<script></script>"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></span> -<span typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"width","ak":"100x100px"},{"ck":"caption","ak":"<script></script>"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="100" width="100" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"100","width":"100"},"sa":{"resource":"File:Nonexistent"}}'/></a></span> -<span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"&lt;"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<span typeof=\"mw:Entity\" data-parsoid='{\"src\":\"&amp;lt;\",\"srcContent\":\"&lt;\",\"dsr\":[107,111,null,null]}'>&lt;</span>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></span> -<span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"a<i>b</i>c"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"a<i data-parsoid='{\"stx\":\"html\",\"dsr\":[134,142,3,4]}'>b</i>c"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<script></script>"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></figure-inline> +<figure-inline typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"width","ak":"100x100px"},{"ck":"caption","ak":"<script></script>"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="100" width="100" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"100","width":"100"},"sa":{"resource":"File:Nonexistent"}}'/></a></figure-inline> +<figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"&lt;"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<span typeof=\"mw:Entity\" data-parsoid='{\"src\":\"&amp;lt;\",\"srcContent\":\"&lt;\",\"dsr\":[107,111,null,null]}'>&lt;</span>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></figure-inline> +<figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"a<i>b</i>c"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"a<i data-parsoid='{\"stx\":\"html\",\"dsr\":[134,142,3,4]}'>b</i>c"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Nonexistent","height":"220","width":"220"},"sa":{"resource":"File:Nonexistent"}}'/></a></figure-inline></p> !! end !! test @@ -8600,13 +8620,26 @@ Parsoid: Scoped parsing should handle mixed transclusions and plain text !! test Link with angle bracket after anchor +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext [[Foo#<bar>]] !! html/php -<p><a href="/wiki/Foo#.3Cbar.3E" title="Foo">Foo#<bar></a> +<p><a href="/wiki/Foo#<bar>" title="Foo">Foo#<bar></a> </p> !! html/parsoid -<p><a rel="mw:WikiLink" href="./Foo#.3Cbar.3E" title="Foo" data-parsoid='{"stx":"simple","a":{"href":"./Foo#.3Cbar.3E"},"sa":{"href":"Foo#<bar>"}}'>Foo#<bar></a></p> +<p><a rel="mw:WikiLink" href="./Foo#<bar>" title="Foo" data-parsoid='{"stx":"simple","a":{"href":"./Foo#<bar>"},"sa":{"href":"Foo#<bar>"}}'>Foo#<bar></a></p> +!! end + +!! test +Link with angle bracket after anchor (legacy) +!! config +wgFragmentMode=[ 'legacy' ] +!! wikitext +[[Foo#<bar>]] +!! html/php +<p><a href="/wiki/Foo#.3Cbar.3E" title="Foo">Foo#<bar></a> +</p> !! end ### @@ -8623,7 +8656,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity" class="extiw" title="meatball:SoftSecurity">MeatBall:SoftSecurity</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity" title="meatball:SoftSecurity">MeatBall:SoftSecurity</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity" title="meatball:SoftSecurity">MeatBall:SoftSecurity</a></p> !! end !! test @@ -8636,7 +8669,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="http://www.usemod.com/cgi-bin/mb.pl" class="extiw" title="meatball:">MeatBall:</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?" title="meatball:">MeatBall:</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?" title="meatball:">MeatBall:</a></p> !! end ## html2wt and html2html will fail because we will prefer the :en: interwiki prefix over wikipedia: @@ -8658,8 +8691,8 @@ parsoid=wt2html,wt2wt </ul> !! html/parsoid <ul> -<li><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/ro:OlteniÅ£a" title="wikipedia:ro:OlteniÅ£a">Wikipedia:ro:OlteniÅ£a</a></li> -<li><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/ro:OlteniÅ£a" title="wikipedia:ro:OlteniÅ£a">Wikipedia:ro:OlteniÅ£a</a></li> +<li><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/ro:OlteniÅ£a" title="wikipedia:ro:OlteniÅ£a">Wikipedia:ro:OlteniÅ£a</a></li> +<li><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/ro:OlteniÅ£a" title="wikipedia:ro:OlteniÅ£a">Wikipedia:ro:OlteniÅ£a</a></li> </ul> !! end @@ -8674,6 +8707,27 @@ Interwiki link with fragment (T4130) !! test Link scenarios with escaped fragments +!! config +wgFragmentMode=[ 'html5', 'legacy' ] +!! wikitext +[[#Is this great?]] +[[Foo#Is this great?]] +[[meatball:Foo#Is this great?]] +!! html/php +<p><a href="#Is_this_great?">#Is this great?</a> +<a href="/wiki/Foo#Is_this_great?" title="Foo">Foo#Is this great?</a> +<a href="http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great.3F" class="extiw" title="meatball:Foo">meatball:Foo#Is this great?</a> +</p> +!! html/parsoid +<p><a rel="mw:WikiLink" href="./Main_Page#Is_this_great?" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Is_this_great?"},"sa":{"href":"#Is this great?"}}'>#Is this great?</a> +<a rel="mw:WikiLink" href="./Foo#Is_this_great?" title="Foo" data-parsoid='{"stx":"simple","a":{"href":"./Foo#Is_this_great?"},"sa":{"href":"Foo#Is this great?"}}'>Foo#Is this great?</a> +<a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great?" title="meatball:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great?"},"sa":{"href":"meatball:Foo#Is this great?"},"isIW":true}'>meatball:Foo#Is this great?</a></p> +!! end + +!! test +Link scenarios with escaped fragments (legacy) +!! config +wgFragmentMode=[ 'legacy' ] !! wikitext [[#Is this great?]] [[Foo#Is this great?]] @@ -8683,10 +8737,6 @@ Link scenarios with escaped fragments <a href="/wiki/Foo#Is_this_great.3F" title="Foo">Foo#Is this great?</a> <a href="http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great.3F" class="extiw" title="meatball:Foo">meatball:Foo#Is this great?</a> </p> -!! html/parsoid -<p><a rel="mw:WikiLink" href="./Main_Page#Is_this_great.3F" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Is_this_great.3F"},"sa":{"href":"#Is this great?"}}'>#Is this great?</a> -<a rel="mw:WikiLink" href="./Foo#Is_this_great.3F" title="Foo" data-parsoid='{"stx":"simple","a":{"href":"./Foo#Is_this_great.3F"},"sa":{"href":"Foo#Is this great?"}}'>Foo#Is this great?</a> -<a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great.3F" title="meatball:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://www.usemod.com/cgi-bin/mb.pl?Foo#Is_this_great.3F"},"sa":{"href":"meatball:Foo#Is this great?"},"isIW":true}'>meatball:Foo#Is this great?</a></p> !! end # Ideally the wikipedia: prefix here should be proto-relative too @@ -8711,19 +8761,19 @@ Different interwiki prefixes mapping to the same URL [[ wikiPEdia :Foo]] !! html/parsoid -<p><a rel="mw:ExtLink" href="//en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"//en.wikipedia.org/wiki/Foo"},"sa":{"href":":en:Foo"},"isIW":true}' title="en:Foo">en:Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="//en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"//en.wikipedia.org/wiki/Foo"},"sa":{"href":":en:Foo"},"isIW":true}' title="en:Foo">en:Foo</a></p> -<p><a rel="mw:ExtLink" href="//en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"//en.wikipedia.org/wiki/Foo"},"sa":{"href":":en:Foo"},"isIW":true}' title="en:Foo">Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="//en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"//en.wikipedia.org/wiki/Foo"},"sa":{"href":":en:Foo"},"isIW":true}' title="en:Foo">Foo</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":"wikipedia:Foo"},"isIW":true}' title="wikipedia:Foo">wikipedia:Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":"wikipedia:Foo"},"isIW":true}' title="wikipedia:Foo">wikipedia:Foo</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":":wikipedia:Foo"},"isIW":true}' title="wikipedia:Foo">Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":":wikipedia:Foo"},"isIW":true}' title="wikipedia:Foo">Foo</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/en:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/en:Foo"},"sa":{"href":"wikipedia:en:Foo"},"isIW":true}' title="wikipedia:en:Foo">wikipedia:en:Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/en:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/en:Foo"},"sa":{"href":"wikipedia:en:Foo"},"isIW":true}' title="wikipedia:en:Foo">wikipedia:en:Foo</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/en:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/en:Foo"},"sa":{"href":":wikipedia:en:Foo"},"isIW":true}' title="wikipedia:en:Foo">wikipedia:en:Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/en:Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/en:Foo"},"sa":{"href":":wikipedia:en:Foo"},"isIW":true}' title="wikipedia:en:Foo">wikipedia:en:Foo</a></p> -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":" wikiPEdia :Foo"},"isIW":true}' title="wikipedia:Foo"> wikiPEdia :Foo</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":" wikiPEdia :Foo"},"isIW":true}' title="wikipedia:Foo"> wikiPEdia :Foo</a></p> !! end !! test @@ -8743,9 +8793,9 @@ Interwiki links that cannot be represented in wiki syntax <a rel="nofollow" class="external text" href="http://de.wikipedia.org/wiki/#foo">is just fragment</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?ok" title="meatball:ok">meatball:ok</a> -<a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?ok#foo" title="meatball:ok">ok with fragment</a> -<a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?ok_as_well?" title="meatball:ok as well?">ok ending with ? mark</a> +<p><a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?ok" title="meatball:ok">meatball:ok</a> +<a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?ok#foo" title="meatball:ok">ok with fragment</a> +<a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?ok_as_well?" title="meatball:ok as well?">ok ending with ? mark</a> <a rel="mw:ExtLink" href="http://de.wikipedia.org/wiki/Foo?action=history">has query</a> <a rel="mw:ExtLink" href="http://de.wikipedia.org/wiki/#foo">is just fragment</a></p> !! end @@ -8758,7 +8808,7 @@ Interwiki links: trail <p><a href="http://en.wikipedia.org/wiki/Foo" class="extiw" title="wikipedia:Foo">Bar</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":"wikipedia:Foo"},"isIW":true,"tail":"r"}' title="wikipedia:Foo">Bar</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"piped","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":"wikipedia:Foo"},"isIW":true,"tail":"r"}' title="wikipedia:Foo">Bar</a></p> !! end !! test @@ -8812,7 +8862,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="http://www.usemod.com/cgi-bin/mb.pl?Hello" class="extiw" title="meatball:Hello">local:meatball:Hello</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://www.usemod.com/cgi-bin/mb.pl?Hello" title="meatball:Hello">local:meatball:Hello</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://www.usemod.com/cgi-bin/mb.pl?Hello" title="meatball:Hello">local:meatball:Hello</a></p> !! end !! test @@ -8910,8 +8960,8 @@ Blah blah blah </p> !! html/parsoid <p>Blah blah blah -<a rel="mw:ExtLink" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">es:Spanish</a> -<a rel="mw:ExtLink" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese"> zh : Chinese </a></p> +<a rel="mw:WikiLink/Interwiki" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">es:Spanish</a> +<a rel="mw:WikiLink/Interwiki" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese"> zh : Chinese </a></p> !! end !! test @@ -8928,7 +8978,7 @@ parsoid=wt2html [[:::es:Spanish]] </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">es:Spanish</a> +<p><a rel="mw:WikiLink/Interwiki" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">es:Spanish</a> [[::es:Spanish]] [[:::es:Spanish]]</p> !! end @@ -9005,7 +9055,7 @@ parsoid=wt2html,wt2wt,html2html Blah blah blah [[zh:Chinese]] !! html/parsoid -<p>Blah blah blah <a rel="mw:ExtLink" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p> +<p>Blah blah blah <a rel="mw:WikiLink/Interwiki" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p> !! end ## PHP parser tests script needs an update @@ -9019,7 +9069,7 @@ parsoid=wt2html,wt2wt,html2html Blah blah blah [[zh:Chinese]] !! html/parsoid -<p>Blah blah blah <a rel="mw:ExtLink" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p> +<p>Blah blah blah <a rel="mw:WikiLink/Interwiki" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p> !! end !! test @@ -9106,7 +9156,7 @@ parsoid=wt2html,wt2wt,html2html </p><p><a href="/wiki/Ko:" title="Ko:">ko:</a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://es.wikipedia.org/wiki/" title="es:">es:</a></p> +<p><a rel="mw:WikiLink/Interwiki" href="http://es.wikipedia.org/wiki/" title="es:">es:</a></p> <p><a rel="mw:WikiLink" href="./Ko:" title="Ko:">ko:</a></p> !! end @@ -9134,7 +9184,7 @@ Blah blah blah </p> !! html/parsoid <p>Blah blah blah -<a rel="mw:ExtLink" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">local:es:Spanish</a></p> +<a rel="mw:WikiLink/Interwiki" href="http://es.wikipedia.org/wiki/Spanish" title="es:Spanish">local:es:Spanish</a></p> !! end !! test @@ -9177,10 +9227,12 @@ Blah blah blah # This tests the Parsoid bail-out code. !! test 3. Other redirect variants +!! options +parsoid=wt2html !! wikitext #REDIRECT [[<nowiki>[[Bar]]</nowiki>]] !! html/parsoid -<ol><li data-parsoid>REDIRECT [[[[Bar]]]]</li></ol> +<ol><li>REDIRECT [[<span typeof="mw:Nowiki">[[Bar]]</span>]]</li></ol> !! end !! test @@ -11989,14 +12041,14 @@ some <h3><span class="mw-headline" id="here">here</span></h3> !! html/parsoid -<!-- comment --><meta typeof="mw:Includes/NoInclude" data-parsoid='{"src":"<noinclude>"}'/><!-- comment --><meta typeof="mw:Includes/NoInclude/End" data-parsoid='{"src":"</noinclude>"}'/><!-- comment --><h2> hu </h2> +<!-- comment --><meta typeof="mw:Includes/NoInclude" data-parsoid='{"src":"<noinclude>"}'/><!-- comment --><meta typeof="mw:Includes/NoInclude/End" data-parsoid='{"src":"</noinclude>"}'/><!-- comment --><h2 id="hu"> hu </h2> <meta typeof="mw:Includes/NoInclude" data-parsoid='{"src":"<noinclude>"}'/> <p>some</p> <meta typeof="mw:Includes/NoInclude/End" data-parsoid='{"src":"</noinclude>"}'/><ul><li> stuff</li> <li> here</li></ul> -<meta typeof="mw:Includes/IncludeOnly" data-parsoid='{"src":"<includeonly>can have stuff</includeonly>"}'/><meta typeof="mw:Includes/IncludeOnly/End" data-parsoid='{"src":""}'/><h3> here </h3> +<meta typeof="mw:Includes/IncludeOnly" data-parsoid='{"src":"<includeonly>can have stuff</includeonly>"}'/><meta typeof="mw:Includes/IncludeOnly/End" data-parsoid='{"src":""}'/><h3 id="here"> here </h3> !! end @@ -12520,6 +12572,8 @@ Preprocessor precedence 14: broken language converter in comment !! test Preprocessor precedence 15: broken brace markup in headings +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! options parsoid=wt2html !! wikitext @@ -12537,32 +12591,31 @@ __NOTOC__ __NOEDITSECTION__ ===6 foo-{bar 6=== 6 !! html/php+tidy -<h3><span class="mw-headline" id="1_foo.5Bbar_1">1 foo[bar 1</span></h3> +<h3><span id="1_foo.5Bbar_1"></span><span class="mw-headline" id="1_foo[bar_1">1 foo[bar 1</span></h3> <p>1</p> -<h3><span class="mw-headline" id="2_foo.5B.5Bbar_2">2 foo[[bar 2</span></h3> +<h3><span id="2_foo.5B.5Bbar_2"></span><span class="mw-headline" id="2_foo[[bar_2">2 foo[[bar 2</span></h3> <p>2</p> -<h3><span class="mw-headline" id="3_foo.7Bbar_3">3 foo{bar 3</span></h3> +<h3><span id="3_foo.7Bbar_3"></span><span class="mw-headline" id="3_foo{bar_3">3 foo{bar 3</span></h3> <p>3</p> -<h3><span class="mw-headline" id="4_foo.7B.7Bbar_4">4 foo{{bar 4</span></h3> +<h3><span id="4_foo.7B.7Bbar_4"></span><span class="mw-headline" id="4_foo{{bar_4">4 foo{{bar 4</span></h3> <p>4</p> -<h3><span class="mw-headline" id="5_foo.7B.7B.7Bbar_5">5 foo{{{bar 5</span></h3> +<h3><span id="5_foo.7B.7B.7Bbar_5"></span><span class="mw-headline" id="5_foo{{{bar_5">5 foo{{{bar 5</span></h3> <p>5</p> -<h3><span class="mw-headline" id="6_foo-.7Bbar_6">6 foo-{bar 6</span></h3> +<h3><span id="6_foo-.7Bbar_6"></span><span class="mw-headline" id="6_foo-{bar_6">6 foo-{bar 6</span></h3> <p>6</p> !! html/parsoid -<meta property="mw:PageProp/notoc"/> <meta property="mw:PageProp/noeditsection"/ -> -<h3>1 foo[bar 1</h3> +<meta property="mw:PageProp/notoc"/> <meta property="mw:PageProp/noeditsection"/> +<h3 id="1_foo[bar_1"><span id="1_foo.5Bbar_1" typeof="mw:FallbackId"></span>1 foo[bar 1</h3> <p>1</p> -<h3>2 foo[[bar 2</h3> +<h3 id="2_foo[[bar_2"><span id="2_foo.5B.5Bbar_2" typeof="mw:FallbackId"></span>2 foo[[bar 2</h3> <p>2</p> -<h3>3 foo{bar 3</h3> +<h3 id="3_foo{bar_3"><span id="3_foo.7Bbar_3" typeof="mw:FallbackId"></span>3 foo{bar 3</h3> <p>3</p> -<h3>4 foo{{bar 4</h3> +<h3 id="4_foo{{bar_4"><span id="4_foo.7B.7Bbar_4" typeof="mw:FallbackId"></span>4 foo{{bar 4</h3> <p>4</p> -<h3>5 foo{{{bar 5</h3> +<h3 id="5_foo{{{bar_5"><span id="5_foo.7B.7B.7Bbar_5" typeof="mw:FallbackId"></span>5 foo{{{bar 5</h3> <p>5</p> -<h3>6 foo-{bar 6</h3> +<h3 id="6_foo-{bar_6"><span id="6_foo-.7Bbar_6" typeof="mw:FallbackId"></span>6 foo-{bar 6</h3> <p>6</p> !! end @@ -14264,15 +14317,15 @@ parsoid=wt2html,wt2wt,html2html <p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test -Serialize simple image with figure-inline wrapper +Serialize simple image with span wrapper !! options parsoid=html2wt !! html/parsoid -<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> +<p><span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> !! wikitext [[File:Foobar.jpg]] !! end @@ -14285,7 +14338,7 @@ Simple image (using File: namespace, now canonical) <p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -14402,7 +14455,7 @@ Linktrails should not work for images: [[File:Foobar.jpg]]s <p>Linktrails should not work for images: <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>s </p> !! html/parsoid -<p>Linktrails should not work for images: <span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span>s</p> +<p>Linktrails should not work for images: <figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline>s</p> !! end !! test @@ -14448,7 +14501,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" width="50" height="6" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/75px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/100px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image mw:ExpandedAttrs" about="#mwt2" data-parsoid='{"optList":[{"ck":"width","ak":"{{echo|50px}}"}]}' data-mw='{"attribs":[["width",{"html":"<span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid='{\"pi\":[[{\"k\":\"1\"}]],\"dsr\":[18,31,null,null]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"50px\"}},\"i\":0}}]}'>50px</span>"}]]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline typeof="mw:Image mw:ExpandedAttrs" about="#mwt2" data-parsoid='{"optList":[{"ck":"width","ak":"{{echo|50px}}"}]}' data-mw='{"attribs":[["width",{"html":"<span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid='{\"pi\":[[{\"k\":\"1\"}]],\"dsr\":[18,31,null,null]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"50px\"}},\"i\":0}}]}'>50px</span>"}]]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end ## Parsoid does not provide editing support for images where templates produce multiple image attributes. @@ -14492,7 +14545,7 @@ thumbsize=220 </div> <p>456</p> !! html/parsoid -<p>123<span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span>456</p> +<p>123<figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline>456</p> <p>123</p><figure class="mw-default-size mw-halign-right" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure><p>456</p> <p>123</p><figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></figure><p>456</p> !! end @@ -14516,7 +14569,7 @@ Image with multiple widths -- use last <p><a href="/wiki/File:Foobar.jpg" class="image" title="caption"><img alt="caption" src="http://example.com/images/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg" width="300" height="34" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/450px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/600px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="34" width="300"/></a></span></p> +<p><figure-inline typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="34" width="300"/></a></figure-inline></p> !! end !! test @@ -14533,7 +14586,7 @@ thumbsize=220 </p> !! html/parsoid <figure class="mw-default-size mw-halign-left" typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a><figcaption>caption</figcaption></figure> -<p><span class="mw-default-size mw-valign-middle" typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size mw-valign-middle" typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -14566,7 +14619,7 @@ parsoid=wt2html,wt2wt,html2html <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/177px-Foobar.jpg" width="177" height="20" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/265px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/353px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></span> <span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/177px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="20" width="177"/></a></span></p> +<p><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></figure-inline> <figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/177px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="20" width="177"/></a></figure-inline></p> !! end !! test @@ -14577,7 +14630,7 @@ Image with link parameter, wiki target <p><a href="/wiki/Main_Page" title="Main Page"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end # parsoid T51293 (part 1) @@ -14589,7 +14642,7 @@ Image with link parameter, URL target <p><a href="http://example.com/" rel="nofollow"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><a href="http://example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="http://example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end # parsoid T51293 (part 2) @@ -14601,7 +14654,7 @@ Image with link parameter, protocol-less URL target <p><a href="//example.com/" rel="nofollow"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><a href="//example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><a href="//example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -14673,7 +14726,7 @@ Image with empty link parameter <p><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image"><span><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></span></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image"><span><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></span></figure-inline></p> !! end !! test @@ -14684,7 +14737,7 @@ Image with link parameter (wiki target) and unnamed parameter <p><a href="/wiki/Main_Page" title="Title"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"Title"}'><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"Title"}'><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -14695,7 +14748,7 @@ Image with link parameter (URL target) and unnamed parameter <p><a href="http://example.com/" title="Title" rel="nofollow"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"Title"}'><a href="http://example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"Title"}'><a href="http://example.com/"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -14818,9 +14871,9 @@ Image with wiki markup in implicit alt </p><p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="testing bold in alt" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"testing '''bold''' in alt"}]}' data-mw='{"caption":"testing <b data-parsoid='{\"dsr\":[27,37,3,3]}'>bold</b> in alt"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"Image:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"testing '''bold''' in alt"}]}' data-mw='{"caption":"testing <b data-parsoid='{\"dsr\":[27,37,3,3]}'>bold</b> in alt"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"Image:Foobar.jpg"}}'/></a></figure-inline></p> -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"alt","ak":"alt=testing '''bold''' in alt"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img alt="testing bold in alt" resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"alt":"testing bold in alt","resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"alt":"alt=testing '''bold''' in alt","resource":"Image:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"alt","ak":"alt=testing '''bold''' in alt"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img alt="testing bold in alt" resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"alt":"testing bold in alt","resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"alt":"alt=testing '''bold''' in alt","resource":"Image:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -14913,9 +14966,9 @@ parsoid=wt2html,wt2wt,html2html </p><p><a href="/wiki/File:Foobar.jpg" class="image" title="caption"><img alt="caption" src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" width="180" height="20" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></span></p> -<p><span class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></span></p> -<p><span class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></figure-inline></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></figure-inline></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></figure-inline></p> !! end !! test @@ -14980,8 +15033,8 @@ parsoid=wt2html,wt2wt,html2html </p><p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="2000" height="227" class="thumbborder" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="227" width="2000"/></a></span></p> -<p><span class="mw-image-border" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="227" width="2000"/></a></span></p> +<p><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="227" width="2000"/></a></figure-inline></p> +<p><figure-inline class="mw-image-border" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="227" width="2000"/></a></figure-inline></p> !! end !! test @@ -14997,8 +15050,8 @@ parsoid=wt2html,wt2wt,html2html </p><p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg" width="1000" height="113" class="thumbborder" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/1500px-Foobar.jpg 1.5x, http://example.com/images/3/3a/Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="113" width="1000"/></a></span></p> -<p><span class="mw-image-border" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="113" width="1000"/></a></span></p> +<p><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="113" width="1000"/></a></figure-inline></p> +<p><figure-inline class="mw-image-border" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="113" width="1000"/></a></figure-inline></p> !! end !! test @@ -15041,7 +15094,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" width="50" height="6" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/75px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/100px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image/Frameless"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></span></p> +<p><figure-inline typeof="mw:Image/Frameless"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></figure-inline></p> !! end !! test @@ -15057,8 +15110,8 @@ parsoid=wt2html,wt2wt,html2html </p><p><a href="/wiki/File:Foobar.svg" class="image"><img alt="Foobar.svg" src="http://example.com/images/thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png" width="2000" height="1500" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/3000px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/4000px-Foobar.svg.png 2x" /></a> </p> !! html/parsoid -<p><span typeof="mw:Image/Frameless"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> -<p><span typeof="mw:Image/Frameless"><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png" data-file-width="240" data-file-height="180" data-file-type="drawing" height="1500" width="2000"/></a></span></p> +<p><figure-inline typeof="mw:Image/Frameless"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> +<p><figure-inline typeof="mw:Image/Frameless"><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png" data-file-width="240" data-file-height="180" data-file-type="drawing" height="1500" width="2000"/></a></figure-inline></p> !! end !! test @@ -15116,7 +15169,7 @@ Frameless image caption with a free URL <p><a href="/wiki/File:Foobar.jpg" class="image" title="http://example.com"><img alt="http://example.com" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"http://example.com"}]}' data-mw='{"caption":"<a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"stx\":\"url\",\"dsr\":[18,36,0,0]}'>http://example.com</a>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"http://example.com"}]}' data-mw='{"caption":"<a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"stx\":\"url\",\"dsr\":[18,36,0,0]}'>http://example.com</a>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15226,7 +15279,7 @@ T2648: Frameless image caption with a link <p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink\" href=\"./Link\" title=\"Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Link\"},\"sa\":{\"href\":\"link\"},\"dsr\":[30,38,2,2]}'>link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink\" href=\"./Link\" title=\"Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Link\"},\"sa\":{\"href\":\"link\"},\"dsr\":[30,38,2,2]}'>link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15237,7 +15290,7 @@ T2648: Frameless image caption with a link (suffix) <p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a linkfoo in it"><img alt="text with a linkfoo in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[link]]foo in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink\" href=\"./Link\" title=\"Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Link\"},\"sa\":{\"href\":\"link\"},\"dsr\":[30,41,2,5],\"tail\":\"foo\"}'>linkfoo</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[link]]foo in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink\" href=\"./Link\" title=\"Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Link\"},\"sa\":{\"href\":\"link\"},\"dsr\":[30,41,2,5],\"tail\":\"foo\"}'>linkfoo</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15248,7 +15301,7 @@ T2648: Frameless image caption with an interwiki link <p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a MeatBall:Link in it"><img alt="text with a MeatBall:Link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[MeatBall:Link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:ExtLink\" href=\"http://www.usemod.com/cgi-bin/mb.pl?Link\" title=\"meatball:Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"http://www.usemod.com/cgi-bin/mb.pl?Link\"},\"sa\":{\"href\":\"MeatBall:Link\"},\"isIW\":true,\"dsr\":[30,47,2,2]}'>MeatBall:Link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[MeatBall:Link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink/Interwiki\" href=\"http://www.usemod.com/cgi-bin/mb.pl?Link\" title=\"meatball:Link\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"http://www.usemod.com/cgi-bin/mb.pl?Link\"},\"sa\":{\"href\":\"MeatBall:Link\"},\"isIW\":true,\"dsr\":[30,47,2,2]}'>MeatBall:Link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15259,7 +15312,7 @@ T2648: Frameless image caption with a piped interwiki link <p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[MeatBall:Link|link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:ExtLink\" href=\"http://www.usemod.com/cgi-bin/mb.pl?Link\" title=\"meatball:Link\" data-parsoid='{\"stx\":\"piped\",\"a\":{\"href\":\"http://www.usemod.com/cgi-bin/mb.pl?Link\"},\"sa\":{\"href\":\"MeatBall:Link\"},\"isIW\":true,\"dsr\":[30,52,16,2]}'>link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"text with a [[MeatBall:Link|link]] in it"}]}' data-mw='{"caption":"text with a <a rel=\"mw:WikiLink/Interwiki\" href=\"http://www.usemod.com/cgi-bin/mb.pl?Link\" title=\"meatball:Link\" data-parsoid='{\"stx\":\"piped\",\"a\":{\"href\":\"http://www.usemod.com/cgi-bin/mb.pl?Link\"},\"sa\":{\"href\":\"MeatBall:Link\"},\"isIW\":true,\"dsr\":[30,52,16,2]}'>link</a> in it"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15267,7 +15320,7 @@ T107474: Frameless image caption with <nowiki> !! wikitext [[File:Foobar.jpg|<nowiki>text with a [[MeatBall:Link|link]] in it</nowiki>]] !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<nowiki>text with a [[MeatBall:Link|link]] in it</nowiki>"}]}' data-mw='{"caption":"<span typeof=\"mw:Nowiki\" data-parsoid='{\"dsr\":[18,75,8,9]}'>text with a [[MeatBall:Link|link]] in it</span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<nowiki>text with a [[MeatBall:Link|link]] in it</nowiki>"}]}' data-mw='{"caption":"<span typeof=\"mw:Nowiki\" data-parsoid='{\"dsr\":[18,75,8,9]}'>text with a [[MeatBall:Link|link]] in it</span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15278,7 +15331,7 @@ Escape HTML special chars in image alt text <p><a href="/wiki/File:Foobar.jpg" class="image" title="& < > ""><img alt="& < > "" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"& < > \""}]}' data-mw='{"caption":"&amp; &lt; > \""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"& < > \""}]}' data-mw='{"caption":"&amp; &lt; > \""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15291,7 +15344,7 @@ language=zh <p><a href="/wiki/File:Foobar.jpg" class="image" title="& < > ""><img alt="& < > "" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"& < > \""}]}' data-mw='{"caption":"&amp; &lt; > \""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"& < > \""}]}' data-mw='{"caption":"&amp; &lt; > \""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15302,7 +15355,7 @@ Entities in file name and attributes <p><a href="/index.php?title=Special:Upload&wpDestFile=7%25_solution.gif" class="new" title="File:7% solution.gif">7% solution</a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"bogus","ak":"manualthumb=7%25 solution.gif"},{"ck":"link","ak":"link=7%25 solution"},{"ck":"caption","ak":"[[7%25 solution]]"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<a rel=\"mw:WikiLink\" href=\"./7%25_solution\" title=\"7% solution\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./7%25_solution\"},\"sa\":{\"href\":\"7%25 solution\"},\"dsr\":[74,91,2,2]}'>7% solution</a>"}'><a href="./7%25_solution" data-parsoid='{"a":{"href":"./7%25_solution"},"sa":{"href":"link=7%25 solution"}}'><img resource="./File:7%25_solution.gif" src="./Special:FilePath/7%25_solution.gif" height="220" width="220" data-parsoid='{"a":{"resource":"./File:7%25_solution.gif","height":"220","width":"220"},"sa":{"resource":"File:7%25 solution.gif"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"bogus","ak":"manualthumb=7%25 solution.gif"},{"ck":"link","ak":"link=7%25 solution"},{"ck":"caption","ak":"[[7%25 solution]]"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<a rel=\"mw:WikiLink\" href=\"./7%25_solution\" title=\"7% solution\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./7%25_solution\"},\"sa\":{\"href\":\"7%25 solution\"},\"dsr\":[74,91,2,2]}'>7% solution</a>"}'><a href="./7%25_solution" data-parsoid='{"a":{"href":"./7%25_solution"},"sa":{"href":"link=7%25 solution"}}'><img resource="./File:7%25_solution.gif" src="./Special:FilePath/7%25_solution.gif" height="220" width="220" data-parsoid='{"a":{"resource":"./File:7%25_solution.gif","height":"220","width":"220"},"sa":{"resource":"File:7%25 solution.gif"}}'/></a></figure-inline></p> !! end !! test @@ -15313,7 +15366,7 @@ T2499: Alt text should have Ӓ, not &1234; <p><a href="/wiki/File:Foobar.jpg" class="image" title="♀"><img alt="♀" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"&#9792;"}]}' data-mw='{"caption":"<span typeof=\"mw:Entity\" data-parsoid='{\"src\":\"&amp;#9792;\",\"srcContent\":\"♀\",\"dsr\":[18,25,null,null]}'>♀</span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"&#9792;"}]}' data-mw='{"caption":"<span typeof=\"mw:Entity\" data-parsoid='{\"src\":\"&amp;#9792;\",\"srcContent\":\"♀\",\"dsr\":[18,25,null,null]}'>♀</span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15337,7 +15390,7 @@ Image caption containing another image <div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" width="180" height="20" class="thumbimage" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"></a></div>This is a caption with another <a href="/wiki/File:Thumb.png" class="image" title="image"><img alt="image" src="http://example.com/images/e/ea/Thumb.png" width="135" height="135" /></a> inside it!</div></div></div> !! html/parsoid -<figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a><figcaption>This is a caption with another <span class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"image"}'><a href="./File:Thumb.png"><img resource="./File:Thumb.png" src="//example.com/images/e/ea/Thumb.png" data-file-width="135" data-file-height="135" data-file-type="bitmap" height="135" width="135"/></a></span> inside it!</figcaption></figure> +<figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a><figcaption>This is a caption with another <figure-inline class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"image"}'><a href="./File:Thumb.png"><img resource="./File:Thumb.png" src="//example.com/images/e/ea/Thumb.png" data-file-width="135" data-file-height="135" data-file-type="bitmap" height="135" width="135"/></a></figure-inline> inside it!</figcaption></figure> !! end !! test @@ -15349,7 +15402,7 @@ Image: caption containing a newline <p><a href="/wiki/File:Foobar.jpg" class="image" title="This *is some text"><img alt="This *is some text" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"This\n*is some text"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"This\n*is some text"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !!end !!test @@ -15410,7 +15463,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="/wiki/File:Foobar.jpg" class="image" title="a"><img alt="a" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" class="b" /></a> </p> !! html/parsoid -<p><span class="mw-default-size b" typeof="mw:Image" data-mw='{"caption":"a"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><figure-inline class="mw-default-size b" typeof="mw:Image" data-mw='{"caption":"a"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !! end !! test @@ -15464,7 +15517,7 @@ parsoid=wt2html,wt2wt,html2html <p><a href="/wiki/File:Foobar.jpg" class="image" title="caption"><img alt="caption" src="http://example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" width="220" height="25" class="extra thumbborder" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/330px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/440px-Foobar.jpg 2x" /></a> </p> !! html/parsoid -<p><span class="mw-default-size mw-image-border extra" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size mw-image-border extra" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a></figure-inline></p> !! end # Note that 'right' is the default alignment, despite the misspelled 'righ' below @@ -15517,7 +15570,7 @@ wgEnableUploads=0 <p><a href="/wiki/File:Foobaz.jpg" title="File:Foobaz.jpg">File:Foobaz.jpg</a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Foobaz.jpg"><img resource="./File:Foobaz.jpg" src="./Special:FilePath/Foobaz.jpg" height="220" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Foobaz.jpg"><img resource="./File:Foobaz.jpg" src="./Special:FilePath/Foobaz.jpg" height="220" width="220"/></a></figure-inline></p> !! end # Parsoid-specific testing for images @@ -15532,7 +15585,7 @@ Parsoid-specific image handling - simple image with size and middle alignment !! wikitext [[File:Foobar.jpg|middle|50px]] !! html/parsoid -<p><span class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></span></p> +<p><figure-inline class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></figure-inline></p> !! end !! test @@ -15543,7 +15596,7 @@ parsoid=wt2wt,wt2html,html2html !! wikitext [[Image:Foobar.jpg|middle|50px]] !! html/parsoid -<p><span class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></span></p> +<p><figure-inline class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></figure-inline></p> !! end !! test @@ -15552,7 +15605,7 @@ Parsoid-specific image handling - simple image with size and middle alignment !! wikitext [[File:Foobar.jpg|50px|middle]] !! html/parsoid -<p><span class="mw-valign-middle" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"width","ak":"50px"},{"ck":"middle","ak":"middle"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-valign-middle" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"width","ak":"50px"},{"ck":"middle","ak":"middle"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15563,7 +15616,7 @@ parsoid=wt2html,wt2wt,html2html !! wikitext [[Image:Foobar.jpg|50px|middle]] !! html/parsoid -<p><span class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></span></p> +<p><figure-inline class="mw-valign-middle" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50"/></a></figure-inline></p> !! end !! test @@ -15571,7 +15624,7 @@ Parsoid-specific image handling - simple image with both sizes, a baseline align !! wikitext [[File:Foobar.jpg|500x10px|baseline|caption]] !! html/parsoid -<p><span class="mw-valign-baseline" typeof="mw:Image" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"width","ak":"500x10px"},{"ck":"baseline","ak":"baseline"},{"ck":"caption","ak":"caption"}],"size":"500x10"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/89px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="10" width="89" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"10","width":"89"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-valign-baseline" typeof="mw:Image" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"width","ak":"500x10px"},{"ck":"baseline","ak":"baseline"},{"ck":"caption","ak":"caption"}],"size":"500x10"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/89px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="10" width="89" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"10","width":"89"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15579,7 +15632,7 @@ Parsoid-specific image handling - simple image with border and size spec !! wikitext [[File:Foobar.jpg|50px|border|caption]] !! html/parsoid -<p><span class="mw-image-border" typeof="mw:Image" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"width","ak":"50px"},{"ck":"border","ak":"border"},{"ck":"caption","ak":"caption"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-image-border" typeof="mw:Image" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"width","ak":"50px"},{"ck":"border","ak":"border"},{"ck":"caption","ak":"caption"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/50px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="6" width="50" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"6","width":"50"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15643,7 +15696,7 @@ Parsoid-specific image handling - frameless image with specific size, border, an !! wikitext [[File:Foobar.jpg|frameless|442x50px|border|caption]] !! html/parsoid -<p><span class="mw-image-border" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"frameless","ak":"frameless"},{"ck":"width","ak":"442x50px"},{"ck":"border","ak":"border"},{"ck":"caption","ak":"caption"}],"size":"442x50"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/442px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="50" width="442" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"50","width":"442"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-image-border" typeof="mw:Image/Frameless" data-mw='{"caption":"caption"}' data-parsoid='{"optList":[{"ck":"frameless","ak":"frameless"},{"ck":"width","ak":"442x50px"},{"ck":"border","ak":"border"},{"ck":"caption","ak":"caption"}],"size":"442x50"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/442px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="50" width="442" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"50","width":"442"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15651,7 +15704,7 @@ Parsoid-specific image handling - simple image with a formatted caption !! wikitext [[File:Foobar.jpg|<table><tr><td>a</td><td>b</td></tr><tr><td>c</td></tr></table>]] !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<table><tr><td>a</td><td>b</td></tr><tr><td>c</td></tr></table>"}]}' data-mw='{"caption":"<table data-parsoid='{\"stx\":\"html\",\"dsr\":[18,81,7,8]}'><tbody data-parsoid='{\"dsr\":[25,73,0,0]}'><tr data-parsoid='{\"stx\":\"html\",\"dsr\":[25,54,4,5]}'><td data-parsoid='{\"stx\":\"html\",\"dsr\":[29,39,4,5]}'>a</td><td data-parsoid='{\"stx\":\"html\",\"dsr\":[39,49,4,5]}'>b</td></tr><tr data-parsoid='{\"stx\":\"html\",\"dsr\":[54,73,4,5]}'><td data-parsoid='{\"stx\":\"html\",\"dsr\":[58,68,4,5]}'>c</td></tr></tbody></table>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"<table><tr><td>a</td><td>b</td></tr><tr><td>c</td></tr></table>"}]}' data-mw='{"caption":"<table data-parsoid='{\"stx\":\"html\",\"dsr\":[18,81,7,8]}'><tbody data-parsoid='{\"dsr\":[25,73,0,0]}'><tr data-parsoid='{\"stx\":\"html\",\"dsr\":[25,54,4,5]}'><td data-parsoid='{\"stx\":\"html\",\"dsr\":[29,39,4,5]}'>a</td><td data-parsoid='{\"stx\":\"html\",\"dsr\":[39,49,4,5]}'>b</td></tr><tr data-parsoid='{\"stx\":\"html\",\"dsr\":[54,73,4,5]}'><td data-parsoid='{\"stx\":\"html\",\"dsr\":[58,68,4,5]}'>c</td></tr></tbody></table>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -15721,7 +15774,7 @@ foo bar !! html/parsoid <p>foo -<span class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/f/ff/Foobar.svg" lang="de" data-file-width="240" data-file-height="180" data-file-type="drawing" height="180" width="240"/></a></span> +<figure-inline class="mw-default-size" typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/f/ff/Foobar.svg" lang="de" data-file-width="240" data-file-height="180" data-file-type="drawing" height="180" width="240"/></a></figure-inline> bar</p> !! end @@ -15745,7 +15798,7 @@ T93580: 2. <ref> inside inline images <references /> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"Undisplayed caption in inline image with ref: <ref>foo</ref>"}]}' data-mw='{"caption":"Undisplayed caption in inline image with ref: <span about=\"#mwt2\" class=\"mw-ref\" id=\"cite_ref-1\" rel=\"dc:references\" typeof=\"mw:Extension/ref\" data-parsoid='{\"dsr\":[64,78,5,6]}' data-mw='{\"name\":\"ref\",\"body\":{\"id\":\"mw-reference-text-cite_note-1\"},\"attrs\":{}}'><a href=\"./Main_Page#cite_note-1\" style=\"counter-reset: mw-Ref 1;\" data-parsoid=\"{}\"><span class=\"mw-reflink-text\" data-parsoid=\"{}\">[1]</span></a></span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{"href":"File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"Undisplayed caption in inline image with ref: <ref>foo</ref>"}]}' data-mw='{"caption":"Undisplayed caption in inline image with ref: <span about=\"#mwt2\" class=\"mw-ref\" id=\"cite_ref-1\" rel=\"dc:references\" typeof=\"mw:Extension/ref\" data-parsoid='{\"dsr\":[64,78,5,6]}' data-mw='{\"name\":\"ref\",\"body\":{\"id\":\"mw-reference-text-cite_note-1\"},\"attrs\":{}}'><a href=\"./Main_Page#cite_note-1\" style=\"counter-reset: mw-Ref 1;\" data-parsoid=\"{}\"><span class=\"mw-reflink-text\" data-parsoid=\"{}\">[1]</span></a></span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{"href":"File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> <ol class="mw-references references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><a href="./Main_Page#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo</span></li></ol> !! end @@ -15757,7 +15810,7 @@ T93580: 3. Templated <ref> inside inline images <references /> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"Undisplayed caption in inline image with ref: {{echo|<ref>{{echo|foo}}</ref>}}"}]}' data-mw='{"caption":"Undisplayed caption in inline image with ref: <span about=\"#mwt2\" class=\"mw-ref\" id=\"cite_ref-1\" rel=\"dc:references\" typeof=\"mw:Transclusion mw:Extension/ref\" data-parsoid='{\"dsr\":[64,96,null,null],\"pi\":[[{\"k\":\"1\"}]]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"&lt;ref>{{echo|foo}}&lt;/ref>\"}},\"i\":0}}]}'><a href=\"./Main_Page#cite_note-1\" style=\"counter-reset: mw-Ref 1;\" data-parsoid=\"{}\"><span class=\"mw-reflink-text\" data-parsoid=\"{}\">[1]</span></a></span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{"href":"File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"Undisplayed caption in inline image with ref: {{echo|<ref>{{echo|foo}}</ref>}}"}]}' data-mw='{"caption":"Undisplayed caption in inline image with ref: <span about=\"#mwt2\" class=\"mw-ref\" id=\"cite_ref-1\" rel=\"dc:references\" typeof=\"mw:Transclusion mw:Extension/ref\" data-parsoid='{\"dsr\":[64,96,null,null],\"pi\":[[{\"k\":\"1\"}]]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"&lt;ref>{{echo|foo}}&lt;/ref>\"}},\"i\":0}}]}'><a href=\"./Main_Page#cite_note-1\" style=\"counter-reset: mw-Ref 1;\" data-parsoid=\"{}\"><span class=\"mw-reflink-text\" data-parsoid=\"{}\">[1]</span></a></span>"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{"href":"File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> <ol class="mw-references references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><a href="./Main_Page#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo</span></li></ol> !! end @@ -16525,7 +16578,7 @@ Section headings with TOC Some text ===Another headline=== !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Headline_1"><span class="tocnumber">1</span> <span class="toctext">Headline 1</span></a> <ul> @@ -16568,7 +16621,7 @@ __FORCETOC__ == Headline 2 == == Headline == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Headline_2"><span class="tocnumber">1</span> <span class="toctext">Headline 2</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#Headline"><span class="tocnumber">2</span> <span class="toctext">Headline</span></a></li> @@ -16585,8 +16638,11 @@ __FORCETOC__ !! end # perl -e 'print "="x$_," Level $_ heading","="x$_,"\n" for 1..10' +# Parsoid html2wt direction adds <nowiki> for level 7 and up. !! test Handling of sections up to level 6 and beyond +!! options +parsoid=wt2html !! wikitext = Level 1 Heading= == Level 2 Heading== @@ -16598,8 +16654,8 @@ Handling of sections up to level 6 and beyond ======== Level 8 Heading======== ========= Level 9 Heading========= ========== Level 10 Heading========== -!! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +!! html/php +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Level_1_Heading"><span class="tocnumber">1</span> <span class="toctext">Level 1 Heading</span></a> <ul> @@ -16640,6 +16696,17 @@ Handling of sections up to level 6 and beyond <h6><span class="mw-headline" id=".3D.3D.3D_Level_9_Heading.3D.3D.3D">=== Level 9 Heading===</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=9" title="Edit section: === Level 9 Heading===">edit</a><span class="mw-editsection-bracket">]</span></span></h6> <h6><span class="mw-headline" id=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D">==== Level 10 Heading====</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=10" title="Edit section: ==== Level 10 Heading====">edit</a><span class="mw-editsection-bracket">]</span></span></h6> +!! html/parsoid +<h1 id="Level_1_Heading" data-parsoid='{}'> Level 1 Heading</h1> +<h2 id="Level_2_Heading" data-parsoid='{}'> Level 2 Heading</h2> +<h3 id="Level_3_Heading" data-parsoid='{}'> Level 3 Heading</h3> +<h4 id="Level_4_Heading" data-parsoid='{}'> Level 4 Heading</h4> +<h5 id="Level_5_Heading" data-parsoid='{}'> Level 5 Heading</h5> +<h6 id="Level_6_Heading" data-parsoid='{}'> Level 6 Heading</h6> +<h6 id="=_Level_7_Heading=" data-parsoid='{}'><span id=".3D_Level_7_Heading.3D" typeof="mw:FallbackId"></span>= Level 7 Heading=</h6> +<h6 id="==_Level_8_Heading==" data-parsoid='{}'><span id=".3D.3D_Level_8_Heading.3D.3D" typeof="mw:FallbackId"></span>== Level 8 Heading==</h6> +<h6 id="===_Level_9_Heading===" data-parsoid='{}'><span id=".3D.3D.3D_Level_9_Heading.3D.3D.3D" typeof="mw:FallbackId"></span>=== Level 9 Heading===</h6> +<h6 id="====_Level_10_Heading====" data-parsoid='{}'><span id=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D" typeof="mw:FallbackId"></span>==== Level 10 Heading====</h6> !! end !! test @@ -16652,7 +16719,7 @@ TOC regression (T11764) == title 2 == === title 2.1 === !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> <ul> @@ -16687,7 +16754,7 @@ TOC for heading containing <span id="..."></span> (T96153) __FORCETOC__ ==<span id="old-anchor"></span>New title== !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#New_title"><span class="tocnumber">1</span> <span class="toctext">New title</span></a></li> </ul> @@ -16709,7 +16776,7 @@ wgMaxTocLevel=3 == title 2 == === title 2.1 === !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> <ul> @@ -16745,7 +16812,7 @@ wgMaxTocLevel=3 ====Section 1.1.1.1==== ==Section 2== !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a> <ul> @@ -16838,7 +16905,7 @@ __TOC__ === title 1.1 === == title 2 == !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> <ul> @@ -16863,24 +16930,33 @@ http://example.com [[File:Foobar.jpg]] <p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a> <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a> <span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a> <figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !!end +# Parsoid doesn't wt2wt this cleanly because it adds <nowiki>s. !! test Short headings with trailing space should match behavior of Parser::doHeadings (T21910) +!! options +parsoid=wt2html,html2html !! wikitext === The line above must have a trailing space! === <!-- --> <!-- --> But just in case it doesn't... -!! html +!! html/php <h1><span class="mw-headline" id=".3D">=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: =">edit</a><span class="mw-editsection-bracket">]</span></span></h1> <p>The line above must have a trailing space! </p> <h1><span class="mw-headline" id=".3D_2">=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: =">edit</a><span class="mw-editsection-bracket">]</span></span></h1> <p>But just in case it doesn't... </p> +!! html/parsoid +<h1 id="="><span id=".3D" typeof="mw:FallbackId"></span>=</h1> +<p>The line above must have a trailing space!</p> +<h1 id="=_2"><span id=".3D_2" typeof="mw:FallbackId"></span>=</h1> <!-- +--> <!-- --> +<p>But just in case it doesn't...</p> !! end !! test @@ -16902,10 +16978,10 @@ section 4 == text " text == section 5 -!! html +!! html/php <p>The tooltips shall not show entities to the user (ie. be double escaped) </p> -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#text_.3E_text"><span class="tocnumber">1</span> <span class="toctext">text > text</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#text_.3C_text"><span class="tocnumber">2</span> <span class="toctext">text < text</span></a></li> @@ -16930,6 +17006,23 @@ section 5 <h2><span class="mw-headline" id="text_.22_text">text " text</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: text " text">edit</a><span class="mw-editsection-bracket">]</span></span></h2> <p>section 5 </p> +!! html/parsoid +<p>The tooltips shall not show entities to the user (ie. be double escaped)</p> + +<h2 id="text_>_text"><span id="text_.3E_text" typeof="mw:FallbackId"></span> text > text </h2> +<p>section 1</p> + +<h2 id="text_<_text"><span id="text_.3C_text" typeof="mw:FallbackId"></span> text < text </h2> +<p>section 2</p> + +<h2 id="text_&_text"><span id="text_.26_text" typeof="mw:FallbackId"></span> text & text </h2> +<p>section 3</p> + +<h2 id="text_'_text"><span id="text_.27_text" typeof="mw:FallbackId"></span> text ' text </h2> +<p>section 4</p> + +<h2 id='text_"_text'><span id="text_.22_text" typeof="mw:FallbackId"></span> text " text </h2> +<p>section 5</p> !! end !! test @@ -16961,10 +17054,10 @@ section 6 [[#Plus-Entity+between+Text]] [[#Underscore_between_Text]] [[#Underscore-Entity_between_Text]] -!! html +!! html/php <p>Id should not contain + for spaces </p> -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Space_between_Text"><span class="tocnumber">1</span> <span class="toctext">Space between Text</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#Space-Entity_between_Text"><span class="tocnumber">2</span> <span class="toctext">Space-Entity between Text</span></a></li> @@ -16999,18 +17092,48 @@ section 6 <a href="#Underscore_between_Text">#Underscore_between_Text</a> <a href="#Underscore-Entity_between_Text">#Underscore-Entity_between_Text</a> </p> +!! html/parsoid +<p>Id should not contain + for spaces</p> + +<h2 id="Space_between_Text"> Space between Text </h2> +<p>section 1</p> + +<h2 id="Space-Entity_between_Text"> Space-Entity<span typeof="mw:Entity" data-parsoid='{"src":"&#32;","srcContent":" "}'> </span>between<span typeof="mw:Entity" data-parsoid='{"src":"&#32;","srcContent":" "}'> </span>Text </h2> +<p>section 2</p> + +<h2 id="Plus+between+Text"><span id="Plus.2Bbetween.2BText" typeof="mw:FallbackId"></span> Plus+between+Text </h2> +<p>section 3</p> + +<h2 id="Plus-Entity+between+Text"><span id="Plus-Entity.2Bbetween.2BText" typeof="mw:FallbackId"></span> Plus-Entity<span typeof="mw:Entity" data-parsoid='{"src":"&#43;","srcContent":"+"}'>+</span>between<span typeof="mw:Entity" data-parsoid='{"src":"&#43;","srcContent":"+"}'>+</span>Text </h2> +<p>section 4</p> + +<h2 id="Underscore_between_Text"> Underscore_between_Text </h2> +<p>section 5</p> + +<h2 id="Underscore-Entity_between_Text"> Underscore-Entity<span typeof="mw:Entity" data-parsoid='{"src":"&#95;","srcContent":"_"}'>_</span>between<span typeof="mw:Entity" data-parsoid='{"src":"&#95;","srcContent":"_"}'>_</span>Text </h2> +<p>section 6</p> + +<p><a rel="mw:WikiLink" href="./Main_Page#Space_between_Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Space_between_Text"},"sa":{"href":"#Space between Text"}}'>#Space between Text</a> +<a rel="mw:WikiLink" href="./Main_Page#Space-Entity_between_Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Space-Entity_between_Text"},"sa":{"href":"#Space-Entity&#32;between&#32;Text"}}'>#Space-Entity between Text</a> +<a rel="mw:WikiLink" href="./Main_Page#Plus+between+Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Plus+between+Text"},"sa":{"href":"#Plus+between+Text"}}'>#Plus+between+Text</a> +<a rel="mw:WikiLink" href="./Main_Page#Plus-Entity+between+Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Plus-Entity+between+Text"},"sa":{"href":"#Plus-Entity&#43;between&#43;Text"}}'>#Plus-Entity+between+Text</a> +<a rel="mw:WikiLink" href="./Main_Page#Underscore_between_Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Underscore_between_Text"},"sa":{"href":"#Underscore_between_Text"}}'>#Underscore_between_Text</a> +<a rel="mw:WikiLink" href="./Main_Page#Underscore-Entity_between_Text" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Underscore-Entity_between_Text"},"sa":{"href":"#Underscore-Entity&#95;between&#95;Text"}}'>#Underscore-Entity_between_Text</a></p> !! end +# Parsoid html2wt disabled because it adds padding spaces around = !! test Headers with excess '=' characters (Are similar tests necessary beyond the 1st level?) +!! options +parsoid=wt2html,wt2wt,html2html !! wikitext =foo== ==foo= =''italic'' heading== ==''italic'' heading= -!! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +!! html/php +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#foo.3D"><span class="tocnumber">1</span> <span class="toctext">foo=</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#.3Dfoo"><span class="tocnumber">2</span> <span class="toctext">=foo</span></a></li> @@ -17024,6 +17147,11 @@ Headers with excess '=' characters <h1><span class="mw-headline" id="italic_heading.3D"><i>italic</i> heading=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: italic heading=">edit</a><span class="mw-editsection-bracket">]</span></span></h1> <h1><span class="mw-headline" id=".3Ditalic_heading">=<i>italic</i> heading</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: =italic heading">edit</a><span class="mw-editsection-bracket">]</span></span></h1> +!! html/parsoid +<h1 id="foo="><span id="foo.3D" typeof="mw:FallbackId"></span>foo=</h1> +<h1 id="=foo"><span id=".3Dfoo" typeof="mw:FallbackId"></span>=foo</h1> +<h1 id="italic_heading="><span id="italic_heading.3D" typeof="mw:FallbackId"></span><i>italic</i> heading=</h1> +<h1 id="=italic_heading"><span id=".3Ditalic_heading" typeof="mw:FallbackId"></span>=<i>italic</i> heading</h1> !! end !! test @@ -17039,8 +17167,8 @@ HTML headers vs TOC (T25393) == Header 2.1 == == Header 2.2 == __NOEDITSECTION__ -!! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +!! html/php +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1"><a href="#Header_1"><span class="tocnumber">1</span> <span class="toctext">Header 1</span></a> <ul> @@ -17064,6 +17192,16 @@ __NOEDITSECTION__ <h2><span class="mw-headline" id="Header_2.1">Header 2.1</span></h2> <h2><span class="mw-headline" id="Header_2.2">Header 2.2</span></h2> +!! html/parsoid +<h1 id="Header_1" data-parsoid='{"stx":"html"}'>Header 1</h1> +<h2 id="Header_1.1" data-parsoid='{}'> Header 1.1 </h2> +<h2 id="Header_1.2" data-parsoid='{}'> Header 1.2 </h2> + +<h1 id="Header_2" data-parsoid='{"stx":"html"}'>Header 2 +</h1> +<h2 id="Header_2.1" data-parsoid='{}'> Header 2.1 </h2> +<h2 id="Header_2.2" data-parsoid='{}'> Header 2.2 </h2> +<meta property="mw:PageProp/noeditsection"/> !! end !! test @@ -17076,11 +17214,17 @@ parsoid=wt2html,wt2wt ==baz==<!-- c2 c3--> -!! html -<h2><span class="mw-headline" id="foo">foo</span></h2> -<h2><span class="mw-headline" id="bar">bar</span></h2> -<h2><span class="mw-headline" id="baz">baz</span></h2> +!! html/php +<h2><span class="mw-headline" id="foo">foo</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: foo">edit</a><span class="mw-editsection-bracket">]</span></span></h2> +<h2><span class="mw-headline" id="bar">bar</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: bar">edit</a><span class="mw-editsection-bracket">]</span></span></h2> +<h2><span class="mw-headline" id="baz">baz</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: baz">edit</a><span class="mw-editsection-bracket">]</span></span></h2> +!! html/parsoid +<h2 id="foo">foo</h2><!----> +<h2 id="bar">bar</h2><!--c1--> +<h2 id="baz">baz</h2><!-- +c2 +c3--> !! end !! test @@ -17091,7 +17235,7 @@ http://example.com[[File:Foobar.jpg]] <p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a><span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></span></p> +<p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a><figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></a></figure-inline></p> !!end !! test @@ -17187,15 +17331,17 @@ parsoid=wt2html,html2html !! test div with multiple empty attribute values +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! options parsoid=wt2html,html2html !! wikitext <div id= title=>HTML rocks</div> !! html/php -<div id="title.3D">HTML rocks</div> +<div id="title=">HTML rocks</div> !! html/parsoid -<div id="title.3D" data-parsoid='{"stx":"html"}'>HTML rocks</div> +<div id="title=" data-parsoid='{"stx":"html"}'>HTML rocks</div> !! end !! test @@ -17521,7 +17667,7 @@ Image link to nonexistent file (T3850 - good) <p><a href="/index.php?title=Special:Upload&wpDestFile=No_such.jpg" class="new" title="File:No such.jpg">File:No such.jpg</a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:No_such.jpg"><img resource="./File:No_such.jpg" src="./Special:FilePath/No_such.jpg" height="220" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:No_such.jpg"><img resource="./File:No_such.jpg" src="./Special:FilePath/No_such.jpg" height="220" width="220"/></a></figure-inline></p> !! end !! test @@ -17773,9 +17919,11 @@ T4304: HTML attribute safety (unsafe breakout parameter 2; 2309) T4304: HTML attribute safety (link) !! wikitext <div title="[[Main Page]]"></div> -!! html +!! html/php <div title="[[Main Page]]"></div> +!! html/parsoid +<div title="[[Main Page]]"></div> !! end !! test @@ -17836,9 +17984,11 @@ T4304: HTML attribute safety (web link) T4304: HTML attribute safety (named web link) !! wikitext <div title="[http://example.com/ link]"></div> -!! html +!! html/php <div title="[http://example.com/ link]"></div> +!! html/parsoid +<div title="[http://example.com/ link]"></div> !! end !! test @@ -18447,13 +18597,26 @@ Table not started</td></tr></table> !! test Sanitizer: Escaping of spaces, multibyte characters, colons & other stuff in id="" +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext <span id="æ: v">byte</span>[[#æ: v|backlink]] !! html/php -<p><span id=".C3.A6:_v">byte</span><a href="#.C3.A6:_v">backlink</a> +<p><span id="æ:_v">byte</span><a href="#æ:_v">backlink</a> </p> !! html/parsoid -<p><span id=".C3.A6:_v" data-parsoid='{"stx":"html","a":{"id":".C3.A6:_v"},"sa":{"id":"æ: v"}}'>byte</span><a rel="mw:WikiLink" href="./Main_Page#.C3.A6:_v" data-parsoid='{"stx":"piped","a":{"href":"./Main_Page#.C3.A6:_v"},"sa":{"href":"#æ: v"}}'>backlink</a></p> +<p><span id="æ:_v" data-parsoid='{"stx":"html","a":{"id":"æ:_v"},"sa":{"id":"æ: v"}}'>byte</span><a rel="mw:WikiLink" href="./Main_Page#æ:_v" data-parsoid='{"stx":"piped","a":{"href":"./Main_Page#æ:_v"},"sa":{"href":"#æ: v"}}'>backlink</a></p> +!! end + +!! test +Sanitizer: Escaping of spaces, multibyte characters, colons & other stuff in id="" (legacy) +!! config +wgFragmentMode=[ 'legacy' ] +!! wikitext +<span id="æ: v">byte</span>[[#æ: v|backlink]] +!! html/php +<p><span id=".C3.A6:_v">byte</span><a href="#.C3.A6:_v">backlink</a> +</p> !! end # In HTML5, the restrictions are that id must contain at least one character, @@ -18516,6 +18679,37 @@ parsoid=wt2html,wt2wt <p><span style="margin: -0.125em -0.45em; rgba(255, 0, 0, 0.3)">2013</span></p> !! end +!! test +Sanitizer: Avoid unnecessary percent encoded characters in interwiki links +!! wikitext +[[meatball:Soft"Security]] +!! html/php +<p><a href="http://www.usemod.com/cgi-bin/mb.pl?Soft%22Security" class="extiw" title="meatball:Soft"Security">meatball:Soft"Security</a> +</p> +!! html/parsoid +<p><a rel="mw:WikiLink/Interwiki" href='http://www.usemod.com/cgi-bin/mb.pl?Soft"Security' title='meatball:Soft"Security'>meatball:Soft"Security</a></p> +!! end + +!! test +Sanitizer: angle brackets are invalid, even in interwiki links (T182338) +!! wikitext +[[meatball:Foo<Bar]] +[[meatball:Foo>Bar]] +[[meatball:Foo<bar]] +[[meatball:Foo>bar]] +!! html/php +<p>[[meatball:Foo<Bar]] +[[meatball:Foo>Bar]] +[[meatball:Foo<bar]] +[[meatball:Foo>bar]] +</p> +!! html/parsoid +<p>[[meatball:Foo<Bar]] +[[meatball:Foo>Bar]] +[[meatball:Foo<span typeof="mw:Entity" data-parsoid='{"src":"&lt;","srcContent":"<"}'><</span>bar]] +[[meatball:Foo<span typeof="mw:Entity" data-parsoid='{"src":"&gt;","srcContent":">"}'>></span>bar]]</p> +!! end + !! test Language converter: output gets cut off unexpectedly (T7757) !! options @@ -18877,25 +19071,28 @@ Fuzz testing: Parser13 !! end +# Note that Parsoid output differs from the PHP parser here: the PHP +# parser breaks the URL for the magic word, while in Parsoid the URL +# production takes precedence. !! test Fuzz testing: Parser14 !! wikitext == onmouseover= == http://__TOC__ -!! html +!! html/php <h2><span class="mw-headline" id="onmouseover.3D">onmouseover=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: onmouseover=">edit</a><span class="mw-editsection-bracket">]</span></span></h2> -http://<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +http://<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#onmouseover.3D"><span class="tocnumber">1</span> <span class="toctext">onmouseover=</span></a></li> </ul> </div> -!! html+tidy +!! html/php+tidy <h2><span class="mw-headline" id="onmouseover.3D">onmouseover=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: onmouseover=">edit</a><span class="mw-editsection-bracket">]</span></span></h2> <p>http://</p> <div id="toc" class="toc"> -<div class="toctitle"> +<div class="toctitle" lang="en" dir="ltr" xml:lang="en"> <h2>Contents</h2> </div> <ul> @@ -18903,6 +19100,9 @@ http://<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> </ul> </div> <p></p> +!! html/parsoid +<h2 id="onmouseover="><span id="onmouseover.3D" typeof="mw:FallbackId"></span> onmouseover= </h2> +<p><a rel="mw:ExtLink" href="http://__TOC__" data-parsoid='{"stx":"url"}'>http://__TOC__</a></p> !! end !! test @@ -18926,7 +19126,7 @@ parsoid=wt2html,html2html </tr> </table> !! html/parsoid -<h2>a</h2> +<h2 id="a">a</h2> <table style="__TOC__"></table> !! end @@ -19101,15 +19301,45 @@ Fuzz testing: image with bogus manual thumbnail <figure class="mw-default-size" typeof="mw:Error mw:Image/Thumb" data-parsoid='{"optList":[{"ck":"manualthumb","ak":"thumbnail= "}]}' data-mw='{"errors":[{"key":"apierror-invalidtitle","message":"Invalid thumbnail title.","params":{"name":""}}],"thumb":""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{"href":"Image:foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="./Special:FilePath/Foobar.jpg" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"220"},"sa":{"resource":"Image:foobar.jpg"}}'/></a></figure> !! end +# Parsoid will emit the newline literally in wt2wt; see next test case. !! test Fuzz testing: encoded newline in generated HTML replacements (T8577) +!! options +parsoid=wt2html !! wikitext <pre dir=" "></pre> !! html/php <pre dir=" "></pre> !! html/parsoid -<pre typeof="mw:Extension/pre" about="#mwt2" dir="&#10;" data-mw='{"name":"pre","attrs":{"dir":"&#10;"},"body":{"extsrc":""}}'></pre> +<pre typeof="mw:Extension/pre" about="#mwt2" dir=" +" data-mw='{"name":"pre","attrs":{"dir":"\n"},"body":{"extsrc":""}}'></pre> +!! end + +!! test +Fuzz testing: encoded newline in generated HTML replacements, html2wt (T8577) +!! options +parsoid=html2wt +!! html/parsoid +<pre typeof="mw:Extension/pre" about="#mwt2" dir=" +" data-mw='{"name":"pre","attrs":{"dir":"\n"},"body":{"extsrc":""}}'></pre> +!! wikitext +<pre dir=" +"></pre> +!! html/php +<pre dir=""></pre> + +!! end + +!! test +Templates in extension attributes are not expanded +!! wikitext +<pre dir="{{echo|ltr}}"></pre> +!! html/php +<pre dir="{{echo|ltr}}"></pre> + +!! html/parsoid +<pre typeof="mw:Extension/pre" about="#mwt2" dir="{{echo|ltr}}" data-mw='{"name":"pre","attrs":{"dir":"{{echo|ltr}}"},"body":{"extsrc":""}}'></pre> !! end !! test @@ -20263,7 +20493,7 @@ File:File:Foobar.jpg !! html/parsoid <ul class="gallery mw-gallery-traditional" type="123" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{"type":"123","summary":"345"},"body":{"extsrc":"\nFile:File:Foobar.jpg\n"}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:File:Foobar.jpg"><img resource="./File:File:Foobar.jpg" src="./Special:FilePath/File:Foobar.jpg" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:File:Foobar.jpg"><img resource="./File:File:Foobar.jpg" src="./Special:FilePath/File:Foobar.jpg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20326,12 +20556,12 @@ image4 |300px| centre !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt3" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Image1.png"><img resource="./File:Image1.png" src="./Special:FilePath/Image1.png" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Image2.gif"><img resource="./File:Image2.gif" src="./Special:FilePath/Image2.gif" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Image3"><img resource="./File:Image3" src="./Special:FilePath/Image3" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Image4"><img resource="./File:Image4" src="./Special:FilePath/Image4" height="300" width="300"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Image5.svg"><img resource="./File:Image5.svg" src="./Special:FilePath/Image5.svg" height="120" width="120"/></a></span></div><div class="gallerytext"> <a rel="mw:ExtLink" href="http://///////">http://///////</a></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:*_image6"><img resource="./File:*_image6" src="./Special:FilePath/*_image6" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Image1.png"><img resource="./File:Image1.png" src="./Special:FilePath/Image1.png" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Image2.gif"><img resource="./File:Image2.gif" src="./Special:FilePath/Image2.gif" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Image3"><img resource="./File:Image3" src="./Special:FilePath/Image3" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Image4"><img resource="./File:Image4" src="./Special:FilePath/Image4" height="300" width="300"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Image5.svg"><img resource="./File:Image5.svg" src="./Special:FilePath/Image5.svg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"> <a rel="mw:ExtLink" href="http://///////">http://///////</a></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:*_image6"><img resource="./File:*_image6" src="./Special:FilePath/*_image6" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20389,11 +20619,11 @@ image:foobar.jpg|Blabla|alt=This is a foo-bar.|blabla. !! html/parsoid <ul class="gallery mw-gallery-traditional" style="max-width: 226px; _width: 226px;" typeof="mw:Extension/gallery" about="#mwt3" data-mw='{"name":"gallery","attrs":{"widths":"70px","heights":"40px","perrow":"2"},"body":{}}'> <li class="gallerycaption">Foo <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></span></div><div class="gallerytext">caption</div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext">some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="This is a foo-bar." resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext">blabla.</div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></figure-inline></div><div class="gallerytext">caption</div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext">some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="This is a foo-bar." resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext">blabla.</div></li> </ul> !! end @@ -20450,11 +20680,11 @@ image:foobar.jpg|Blabla|alt=This is a foo-bar.|blabla. !! html/parsoid <ul class="gallery mw-gallery-traditional" style="max-width: 226px; _width: 226px;" typeof="mw:Extension/gallery" about="#mwt3" data-mw='{"name":"gallery","attrs":{"widths":"70px","heights":"40px","perrow":"2","caption":"Foo [[Main Page]]"},"body":{"extsrc":"\nFile:Nonexistent.jpg|caption\nFile:Nonexistent.jpg\nimage:foobar.jpg|some '''caption''' [[Main Page]]\nimage:foobar.jpg\nimage:foobar.jpg|Blabla|alt=This is a foo-bar.|blabla.\n"}}'> <li class="gallerycaption">Foo <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></span></div><div class="gallerytext">caption</div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext">some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="This is a foo-bar." resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></span></div><div class="gallerytext">blabla.</div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></figure-inline></div><div class="gallerytext">caption</div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="40" width="70"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext">some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 105px;"><div class="thumb" style="width: 100px; height: 70px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="This is a foo-bar." resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/70px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="8" width="70"/></a></figure-inline></div><div class="gallerytext">blabla.</div></li> </ul> !! end @@ -20494,9 +20724,9 @@ image:foobar.jpg|link=Main Page#section|caption !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./Main_Page#section"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./Main_Page#section"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext">caption</div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./Main_Page"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./Main_Page#section"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./Main_Page#section"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext">caption</div></li> </ul> !! end @@ -20526,7 +20756,7 @@ File:Foobar.jpg|{{echo|ho}} !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt6" data-mw='{"name":"gallery","attrs":{},"body":{}}'> <li class="gallerycaption"><span about="#mwt3" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"hi"}},"i":0}}]}'>hi</span></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"><span about="#mwt5" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"ho"}},"i":0}}]}'>ho</span></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><span about="#mwt5" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"ho"}},"i":0}}]}'>ho</span></div></li> </ul> !! end @@ -20561,8 +20791,8 @@ File:Foobar.jpg|alt=galleryalt|{{Test|unamedParam|alt=param}} !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt6" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"><span typeof="mw:Image" data-mw='{"caption":"desc"}'><a href="./File:Foobar.jpg"><img alt="inneralt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></span></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"><span about="#mwt4" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"Test","href":"./Template:Test"},"params":{"1":{"wt":"unamedParam"},"alt":{"wt":"param"}},"i":0}}]}'>This is a test template</span></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><figure-inline typeof="mw:Image" data-mw='{"caption":"desc"}'><a href="./File:Foobar.jpg"><img alt="inneralt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></figure-inline></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><span about="#mwt4" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"Test","href":"./Template:Test"},"params":{"1":{"wt":"unamedParam"},"alt":{"wt":"param"}},"i":0}}]}'>This is a test template</span></div></li> </ul> !! end @@ -20615,10 +20845,10 @@ some <b>caption</b> <a href="/wiki/Main_Page" title="Main Page">Main Page</a> !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt3" data-mw='{"name":"gallery","attrs":{"showfilename":""},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></span></div><div class="gallerytext"><a href="./File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">File:Nonexistent.jpg</a>caption</div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></span></div><div class="gallerytext"><a href="./File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">File:Nonexistent.jpg</a></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"><a href="./File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">File:Foobar.jpg</a>some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"><a href="./File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">File:Foobar.jpg</a></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"><a href="./File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">File:Nonexistent.jpg</a>caption</div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"><a href="./File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">File:Nonexistent.jpg</a></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><a href="./File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">File:Foobar.jpg</a>some <b>caption</b> <a rel="mw:WikiLink" href="./Main_Page" title="Main Page">Main Page</a></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><a href="./File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">File:Foobar.jpg</a></div></li> </ul> !! end @@ -20663,27 +20893,27 @@ foobar.jpg !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Error mw:Image"><a href="./File:Nonexistent.jpg"><img resource="./File:Nonexistent.jpg" src="./Special:FilePath/Nonexistent.jpg" height="120" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end !! test -Gallery override link with WikiLink (T36852) +Gallery override link with wikilink (T36852) !! options parsoid={ "nativeGallery": true } !! wikitext <gallery> -File:Foobar.jpg|alt=galleryalt|link=InterWikiLink +File:Foobar.jpg|alt=galleryalt|link=Wikilink </gallery> !! html/php <ul class="gallery mw-gallery-traditional"> <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/InterWikiLink"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div></div> + <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/Wikilink"><img alt="galleryalt" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div></div> <div class="gallerytext"> </div> </div></li> @@ -20691,7 +20921,7 @@ File:Foobar.jpg|alt=galleryalt|link=InterWikiLink !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-parsoid='{"dsr":[0,70,2,2]}' data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./InterWikiLink"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./Wikilink"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20716,7 +20946,7 @@ File:Foobar.jpg|alt=galleryalt|link=http://www.example.org !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="http://www.example.org"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="http://www.example.org"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20763,7 +20993,7 @@ File:Foobar.jpg|alt=galleryalt|link=" onclick="alert('malicious javascript code! !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./%22_onclick=%22alert('malicious_javascript_code!');"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./%22_onclick=%22alert('malicious_javascript_code!');"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20790,7 +21020,7 @@ File:Foobar.jpg|link=< !! html/parsoid <ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext">link=<</div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext">link=<</div></li> </ul> !! end @@ -20833,7 +21063,7 @@ File:Foobar.jpg !! html/parsoid <ul class="gallery mw-gallery-traditional center" style="text-align: center;" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{"class":"center","style":"text-align: center;"},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -20858,7 +21088,7 @@ File:Foobar.jpg !! html/parsoid <ul class="gallery mw-gallery-slideshow" data-showthumbnails="1" typeof="mw:Extension/gallery" about="#mwt2" data-mw='{"name":"gallery","attrs":{"mode":"slideshow","showthumbnails":""},"body":{}}'> -<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px;"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div><div class="gallerytext"></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"></div></li> </ul> !! end @@ -21120,7 +21350,7 @@ djvu <p><a href="/index.php?title=File:LoremIpsum.djvu&page=2" class="image"><img alt="LoremIpsum.djvu" src="http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-2480px-LoremIpsum.djvu.jpg" width="2480" height="3508" srcset="http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-3720px-LoremIpsum.djvu.jpg 1.5x, http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-4960px-LoremIpsum.djvu.jpg 2x" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"page","ak":"page=2"}]}' data-mw='{"page":"2"}'><a href="./File:LoremIpsum.djvu" data-parsoid='{"a":{"href":"./File:LoremIpsum.djvu"},"sa":{"href":"File:LoremIpsum.djvu"}}'><img resource="./File:LoremIpsum.djvu" src="//example.com/images/5/5f/LoremIpsum.djvu" data-file-width="2480" data-file-height="3508" data-file-type="bitmap" height="3508" width="2480" data-parsoid='{"a":{"resource":"./File:LoremIpsum.djvu","height":"3508","width":"2480"},"sa":{"resource":"File:LoremIpsum.djvu"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"page","ak":"page=2"}]}' data-mw='{"page":"2"}'><a href="./File:LoremIpsum.djvu" data-parsoid='{"a":{"href":"./File:LoremIpsum.djvu"},"sa":{"href":"File:LoremIpsum.djvu"}}'><img resource="./File:LoremIpsum.djvu" src="//example.com/images/5/5f/LoremIpsum.djvu" data-file-width="2480" data-file-height="3508" data-file-type="bitmap" height="3508" width="2480" data-parsoid='{"a":{"resource":"./File:LoremIpsum.djvu","height":"3508","width":"2480"},"sa":{"resource":"File:LoremIpsum.djvu"}}'/></a></figure-inline></p> !! end !! test @@ -21378,7 +21608,7 @@ Out-of-order TOC heading levels =====5===== ==2== !! html -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#2"><span class="tocnumber">1</span> <span class="toctext">2</span></a> <ul> @@ -21478,47 +21708,94 @@ ISBN 1 234 56789 0 - 2006 !! test anchorencode +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext {{anchorencode:foo bar©#%n}} -!! html +!! html/php +<p>foo_bar©#%n +</p> +!! html/parsoid +<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode:foo bar©#%n","function":"anchorencode"},"params":{},"i":0}}]}'>foo_bar©#%n</p> +!! end + +!! test +anchorencode (legacy) +!! config +wgFragmentMode=[ 'legacy' ] +!! wikitext +{{anchorencode:foo bar©#%n}} +!! html/php <p>foo_bar.C2.A9.23.25n </p> !! end !! test anchorencode trims spaces +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext {{anchorencode: __pretty__please__}} -!! html +!! html/php <p>pretty_please </p> +!! html/parsoid +<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode: __pretty__please__","function":"anchorencode"},"params":{},"i":0}}]}'>pretty_please</p> !! end !! test anchorencode deals with links +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext {{anchorencode: [[hello|world]] [[hi]]}} -!! html +!! html/php <p>world_hi </p> +!! html/parsoid +<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode: [[hello|world]] [[hi]]","function":"anchorencode"},"params":{},"i":0}}]}'>world_hi</p> !! end !! test anchorencode deals with templates +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! wikitext -{{anchorencode: {{Foo}} }} -!! html -<p>FOO +{{anchorencode: {{Foo}} x}} +!! html/php +<p>FOO_x </p> +!! html/parsoid +<p about="#mwt2" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode: {{Foo}} x","function":"anchorencode"},"params":{},"i":0}}]}'>FOO_x</p> !! end !! test anchorencode encodes like the TOC generator: (T20431) +!! config +wgFragmentMode=[ 'html5', 'legacy' ] +!! wikitext +=== _ +:.3A%3A _ &&]] x === +{{anchorencode: _ +:.3A%3A _ &&]] x}} +__NOEDITSECTION__ +!! html/php +<h3><span id=".2B:.3A.253A_.26.26.5D.5D_x"></span><span class="mw-headline" id="+:.3A%3A_&&]]_x">_ +:.3A%3A _ &&]] x</span></h3> +<p>+:.3A%3A_&&]]_x +</p> +!! html/parsoid +<h3 id="+:.3A%3A_&&]]_x"><span id=".2B:.3A.253A_.26.26.5D.5D_x" typeof="mw:FallbackId"></span> _ +:.3A%3A _ &<span typeof="mw:Entity" data-parsoid='{"src":"&amp;","srcContent":"&","dsr":[18,23,null,null]}'>&</span>]] x </h3> +<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode: _ +:.3A%3A _ &&amp;]] x","function":"anchorencode"},"params":{},"i":0}}]}'>+:.3A%3A_&&<span typeof="mw:Entity">]</span><span typeof="mw:Entity">]</span>_x</p> +<meta property="mw:PageProp/noeditsection"/> +!! end + +!! test +anchorencode encodes like the TOC generator: (T20431) (legacy) +!! config +wgFragmentMode=[ 'legacy' ] !! wikitext === _ +:.3A%3A&&]] === {{anchorencode: _ +:.3A%3A&&]] }} __NOEDITSECTION__ -!! html +!! html/php <h3><span class="mw-headline" id=".2B:.3A.253A.26.26.5D.5D">_ +:.3A%3A&&]]</span></h3> <p>.2B:.3A.253A.26.26.5D.5D </p> @@ -21794,6 +22071,8 @@ language=sr variant=sr-ec !! test -{}- tags within headlines (within html for parserConvert()) +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! options language=sr variant=sr-ec !! wikitext @@ -21804,14 +22083,14 @@ conversion: == Latinski == !! html/php -<h2><span class="mw-headline" id="-.7BNaslov.7D-">Naslov</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Уреди одељак „Naslov“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2> +<h2><span id="-.7BNaslov.7D-"></span><span class="mw-headline" id="-{Naslov}-">Naslov</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Уреди одељак „Naslov“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2> <p>Ноте тхат евен ан унпротецтед хеадлине ИД ис нот аффецтед бy лангуаге цонверсион: </p> <h2><span class="mw-headline" id="Latinski">Латински</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=2" title="Уреди одељак „Латински“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2> !! html/parsoid -<h2 id="-.7BNaslov.7D-"><span typeof="mw:LanguageVariant" data-mw-variant='{"disabled":{"t":"Naslov"}}'></span></h2> +<h2 id="-{Naslov}-"><span id="-.7BNaslov.7D-" typeof="mw:FallbackId"></span> <span typeof="mw:LanguageVariant" data-mw-variant='{"disabled":{"t":"Naslov"}}'></span> </h2> <p>Note that even an unprotected headline ID is not affected by language conversion:</p> @@ -22624,15 +22903,9 @@ File:foobar.jpg|{{Test|unamedParam|alt=-{R|param}-}}|alt=galleryalt </ul> !! html/parsoid -<ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" data-mw='{"name":"gallery","attrs":{},"body":{"extsrc":"\nFile:foobar.jpg|[[File:foobar.jpg|20px|desc|alt=-{R|foo}-|-{R|bar}-]]|alt=-{R|bat}-\nFile:foobar.jpg|{{Test|unamedParam|alt=-{R|param}-}}|alt=galleryalt\n"}}'> -<li class="gallerybox"> -<div class="thumb"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div> -<div class="gallerytext"><span typeof="mw:Image" data-mw='{"caption":"<span typeof=\"mw:LanguageVariant\" data-mw-variant='{\"disabled\":{\"t\":\"bar\"}}' data-parsoid='{\"fl\":[\"R\"],\"dsr\":[68,77,null,2]}'></span>"}'><a href="./File:Foobar.jpg"><img alt="" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></span></div> -</li> -<li class="gallerybox"> -<div class="thumb"><span typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></span></div> -<div class="gallerytext"><span typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"Test","href":"./Template:Test"},"params":{"1":{"wt":"unamedParam"},"alt":{"wt":"-{R|param}-"}},"i":0}}]}'>This is a test template</span></div> -</li> +<ul class="gallery mw-gallery-traditional" typeof="mw:Extension/gallery" about="#mwt6" data-mw='{"name":"gallery","attrs":{},"body":{"extsrc":"\nFile:foobar.jpg|[[File:foobar.jpg|20px|desc|alt=-{R|foo}-|-{R|bar}-]]|alt=-{R|bat}-\nFile:foobar.jpg|{{Test|unamedParam|alt=-{R|param}-}}|alt=galleryalt\n"}}'> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><figure-inline typeof="mw:Image" data-mw='{"caption":"<span typeof=\"mw:LanguageVariant\" data-mw-variant='{\"disabled\":{\"t\":\"bar\"}}' data-parsoid='{\"fl\":[\"R\"],\"dsr\":[68,77,null,2]}'></span>"}'><a href="./File:Foobar.jpg"><img alt="" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/20px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="2" width="20"/></a></figure-inline></div></li> +<li class="gallerybox" style="width: 155px;"><div class="thumb" style="width: 150px; height: 150px;"><figure-inline typeof="mw:Image"><a href="./File:Foobar.jpg"><img alt="galleryalt" resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="14" width="120"/></a></figure-inline></div><div class="gallerytext"><span about="#mwt4" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"Test","href":"./Template:Test"},"params":{"1":{"wt":"unamedParam"},"alt":{"wt":"-{R|param}-"}},"i":0}}]}'>This is a test template</span></div></li> </ul> !! end @@ -23817,7 +24090,7 @@ percent-encoding and + signs in internal links (T28410) !! html/parsoid <p><a rel="mw:WikiLink" href="./User:+%25" title="User:+%" data-parsoid='{"stx":"simple","a":{"href":"./User:+%25"},"sa":{"href":"User:+%"}}'>User:+%</a> <a rel="mw:WikiLink" href="./Page+title%25" title="Page+title%" data-parsoid='{"stx":"simple","a":{"href":"./Page+title%25"},"sa":{"href":"Page+title%"}}'>Page+title%</a> <a rel="mw:WikiLink" href="./%25+" title="%+" data-parsoid='{"stx":"simple","a":{"href":"./%25+"},"sa":{"href":"%+"}}'>%+</a> <a rel="mw:WikiLink" href="./%25+" title="%+" data-parsoid='{"stx":"piped","a":{"href":"./%25+"},"sa":{"href":"%+"}}'>%20</a> <a rel="mw:WikiLink" href="./%25+" title="%+" data-parsoid='{"stx":"simple","a":{"href":"./%25+"},"sa":{"href":"%+ "}}'>%+ </a> <a rel="mw:WikiLink" href="./%25+r" title="%+r" data-parsoid='{"stx":"simple","a":{"href":"./%25+r"},"sa":{"href":"%+r"}}'>%+r</a> -<a rel="mw:WikiLink" href="./%25" title="%" data-parsoid='{"stx":"simple","a":{"href":"./%25"},"sa":{"href":"%"}}'>%</a> <a rel="mw:WikiLink" href="./+" title="+" data-parsoid='{"stx":"simple","a":{"href":"./+"},"sa":{"href":"+"}}'>+</a> <span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"bogus","ak":"foo"},{"ck":"caption","ak":"[[bar]]"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<a rel=\"mw:WikiLink\" href=\"./Bar\" title=\"Bar\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Bar\"},\"sa\":{\"href\":\"bar\"},\"dsr\":[94,101,2,2]}'>bar</a>"}'><a href="./File:%25+abc9" data-parsoid='{"a":{"href":"./File:%25+abc9"},"sa":{}}'><img resource="./File:%25+abc9" src="./Special:FilePath/%25+abc9" height="220" width="220" data-parsoid='{"a":{"resource":"./File:%25+abc9","height":"220","width":"220"},"sa":{"resource":"File:%+abc%39"}}'/></a></span> +<a rel="mw:WikiLink" href="./%25" title="%" data-parsoid='{"stx":"simple","a":{"href":"./%25"},"sa":{"href":"%"}}'>%</a> <a rel="mw:WikiLink" href="./+" title="+" data-parsoid='{"stx":"simple","a":{"href":"./+"},"sa":{"href":"+"}}'>+</a> <figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"bogus","ak":"foo"},{"ck":"caption","ak":"[[bar]]"}]}' data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"<a rel=\"mw:WikiLink\" href=\"./Bar\" title=\"Bar\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Bar\"},\"sa\":{\"href\":\"bar\"},\"dsr\":[94,101,2,2]}'>bar</a>"}'><a href="./File:%25+abc9" data-parsoid='{"a":{"href":"./File:%25+abc9"},"sa":{}}'><img resource="./File:%25+abc9" src="./Special:FilePath/%25+abc9" height="220" width="220" data-parsoid='{"a":{"resource":"./File:%25+abc9","height":"220","width":"220"},"sa":{"resource":"File:%+abc%39"}}'/></a></figure-inline> <a rel="mw:WikiLink" href="./3E" title="3E" data-parsoid='{"stx":"simple","a":{"href":"./3E"},"sa":{"href":"%33%45"}}'>3E</a> <a rel="mw:WikiLink" href="./3E+" title="3E+" data-parsoid='{"stx":"simple","a":{"href":"./3E+"},"sa":{"href":"%33%45+"}}'>3E+</a></p> !! end @@ -23831,8 +24104,8 @@ Special characters in embedded file links (T29679) <a href="/index.php?title=Special:Upload&wpDestFile=Does_not_exist.jpg" class="new" title="File:Does not exist.jpg">Title with & ampersand</a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Contains_&_ampersand.jpg"><img resource="./File:Contains_&_ampersand.jpg" src="./Special:FilePath/Contains_&_ampersand.jpg" height="220" width="220"/></a></span> -<span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"Title with &amp; ampersand"}'><a href="./File:Does_not_exist.jpg"><img resource="./File:Does_not_exist.jpg" src="./Special:FilePath/Does_not_exist.jpg" height="220" width="220"/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}]}'><a href="./File:Contains_&_ampersand.jpg"><img resource="./File:Contains_&_ampersand.jpg" src="./Special:FilePath/Contains_&_ampersand.jpg" height="220" width="220"/></a></figure-inline> +<figure-inline class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"apierror-filedoesnotexist","message":"This image does not exist."}],"caption":"Title with &amp; ampersand"}'><a href="./File:Does_not_exist.jpg"><img resource="./File:Does_not_exist.jpg" src="./Special:FilePath/Does_not_exist.jpg" height="220" width="220"/></a></figure-inline></p> !! end !! test @@ -23969,7 +24242,7 @@ title=[[Main Page]] __TOC__ == ''Lost'' episodes == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Lost_episodes"><span class="tocnumber">1</span> <span class="toctext"><i>Lost</i> episodes</span></a></li> </ul> @@ -23979,7 +24252,7 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <i>Lost</i> episodes </h2> +<h2 id="Lost_episodes" data-parsoid='{}'> <i>Lost</i> episodes </h2> !! end !! test @@ -23990,7 +24263,7 @@ title=[[Main Page]] __TOC__ == '''should be bold''' then normal text == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#should_be_bold_then_normal_text"><span class="tocnumber">1</span> <span class="toctext"><b>should be bold</b> then normal text</span></a></li> </ul> @@ -24000,7 +24273,7 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <b>should be bold</b> then normal text </h2> +<h2 id="should_be_bold_then_normal_text" data-parsoid='{}'> <b>should be bold</b> then normal text </h2> !! end !! test @@ -24011,7 +24284,7 @@ title=[[Main Page]] __TOC__ == Image [[Image:foobar.jpg]] == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Image"><span class="tocnumber">1</span> <span class="toctext">Image</span></a></li> </ul> @@ -24021,7 +24294,7 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> Image <span class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"Image:foobar.jpg"}}'/></a></span> </h2> +<h2 id="Image" data-parsoid='{}'> Image <figure-inline class="mw-default-size" typeof="mw:Image"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"Image:foobar.jpg"}}'/></a></figure-inline> </h2> !! end !! test @@ -24032,7 +24305,7 @@ title=[[Main Page]] __TOC__ == <blockquote>Quote</blockquote> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Quote"><span class="tocnumber">1</span> <span class="toctext">Quote</span></a></li> </ul> @@ -24043,7 +24316,7 @@ __TOC__ !! html/php+tidy <p></p> <div id="toc" class="toc"> -<div class="toctitle"> +<div class="toctitle" lang="en" dir="ltr" xml:lang="en"> <h2>Contents</h2> </div> <ul> @@ -24058,11 +24331,13 @@ __TOC__ <p><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&action=edit&section=1" title="Edit section: Quote">edit</a><span class="mw-editsection-bracket">]</span></span></p> !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <blockquote>Quote</blockquote> </h2> +<h2 id="Quote" data-parsoid='{}'> <blockquote>Quote</blockquote> </h2> !! end !! test Unclosed tags in TOC +!! config +wgFragmentMode=[ 'html5', 'legacy' ] !! options title=[[Main Page]] !! wikitext @@ -24071,19 +24346,19 @@ __TOC__ <small>Hanc marginis exiguitas non caperet.</small> QED !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> -<li class="toclevel-1 tocsection-1"><a href="#Proof:_2_.3C_3"><span class="tocnumber">1</span> <span class="toctext">Proof: 2 < 3</span></a></li> +<li class="toclevel-1 tocsection-1"><a href="#Proof:_2_<_3"><span class="tocnumber">1</span> <span class="toctext">Proof: 2 < 3</span></a></li> </ul> </div> -<h2><span class="mw-headline" id="Proof:_2_.3C_3">Proof: 2 < 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&action=edit&section=1" title="Edit section: Proof: 2 < 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2> +<h2><span id="Proof:_2_.3C_3"></span><span class="mw-headline" id="Proof:_2_<_3">Proof: 2 < 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&action=edit&section=1" title="Edit section: Proof: 2 < 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2> <p><small>Hanc marginis exiguitas non caperet.</small> QED </p> !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> Proof: 2 < 3 </h2> +<h2 id="Proof:_2_<_3" data-parsoid='{}'><span id="Proof:_2_.3C_3" typeof="mw:FallbackId"></span> Proof: 2 < 3 </h2> <p><small>Hanc marginis exiguitas non caperet.</small> QED</p> !! end @@ -24096,7 +24371,7 @@ __TOC__ == <i>Foo</i> <blockquote>Bar</blockquote> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Foo_Bar"><span class="tocnumber">1</span> <span class="toctext"><i>Foo</i> <b>Bar</b></span></a></li> <li class="toclevel-1 tocsection-2"><a href="#Foo_Bar_2"><span class="tocnumber">2</span> <span class="toctext"><i>Foo</i> Bar</span></a></li> @@ -24109,7 +24384,7 @@ __TOC__ !! html/php+tidy <p></p> <div id="toc" class="toc"> -<div class="toctitle"> +<div class="toctitle" lang="en" dir="ltr" xml:lang="en"> <h2>Contents</h2> </div> <ul> @@ -24126,8 +24401,9 @@ __TOC__ <p><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Foo Bar">edit</a><span class="mw-editsection-bracket">]</span></span></p> !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <i data-parsoid='{"stx":"html"}'>Foo</i> <b data-parsoid='{"stx":"html"}'>Bar</b> </h2> -<h2> <i data-parsoid='{"stx":"html"}'>Foo</i> <blockquote>Bar</blockquote> </h2> +<h2 id="Foo_Bar" data-parsoid='{}'> <i data-parsoid='{"stx":"html"}'>Foo</i> <b data-parsoid='{"stx":"html"}'>Bar</b> </h2> + +<h2 id="Foo_Bar_2" data-parsoid='{}'> <i data-parsoid='{"stx":"html"}'>Foo</i> <blockquote>Bar</blockquote> </h2> !! end # Don't expect Parsoid to roundtrip this until the php parser comes closer to @@ -24142,7 +24418,7 @@ __TOC__ == <sup class="a > b">Evilbye</sup> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Hello"><span class="tocnumber">1</span> <span class="toctext"><sup>Hello</sup></span></a></li> <li class="toclevel-1 tocsection-2"><a href="#b.22.3EEvilbye"><span class="tocnumber">2</span> <span class="toctext"><sup> b">Evilbye</sup></span></a></li> @@ -24154,9 +24430,9 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" /> -<h2> <sup class="in-h2" data-parsoid='{"stx":"html"}'>Hello</sup> </h2> +<h2 id="Hello"> <sup class="in-h2" data-parsoid='{"stx":"html"}'>Hello</sup> </h2> -<h2> <sup class="a " data-parsoid='{"stx":"html"}'> b">Evilbye</sup> </h2> +<h2 id='b">Evilbye'><span id="b.22.3EEvilbye" typeof="mw:FallbackId"></span> <sup class="a " data-parsoid='{"stx":"html"}'> b">Evilbye</sup> </h2> !! end !! test @@ -24173,7 +24449,7 @@ __TOC__ == <span dir="ltr" style="font-style: italic">Attributes after dir on these span tags must be deleted from the TOC</span> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#C.2B.2B"><span class="tocnumber">1</span> <span class="toctext"><span dir="ltr">C++</span></span></a></li> <li class="toclevel-1 tocsection-2"><a href="#.D7.96.D7.91.D7.A0.D7.92.21"><span class="tocnumber">2</span> <span class="toctext"><span dir="rtl">זבנג!</span></span></a></li> @@ -24191,11 +24467,11 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <span dir="ltr">C++</span> </h2> -<h2> <span dir="rtl">זבנג!</span> </h2> -<h2> <span style="font-style: italic">The attributes on these span tags must be deleted from the TOC</span> </h2> -<h2> <span style="font-style: italic" dir="ltr">All attributes on these span tags must be deleted from the TOC</span> </h2> -<h2> <span dir="ltr" style="font-style: italic">Attributes after dir on these span tags must be deleted from the TOC</span> </h2> +<h2 id="C++" data-parsoid='{}'><span id="C.2B.2B" typeof="mw:FallbackId"></span> <span dir="ltr">C++</span> </h2> +<h2 id="זבנג!"><span id=".D7.96.D7.91.D7.A0.D7.92.21" typeof="mw:FallbackId"></span> <span dir="rtl">זבנג!</span> </h2> +<h2 id="The_attributes_on_these_span_tags_must_be_deleted_from_the_TOC"> <span style="font-style: italic">The attributes on these span tags must be deleted from the TOC</span> </h2> +<h2 id="All_attributes_on_these_span_tags_must_be_deleted_from_the_TOC"> <span style="font-style: italic" dir="ltr">All attributes on these span tags must be deleted from the TOC</span> </h2> +<h2 id="Attributes_after_dir_on_these_span_tags_must_be_deleted_from_the_TOC"> <span dir="ltr" style="font-style: italic">Attributes after dir on these span tags must be deleted from the TOC</span> </h2> !! end !! test @@ -24204,7 +24480,7 @@ T74884: bdi element in ToC __TOC__ == <bdi>test</bdi> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#test"><span class="tocnumber">1</span> <span class="toctext"><bdi>test</bdi></span></a></li> </ul> @@ -24214,7 +24490,7 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <bdi>test</bdi> </h2> +<h2 id="test" data-parsoid='{}'> <bdi>test</bdi> </h2> !! end !! test @@ -24223,7 +24499,7 @@ T35715: s/strike element in ToC __TOC__ == <s>test</s> test <strike>test</strike> == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#test_test_test"><span class="tocnumber">1</span> <span class="toctext"><s>test</s> test <strike>test</strike></span></a></li> </ul> @@ -24233,7 +24509,7 @@ __TOC__ !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> <s>test</s> test <strike>test</strike> </h2> +<h2 id="test_test_test" data-parsoid='{}'> <s>test</s> test <strike>test</strike> </h2> !! end # Note that the html output does not have the <p></p>, but the @@ -24245,7 +24521,7 @@ Empty <p> tag in TOC, removed by Sanitizer (T92892) __TOC__ == x == !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#x"><span class="tocnumber">1</span> <span class="toctext">x</span></a></li> </ul> @@ -24256,7 +24532,7 @@ __TOC__ !! html/php+tidy <p></p> <div id="toc" class="toc"> -<div class="toctitle"> +<div class="toctitle" lang="en" dir="ltr" xml:lang="en"> <h2>Contents</h2> </div> <ul> @@ -24267,7 +24543,7 @@ __TOC__ <h2><span class="mw-headline" id="x">x</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: x">edit</a><span class="mw-editsection-bracket">]</span></span></h2> !! html/parsoid <meta property="mw:PageProp/toc" data-parsoid='{}'/> -<h2 data-parsoid='{}'> x </h2> +<h2 id="x" data-parsoid='{}'> x </h2> !! end !! article @@ -24390,9 +24666,11 @@ Strip marker in padright Strip marker in anchorencode !! wikitext {{anchorencode:x<nowiki/>y}} -!! html +!! html/php <p>xy </p> +!! html/parsoid +<p about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"pi":[[]]}' data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode:x<nowiki/>y","function":"anchorencode"},"params":{},"i":0}}]}'>xy</p> !! end !! test @@ -24851,16 +25129,59 @@ Headings: 4b. No escaping needed (inside p-tags) !! options parsoid=html2wt !! html/parsoid -<p>=== -=foo= x +<p>=foo= x =foo= <s></s> </p> !! wikitext -=== =foo= x =foo= <s></s> +!! html/php +<p>=foo= x +=foo= <s></s> +</p> !!end +!! test +Headings: 4c. Short headings (1) +!! options +parsoid=html2wt +!! html/parsoid +<p>=== +</p> +!! wikitext +<nowiki>===</nowiki> +!! html/php +<p>=== +</p> +!! end + +# in the html2wt direction we emit '= = =' or '=<nowiki>=</nowiki>=' +!! test +Headings: 4d. Short headings (2) +!! options +parsoid=wt2html,html2html +!! wikitext += +== +=== +==== +===== +!! html/php +<p>= +== +</p> +<h1><span class="mw-headline" id=".3D">=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: =">edit</a><span class="mw-editsection-bracket">]</span></span></h1> +<h1><span class="mw-headline" id=".3D.3D">==</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: ==">edit</a><span class="mw-editsection-bracket">]</span></span></h1> +<h2><span class="mw-headline" id=".3D_2">=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: =">edit</a><span class="mw-editsection-bracket">]</span></span></h2> + +!! html/parsoid +<p>= +==</p> +<h1 id="="><span id=".3D" typeof="mw:FallbackId"></span>=</h1> +<h1 id="=="><span id=".3D.3D" typeof="mw:FallbackId"></span>==</h1> +<h2 id="=_2"><span id=".3D_2" typeof="mw:FallbackId"></span>=</h2> +!! end + !! test Headings: 5. Empty headings !! options @@ -27447,7 +27768,7 @@ Image: upright option is ignored on inline and frame images (parsoid) !! wikitext [[File:Foobar.jpg|500x500px|upright=0.5|caption]] !! html/parsoid -<p><span typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/500px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="57" width="500"/></a></span></p> +<p><figure-inline typeof="mw:Image" data-mw='{"caption":"caption"}'><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/500px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="57" width="500"/></a></figure-inline></p> !! end !! test @@ -27455,7 +27776,7 @@ Image: in template parameter with empty parameter !! wikitext {{echo|[[File:Foobar.jpg|link=]]}} !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Transclusion mw:Image" about="#mwt1" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[[File:Foobar.jpg|link=]]"}},"i":0}}]}'><span><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></span></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Transclusion mw:Image" about="#mwt1" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[[File:Foobar.jpg|link=]]"}},"i":0}}]}'><span><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941"/></span></figure-inline></p> !! end !! test @@ -27508,7 +27829,7 @@ Image: Invalid title as link <p><a href="/wiki/File:Foobar.jpg" class="image" title="link=<"><img alt="link=<" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> </p> !! html/parsoid -<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"link","ak":"link=<"}]}' data-mw='{"caption":"link=&lt;"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p> +<p><figure-inline class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"link","ak":"link=<"}]}' data-mw='{"caption":"link=&lt;"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure-inline></p> !! end !! test @@ -28534,6 +28855,14 @@ parsoid=wt2html <p><span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"hi"}},"i":0}}]}'>hi</span><a rel="mw:ExtLink" href="http://example.com"></a><a rel="mw:WikiLink" href="./Ho" title="Ho" data-parsoid='{"misnested":true}'>ho</a></p> !! end +!! test +Catch regression when unpacking with trailing content +!! wikitext +{{echo|Foo <references/> bar}} +!! html/parsoid +<p about="#mwt2" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"Foo <references/> bar"}},"i":0}}]}'>Foo </p><ol class="mw-references references" typeof="mw:Extension/references" about="#mwt2" data-mw='{"name":"references","attrs":{}}'></ol><p about="#mwt2"> bar</p> +!! end + !! test Use data-parsoid.firstWikitextNode to compute newline constraints for template content !! options @@ -29535,6 +29864,24 @@ wgRawHtml=1 !! test Decoding of HTML entities in headings and links for IDs and link fragments (T103714) +!! config +wgFragmentMode=[ 'html5', 'legacy' ] +!! wikitext +== A&B&C&amp;D&amp;amp;E == +[[#A&B&C&amp;D&amp;amp;E]] +!! html/php +<h2><span id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE"></span><span class="mw-headline" id="A&B&C&amp;D&amp;amp;E">A&B&C&amp;D&amp;amp;E</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: A&B&C&amp;D&amp;amp;E">edit</a><span class="mw-editsection-bracket">]</span></span></h2> +<p><a href="#A&B&C&amp;D&amp;amp;E">#A&B&C&amp;D&amp;amp;E</a> +</p> +!! html/parsoid +<h2 id="A&B&C&amp;D&amp;amp;E"><span id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE" typeof="mw:FallbackId" data-parsoid="{}"></span> A&B<span typeof="mw:Entity" data-parsoid='{"src":"&amp;","srcContent":"&"}'>&</span>C<span typeof="mw:Entity" data-parsoid='{"src":"&amp;","srcContent":"&"}'>&</span>amp;D<span typeof="mw:Entity" data-parsoid='{"src":"&amp;","srcContent":"&"}'>&</span>amp;amp;E </h2> +<p><a rel="mw:WikiLink" href="./Main_Page#A&B&C&amp;D&amp;amp;E" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#A&B&C&amp;D&amp;amp;E"},"sa":{"href":"#A&B&amp;C&amp;amp;D&amp;amp;amp;E"}}'>#A&B&C&amp;D&amp;amp;E</a></p> +!! end + +!! test +Decoding of HTML entities in headings and links for IDs and link fragments (T103714) (legacy) +!! config +wgFragmentMode=[ 'legacy' ] !! wikitext == A&B&C&amp;D&amp;amp;E == [[#A&B&C&amp;D&amp;amp;E]] @@ -29544,17 +29891,46 @@ Decoding of HTML entities in headings and links for IDs and link fragments (T103 </p> !! end +!! test +Decoding of HTML entities in embedded HTML tags +!! wikitext +<table class="1&2&3&amp;4&amp;amp;5"><tr><td>x</td></tr></table> +!! html/php +<table class="1&2&3&amp;4&amp;amp;5"><tr><td>x</td></tr></table> + +!! html/parsoid +<table class="1&2&3&amp;4&amp;amp;5" data-parsoid='{"stx":"html","a":{"class":"1&2&3&amp;4&amp;amp;5"},"sa":{"class":"1&2&amp;3&amp;amp;4&amp;amp;amp;5"}}'><tbody><tr data-parsoid='{"stx":"html"}'><td data-parsoid='{"stx":"html"}'>x</td></tr></tbody></table> +!! end + !! test Decoding of HTML entities in indicator names for IDs (T104196) !! options +parsoid=wt2html,html2html showindicators !! wikitext <indicator name="1&2&3&amp;4&amp;amp;5">Indicator</indicator> !! html/php 1&2&3&4&amp;5=Indicator +!! html/parsoid +<p><span typeof="mw:Extension/indicator" about="#mwt3" data-mw='{"name":"indicator","attrs":{"name":"1&2&3&amp;4&amp;amp;5"},"body":{"extsrc":"Indicator"}}'></span></p> +!! end + +# this version of the test strips out the ambiguity so Parsoid rts cleanly +!! test +Decoding of HTML entities in indicator names for IDs (unambiguous) (T104196) +!! options +showindicators +!! wikitext +<indicator name="1&2&3&amp;4&amp;amp;5">Indicator</indicator> +!! html/php +1&2&3&4&amp;5=Indicator + +!! html/parsoid +<p><span typeof="mw:Extension/indicator" about="#mwt3" data-mw='{"name":"indicator","attrs":{"name":"1&2&3&amp;4&amp;amp;5"},"body":{"extsrc":"Indicator"}}'></span></p> !! end +# This fragment mode is what Parsoid supports. !! test HTML5 ids: fallback to legacy !! config @@ -29579,7 +29955,7 @@ wgFragmentMode=[ 'html5', 'legacy' ] [[#啤酒]] [[#%E5%95%A4%E9%85%92]] !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Foo_bar"><span class="tocnumber">1</span> <span class="toctext">Foo bar</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#foo_Bar_2"><span class="tocnumber">2</span> <span class="toctext">foo Bar</span></a></li> @@ -29600,8 +29976,27 @@ wgFragmentMode=[ 'html5', 'legacy' ] </p><p>💩 <span id="💩"></span> </p><p><a href="#啤酒">#啤酒</a> <a href="#啤酒">#啤酒</a> </p> +!! html/parsoid +<h2 id="Foo_bar"> Foo bar </h2> + +<h2 id="foo_Bar_2"> foo Bar </h2> + +<h2 id="Тест"><span id=".D0.A2.D0.B5.D1.81.D1.82" typeof="mw:FallbackId"></span> Тест </h2> + +<h2 id="Тест_2"><span id=".D0.A2.D0.B5.D1.81.D1.82_2" typeof="mw:FallbackId"></span> Тест </h2> + +<h2 id="тест"><span id=".D1.82.D0.B5.D1.81.D1.82" typeof="mw:FallbackId"></span> тест </h2> + +<h2 id="Hey_<_#_"_>_%_:_'"><span id="Hey_.3C_.23_.22_.3E_.25_:_.27" typeof="mw:FallbackId"></span> Hey < # " > %<span typeof="mw:DisplaySpace mw:Placeholder" data-parsoid='{"src":" ","isDisplayHack":true}'> </span>: ' </h2> +<p><a rel="mw:WikiLink" href="./Main_Page#Foo_bar">#Foo bar</a> <a rel="mw:WikiLink" href="./Main_Page#foo_Bar">#foo Bar</a> <a rel="mw:WikiLink" href="./Main_Page#Тест">#Тест</a> <a rel="mw:WikiLink" href="./Main_Page#тест">#тест</a> <a rel="mw:WikiLink" href="./Main_Page#Hey_<_#_"_>_%_:_'" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#Hey_<_#_\"_>_%_:_'"},"sa":{"href":"#Hey < # \" > % : '"}}'>#Hey < # " > % : '</a></p> + +<p><span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"anchorencode:💩","function":"anchorencode"},"params":{},"i":0}}]}'>💩</span> <span id="💩" about="#mwt3" typeof="mw:ExpandedAttrs" data-mw='{"attribs":[[{"txt":"id"},{"html":"<span about=\"#mwt2\" typeof=\"mw:Transclusion\" data-parsoid='{\"pi\":[[]],\"dsr\":[190,209,null,null]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"anchorencode:💩\",\"function\":\"anchorencode\"},\"params\":{},\"i\":0}}]}'>💩</span>"}]]}'></span></p> + +<!-- These two links should produce identical HTML --> +<p><a rel="mw:WikiLink" href="./Main_Page#啤酒">#啤酒</a> <a rel="mw:WikiLink" href="./Main_Page#啤酒" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#啤酒"},"sa":{"href":"#%E5%95%A4%E9%85%92"}}'>#啤酒</a></p> !! end +# Parsoid doesn't support this mode !! test HTML5 ids: legacy with a fallback to modern !! config @@ -29626,7 +30021,7 @@ wgFragmentMode=[ 'legacy', 'html5' ] [[#啤酒]] [[#%E5%95%A4%E9%85%92]] !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Foo_bar"><span class="tocnumber">1</span> <span class="toctext">Foo bar</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#foo_Bar_2"><span class="tocnumber">2</span> <span class="toctext">foo Bar</span></a></li> @@ -29649,6 +30044,7 @@ wgFragmentMode=[ 'legacy', 'html5' ] </p> !! end +# Parsoid doesn't support this mode. !! test HTML5 ids: no legacy !! config @@ -29673,7 +30069,7 @@ wgFragmentMode=[ 'html5' ] [[#啤酒]] [[#%E5%95%A4%E9%85%92]] !! html/php -<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div> +<div id="toc" class="toc"><div class="toctitle" lang="en" dir="ltr"><h2>Contents</h2></div> <ul> <li class="toclevel-1 tocsection-1"><a href="#Foo_bar"><span class="tocnumber">1</span> <span class="toctext">Foo bar</span></a></li> <li class="toclevel-1 tocsection-2"><a href="#foo_Bar_2"><span class="tocnumber">2</span> <span class="toctext">foo Bar</span></a></li> @@ -29720,6 +30116,9 @@ T51672: Test for brackets in attributes of elements in external link texts <p><a rel="nofollow" class="external text" href="http://example.com/">link <span title="title with [brackets]">span</span></a> <a rel="nofollow" class="external text" href="http://example.com/">link <span title="title with [brackets]">span</span></a> </p> +!! html/parsoid +<p><a rel="mw:ExtLink" href="http://example.com/">link <span title="title with [brackets]">span</span></a> +<a rel="mw:ExtLink" href="http://example.com/">link <span title="title with [brackets]" data-parsoid='{"stx":"html","a":{"title":"title with [brackets]"},"sa":{"title":"title with &#91;brackets&#93;"}}'>span</span></a></p> !! end !! test @@ -29732,6 +30131,9 @@ T72875: Test for brackets in attributes of elements in internal link texts <p><a href="/wiki/Foo" title="Foo">link <span title="title with [[double brackets]]">span</span></a> <a href="/wiki/Foo" title="Foo">link <span title="title with [[double brackets]]">span</span></a> </p> +!! html/parsoid +<p><a rel="mw:WikiLink" href="./Foo" title="Foo">link <span title="title with [[double brackets]]">span</span></a> +<a rel="mw:WikiLink" href="./Foo" title="Foo">link <span title="title with [[double brackets]]" data-parsoid='{"stx":"html","a":{"title":"title with [[double brackets]]"},"sa":{"title":"title with &#91;&#91;double brackets&#93;&#93;"}}'>span</span></a></p> !! end !! test @@ -29743,6 +30145,8 @@ wgFragmentMode=[ 'html5' ] !! html/php <p><span id="[foo]"></span><a href="#[foo]">#[foo]</a> </p> +!! html/parsoid +<p><span id="[foo]" about="#mwt3" typeof="mw:ExpandedAttrs" data-parsoid='{"stx":"html","a":{"id":"[foo]"},"sa":{"id":"{{anchorencode:[foo]}}"}}' data-mw='{"attribs":[[{"txt":"id"},{"html":"<span typeof=\"mw:Transclusion mw:Entity\" about=\"#mwt1\" data-parsoid='{\"srcContent\":\"[\",\"dsr\":[10,32,null,null],\"pi\":[[]]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"anchorencode:[foo]\",\"function\":\"anchorencode\"},\"params\":{},\"i\":0}}]}'>[</span><span about=\"#mwt1\" data-parsoid=\"{}\">foo</span><span typeof=\"mw:Entity\" about=\"#mwt1\" data-parsoid='{\"src\":\"&amp;#x5D;\",\"srcContent\":\"]\"}'>]</span>"}]]}'></span><a typeof="mw:ExpandedAttrs" about="#mwt4" rel="mw:WikiLink" href="./Main_Page#[foo]" data-parsoid='{"stx":"simple","a":{"href":"./Main_Page#[foo]"},"sa":{"href":"#{{anchorencode:[foo]}}"}}' data-mw='{"attribs":[[{"txt":"href"},{"html":"#<span typeof=\"mw:Transclusion mw:Entity\" about=\"#mwt2\" data-parsoid='{\"srcContent\":\"[\",\"dsr\":[44,66,null,null],\"pi\":[[]]}' data-mw='{\"parts\":[{\"template\":{\"target\":{\"wt\":\"anchorencode:[foo]\",\"function\":\"anchorencode\"},\"params\":{},\"i\":0}}]}'>[</span><span about=\"#mwt2\" data-parsoid=\"{}\">foo</span><span typeof=\"mw:Entity\" about=\"#mwt2\" data-parsoid='{\"src\":\"&amp;#x5D;\",\"srcContent\":\"]\"}'>]</span>"}]]}'>#[foo]</a></p> !! end ## ------------------------------ @@ -29773,7 +30177,7 @@ e = 3 = f !! html/parsoid -<section data-mw-section-id="1"><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1"><h1 id="1"> 1 </h1> <p>a</p> </section><section data-mw-section-id="2"><h1 id="2"> 2 </h1> @@ -29855,7 +30259,7 @@ c = 2 = d !! html/parsoid -<section data-mw-section-id="1"><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1"><h1 id="1"> 1 </h1> <p>a</p> <section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.1" data-parsoid='{"dsr":[9,33,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.1 ==\nb"}},"i":0}}]}'> 1.1 </h2><span about="#mwt1"> @@ -29889,7 +30293,7 @@ d = 2 = e !! html/parsoid -<section data-mw-section-id="1"><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1"><h1 id="1"> 1 </h1> <p>a</p> <section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.1" data-parsoid='{"dsr":[9,50,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.1 ==\nb\n=== 1.1.1 ===\nd"}},"i":0}},"\n"]}'> 1.1 </h2><span about="#mwt1"> @@ -29925,7 +30329,7 @@ d = 2 = e !! html/parsoid -<section data-mw-section-id="1" data-parsoid="{}"><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1" data-parsoid="{}"><h1 id="1"> 1 </h1> <p>a</p> <p about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[9,60,0,0],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"x\n== 1.1 ==\nb\n==1.2==\nc\n===1.2.1===\nd"}},"i":0}},"\n"]}'>x</p><span about="#mwt1"> @@ -29942,6 +30346,7 @@ e # Because of section-wrapping and template-wrapping interactions, # the scope of the template is expanded so that the template markup # is valid in the presence of <section> tags. +# This exercises the s1 is null scenario in the wrapSections code !! test Section wrapping with template-generated sections (bad nesting 1) !! options @@ -29949,6 +30354,40 @@ parsoid={ "wrapSections": true } !! wikitext +<div> +a + +{{echo| += 1 = +b +}} + +c +</div> +!! html/parsoid +<section data-mw-section-id="-1"></section><section data-mw-section-id="-2"><div data-parsoid='{"stx":"html"}'> +<p>a</p> + +<span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1"}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"\n= 1 =\nb\n"}},"i":0}},"\n\nc\n"]}'> +</span><section data-mw-section-id="-1" about="#mwt1"><h1 about="#mwt1" id="1"> 1 </h1><span about="#mwt1"> +</span><p about="#mwt1">b +</p><span about="#mwt1"> + +</span><p about="#mwt1">c</p><span about="#mwt1"> +</span></section></div></section> +!! end + +# Because of section-wrapping and template-wrapping interactions, +# the scope of the template is expanded so that the template markup +# is valid in the presence of <section> tags. +# This exercises the s1 is ancestor of s2 scenario in the wrapSections code +!! test +Section wrapping with template-generated sections (bad nesting 2) +!! options +parsoid={ + "wrapSections": true +} +!! wikitext = 1 = a @@ -29964,7 +30403,7 @@ d = 3 = e !! html/parsoid -<section data-mw-section-id="1"><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1"><h1 id="1"> 1 </h1> <p>a</p> </section><section data-mw-section-id="-1"><h1 about="#mwt1" typeof="mw:Transclusion" id="2" data-parsoid='{"dsr":[9,45,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"= 2 =\nb\n== 2.1 ==\nc"}},"i":0}},"\n\nd\n\n"]}'> 2 </h1><span about="#mwt1"> @@ -29983,8 +30422,9 @@ e # so that template wrapping semantics are valid whether section # tags are retained or stripped. But, the template scope can expand # greatly when accounting for section tags. +# This exercises the s1 and s2 are in different subtrees scenario !! test -Section wrapping with template-generated sections (bad nesting 2) +Section wrapping with template-generated sections (bad nesting 3) !! options parsoid={ "wrapSections": true, @@ -30006,7 +30446,7 @@ d = 3 = e !! html/parsoid -<section data-mw-section-id="1" about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":["= 1 =\na\n\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.2 ==\nb\n= 2 =\nc"}},"i":0}},"\n\nd\n\n"]}'><h1 id="1"> 1 </h1> +<section data-mw-section-id="0"></section><section data-mw-section-id="1" about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":["= 1 =\na\n\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.2 ==\nb\n= 2 =\nc"}},"i":0}},"\n\nd\n\n"]}'><h1 id="1"> 1 </h1> <p>a</p> <section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.2" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.2 ==\nb\n= 2 =\nc"}},"i":0}}]}'> 1.2 </h2><span about="#mwt1"> @@ -30144,3 +30584,27 @@ foo </section><section data-mw-section-id="2"><h1 id="d"> d </h1></section> !! end + +!! test +Lead section containing only whitespace and comments. +!! options +parsoid={ + "wrapSections": true +} +!! wikitext + +<!-- this is a comment, presumably significant to editors --> += 1 = +a + += 2 = +b +!! html/parsoid +<section data-mw-section-id="0" data-parsoid="{}"> +<!-- this is a comment, presumably significant to editors --> +</section><section data-mw-section-id="1"><h1 id="1"> 1 </h1> +<p>a</p> + +</section><section data-mw-section-id="2"><h1 id="2"> 2 </h1> +<p>b</p></section> +!! end diff --git a/tests/phpunit/MediaWikiTestCase.php b/tests/phpunit/MediaWikiTestCase.php index 4d3c37bda5..9f2e5f9395 100644 --- a/tests/phpunit/MediaWikiTestCase.php +++ b/tests/phpunit/MediaWikiTestCase.php @@ -1008,10 +1008,6 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase { $user = static::getTestSysop()->getUser(); $comment = __METHOD__ . ': Sample page for unit test.'; - // Avoid memory leak...? - // LinkCache::singleton()->clear(); - // Maybe. But doing this absolutely breaks $title->isRedirect() when called during unit tests.... - $page = WikiPage::factory( $title ); $page->doEditContent( ContentHandler::makeContent( $text, $title ), $comment, 0, false, $user ); diff --git a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php index d961e415e3..9d56150eb0 100644 --- a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php +++ b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php @@ -752,6 +752,9 @@ class GlobalTest extends MediaWikiTestCase { ); } + /** + * @covers ::wfMemcKey + */ public function testWfMemcKey() { $cache = ObjectCache::getLocalClusterInstance(); $this->assertEquals( @@ -760,6 +763,9 @@ class GlobalTest extends MediaWikiTestCase { ); } + /** + * @covers ::wfForeignMemcKey + */ public function testWfForeignMemcKey() { $cache = ObjectCache::getLocalClusterInstance(); $keyspace = $this->readAttribute( $cache, 'keyspace' ); @@ -769,6 +775,9 @@ class GlobalTest extends MediaWikiTestCase { ); } + /** + * @covers ::wfGlobalCacheKey + */ public function testWfGlobalCacheKey() { $cache = ObjectCache::getLocalClusterInstance(); $this->assertEquals( diff --git a/tests/phpunit/includes/HtmlTest.php b/tests/phpunit/includes/HtmlTest.php index e867f5ec7b..7e32770b33 100644 --- a/tests/phpunit/includes/HtmlTest.php +++ b/tests/phpunit/includes/HtmlTest.php @@ -386,6 +386,9 @@ class HtmlTest extends MediaWikiTestCase { ); } + /** + * @covers Html::namespaceSelector + */ public function testCanFilterOutNamespaces() { $this->assertEquals( '<select id="namespace" name="namespace">' . "\n" . @@ -408,6 +411,9 @@ class HtmlTest extends MediaWikiTestCase { ); } + /** + * @covers Html::namespaceSelector + */ public function testCanDisableANamespaces() { $this->assertEquals( '<select id="namespace" name="namespace">' . "\n" . @@ -678,6 +684,9 @@ class HtmlTest extends MediaWikiTestCase { return $ret; } + /** + * @covers Html::input + */ public function testWrapperInput() { $this->assertEquals( '<input type="radio" value="testval" name="testname"/>', @@ -691,6 +700,9 @@ class HtmlTest extends MediaWikiTestCase { ); } + /** + * @covers Html::check + */ public function testWrapperCheck() { $this->assertEquals( '<input type="checkbox" value="1" name="testname"/>', @@ -709,6 +721,9 @@ class HtmlTest extends MediaWikiTestCase { ); } + /** + * @covers Html::radio + */ public function testWrapperRadio() { $this->assertEquals( '<input type="radio" value="1" name="testname"/>', @@ -727,6 +742,9 @@ class HtmlTest extends MediaWikiTestCase { ); } + /** + * @covers Html::label + */ public function testWrapperLabel() { $this->assertEquals( '<label for="testid">testlabel</label>', diff --git a/tests/phpunit/includes/LinkFilterTest.php b/tests/phpunit/includes/LinkFilterTest.php index ed4958f254..51b54d2c93 100644 --- a/tests/phpunit/includes/LinkFilterTest.php +++ b/tests/phpunit/includes/LinkFilterTest.php @@ -3,6 +3,7 @@ use Wikimedia\Rdbms\LikeMatch; /** + * @covers LinkFilter * @group Database */ class LinkFilterTest extends MediaWikiLangTestCase { diff --git a/tests/phpunit/includes/MediaWikiServicesTest.php b/tests/phpunit/includes/MediaWikiServicesTest.php index a5c468806f..dbb7799b55 100644 --- a/tests/phpunit/includes/MediaWikiServicesTest.php +++ b/tests/phpunit/includes/MediaWikiServicesTest.php @@ -7,6 +7,10 @@ use MediaWiki\Services\DestructibleService; use MediaWiki\Services\SalvageableService; use MediaWiki\Services\ServiceDisabledException; use MediaWiki\Shell\CommandFactory; +use MediaWiki\Storage\BlobStore; +use MediaWiki\Storage\BlobStoreFactory; +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\SqlBlobStore; /** * @covers MediaWiki\MediaWikiServices @@ -331,6 +335,10 @@ class MediaWikiServicesTest extends MediaWikiTestCase { 'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ], 'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ], 'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ], + 'BlobStoreFactory' => [ 'BlobStoreFactory', BlobStoreFactory::class ], + 'BlobStore' => [ 'BlobStore', BlobStore::class ], + '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ], + 'RevisionStore' => [ 'RevisionStore', RevisionStore::class ], ]; } diff --git a/tests/phpunit/includes/OutputPageTest.php b/tests/phpunit/includes/OutputPageTest.php index d5948edc64..c3488343da 100644 --- a/tests/phpunit/includes/OutputPageTest.php +++ b/tests/phpunit/includes/OutputPageTest.php @@ -525,7 +525,7 @@ class OutputPageTest extends MediaWikiTestCase { $this->assertTrue( $outputPage->haveCacheVaryCookies() ); } - /* + /** * @covers OutputPage::addCategoryLinks * @covers OutputPage::getCategories */ diff --git a/tests/phpunit/includes/PageArchiveTest.php b/tests/phpunit/includes/PageArchiveTest.php index 6420c395ad..15b26c23e6 100644 --- a/tests/phpunit/includes/PageArchiveTest.php +++ b/tests/phpunit/includes/PageArchiveTest.php @@ -11,8 +11,9 @@ * ^--- important, causes tests not to fail with timeout */ class PageArchiveTest extends MediaWikiTestCase { + /** - * @var WikiPage $archivedPage + * @var PageArchive $archivedPage */ private $archivedPage; @@ -78,6 +79,7 @@ class PageArchiveTest extends MediaWikiTestCase { /** * @covers PageArchive::undelete + * @covers PageArchive::undeleteRevisions */ public function testUndeleteRevisions() { // First make sure old revisions are archived @@ -107,4 +109,134 @@ class PageArchiveTest extends MediaWikiTestCase { $row = $res->fetchObject(); $this->assertEquals( IP::toHex( $this->ipEditor ), $row->ipc_hex ); } + + /** + * @covers PageArchive::listRevisions + */ + public function testListRevisions() { + $revisions = $this->archivedPage->listRevisions(); + $this->assertEquals( 2, $revisions->numRows() ); + + // Get the rows as arrays + $row1 = (array)$revisions->current(); + $row2 = (array)$revisions->next(); + // Unset the timestamps (we assume they will be right... + $this->assertInternalType( 'string', $row1['ar_timestamp'] ); + $this->assertInternalType( 'string', $row2['ar_timestamp'] ); + unset( $row1['ar_timestamp'] ); + unset( $row2['ar_timestamp'] ); + + $this->assertEquals( + [ + 'ar_minor_edit' => '0', + 'ar_user' => '0', + 'ar_user_text' => '2600:387:ed7:947e:8c16:a1ad:dd34:1dd7', + 'ar_len' => '11', + 'ar_deleted' => '0', + 'ar_rev_id' => '3', + 'ar_sha1' => '0qdrpxl537ivfnx4gcpnzz0285yxryy', + 'ar_page_id' => '2', + 'ar_comment_text' => 'just a test', + 'ar_comment_data' => null, + 'ar_comment_cid' => null, + 'ar_content_format' => null, + 'ar_content_model' => null, + 'ts_tags' => null, + 'ar_id' => '2', + 'ar_namespace' => '0', + 'ar_title' => 'PageArchiveTest_thePage', + 'ar_text' => '', + 'ar_text_id' => '3', + 'ar_parent_id' => '2', + ], + $row1 + ); + $this->assertEquals( + [ + 'ar_minor_edit' => '0', + 'ar_user' => '0', + 'ar_user_text' => '127.0.0.1', + 'ar_len' => '7', + 'ar_deleted' => '0', + 'ar_rev_id' => '2', + 'ar_sha1' => 'pr0s8e18148pxhgjfa0gjrvpy8fiyxc', + 'ar_page_id' => '2', + 'ar_comment_text' => 'testing', + 'ar_comment_data' => null, + 'ar_comment_cid' => null, + 'ar_content_format' => null, + 'ar_content_model' => null, + 'ts_tags' => null, + 'ar_id' => '1', + 'ar_namespace' => '0', + 'ar_title' => 'PageArchiveTest_thePage', + 'ar_text' => '', + 'ar_text_id' => '2', + 'ar_parent_id' => '0', + ], + $row2 + ); + } + + /** + * @covers PageArchive::listPagesBySearch + */ + public function testListPagesBySearch() { + $pages = PageArchive::listPagesBySearch( 'PageArchiveTest_thePage' ); + $this->assertSame( 1, $pages->numRows() ); + + $page = (array)$pages->current(); + + $this->assertSame( + [ + 'ar_namespace' => '0', + 'ar_title' => 'PageArchiveTest_thePage', + 'count' => '2', + ], + $page + ); + } + + /** + * @covers PageArchive::listPagesBySearch + */ + public function testListPagesByPrefix() { + $pages = PageArchive::listPagesByPrefix( 'PageArchiveTest' ); + $this->assertSame( 1, $pages->numRows() ); + + $page = (array)$pages->current(); + + $this->assertSame( + [ + 'ar_namespace' => '0', + 'ar_title' => 'PageArchiveTest_thePage', + 'count' => '2', + ], + $page + ); + } + + /** + * @covers PageArchive::getTextFromRow + */ + public function testGetTextFromRow() { + $row = (object)[ 'ar_text_id' => 2 ]; + $text = $this->archivedPage->getTextFromRow( $row ); + $this->assertSame( 'testing', $text ); + } + + /** + * @covers PageArchive::getLastRevisionText + */ + public function testGetLastRevisionText() { + $text = $this->archivedPage->getLastRevisionText(); + $this->assertSame( 'Lorem Ipsum', $text ); + } + + /** + * @covers PageArchive::isDeleted + */ + public function testIsDeleted() { + $this->assertTrue( $this->archivedPage->isDeleted() ); + } } diff --git a/tests/phpunit/includes/PagePropsTest.php b/tests/phpunit/includes/PagePropsTest.php index 89fd6e032f..f602cdabcf 100644 --- a/tests/phpunit/includes/PagePropsTest.php +++ b/tests/phpunit/includes/PagePropsTest.php @@ -1,13 +1,15 @@ <?php /** + * @covers PageProps + * * @group Database * ^--- tell jenkins this test needs the database * * @group medium * ^--- tell phpunit that these test cases may take longer than 2 seconds. */ -class TestPageProps extends MediaWikiLangTestCase { +class PagePropsTest extends MediaWikiLangTestCase { /** * @var Title $title1 diff --git a/tests/phpunit/includes/RevisionDbTestBase.php b/tests/phpunit/includes/RevisionDbTestBase.php index 91dbf2cf70..94f8fba250 100644 --- a/tests/phpunit/includes/RevisionDbTestBase.php +++ b/tests/phpunit/includes/RevisionDbTestBase.php @@ -1,4 +1,8 @@ <?php +use MediaWiki\MediaWikiServices; +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\IncompleteRevisionException; +use MediaWiki\Storage\RevisionRecord; /** * RevisionDbTestBase contains test cases for the Revision class that have Database interactions. @@ -72,6 +76,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { MWNamespace::clearCaches(); // Reset namespace cache $wgContLang->resetNamespaces(); + if ( !$this->testPage ) { /** * We have to create a new page for each subclass as the page creation may result @@ -102,6 +107,14 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $props['text'] = 'Lorem Ipsum'; } + if ( !isset( $props['user_text'] ) ) { + $props['user_text'] = 'Tester'; + } + + if ( !isset( $props['user'] ) ) { + $props['user'] = 0; + } + if ( !isset( $props['comment'] ) ) { $props['comment'] = 'just a test'; } @@ -110,6 +123,10 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $props['page'] = $this->testPage->getId(); } + if ( !isset( $props['content_model'] ) ) { + $props['content_model'] = CONTENT_MODEL_WIKITEXT; + } + $rev = new Revision( $props ); $dbw = wfGetDB( DB_MASTER ); @@ -202,14 +219,23 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $revId = $rev->insertOn( wfGetDB( DB_MASTER ) ); $this->assertInternalType( 'integer', $revId ); - $this->assertInternalType( 'integer', $rev->getTextId() ); $this->assertSame( $revId, $rev->getId() ); + // getTextId() must be an int! + $this->assertInternalType( 'integer', $rev->getTextId() ); + + $mainSlot = $rev->getRevisionRecord()->getSlot( 'main', RevisionRecord::RAW ); + + // we currently only support storage in the text table + $textId = MediaWikiServices::getInstance() + ->getBlobStore() + ->getTextIdFromAddress( $mainSlot->getAddress() ); + $this->assertSelect( 'text', [ 'old_id', 'old_text' ], - "old_id = {$rev->getTextId()}", - [ [ strval( $rev->getTextId() ), 'Revision Text' ] ] + "old_id = $textId", + [ [ strval( $textId ), 'Revision Text' ] ] ); $this->assertSelect( 'revision', @@ -228,7 +254,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { [ [ strval( $rev->getId() ), strval( $this->testPage->getId() ), - strval( $rev->getTextId() ), + strval( $textId ), '0', '0', '0', @@ -246,11 +272,12 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { // If an ExternalStore is set don't use it. $this->setMwGlobals( 'wgDefaultExternalStore', false ); $this->setExpectedException( - MWException::class, - "Cannot insert revision: page ID must be nonzero" + IncompleteRevisionException::class, + "rev_page field must not be 0!" ); - $rev = new Revision( [] ); + $title = Title::newFromText( 'Nonexistant-' . __METHOD__ ); + $rev = new Revision( [], 0, $title ); $rev->insertOn( wfGetDB( DB_MASTER ) ); } @@ -321,12 +348,42 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { return $f + [ 'ar_namespace', 'ar_title' ]; }, ]; + yield [ + function ( $f ) { + unset( $f['ar_text'] ); + return $f; + }, + ]; yield [ function ( $f ) { unset( $f['ar_text_id'] ); return $f; }, ]; + yield [ + function ( $f ) { + unset( $f['ar_page_id'] ); + return $f; + }, + ]; + yield [ + function ( $f ) { + unset( $f['ar_parent_id'] ); + return $f; + }, + ]; + yield [ + function ( $f ) { + unset( $f['ar_rev_id'] ); + return $f; + }, + ]; + yield [ + function ( $f ) { + unset( $f['ar_sha1'] ); + return $f; + }, + ]; } /** @@ -334,6 +391,17 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { * @covers Revision::newFromArchiveRow */ public function testNewFromArchiveRow( $selectModifier ) { + $services = MediaWikiServices::getInstance(); + + $store = new RevisionStore( + $services->getDBLoadBalancer(), + $services->getService( '_SqlBlobStore' ), + $services->getMainWANObjectCache() + ); + + $store->setContentHandlerUseDB( $this->getContentHandlerUseDB() ); + $this->setService( 'RevisionStore', $store ); + $page = $this->createPage( 'RevisionStorageTest_testNewFromArchiveRow', 'Lorem Ipsum', @@ -354,6 +422,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $row = $res->fetchObject(); $res->free(); + // MCR migration note: $row is now required to contain ar_title and ar_namespace. + // Alternatively, a Title object can be passed to RevisionStore::newRevisionFromArchiveRow $rev = Revision::newFromArchiveRow( $row ); $this->assertRevEquals( $orig, $rev ); @@ -382,7 +452,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $row = $res->fetchObject(); $res->free(); - $rev = Revision::newFromArchiveRow( $row, [ 'comment' => 'SOMEOVERRIDE' ] ); + $rev = Revision::newFromArchiveRow( $row, [ 'comment_text' => 'SOMEOVERRIDE' ] ); $this->assertNotEquals( $orig->getComment(), $rev->getComment() ); $this->assertEquals( 'SOMEOVERRIDE', $rev->getComment() ); @@ -426,7 +496,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { * @covers Revision::newFromPageId */ public function testNewFromPageIdWithNotLatestId() { - $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ); + $content = new WikitextContent( __METHOD__ ); + $this->testPage->doEditContent( $content, __METHOD__ ); $rev = Revision::newFromPageId( $this->testPage->getId(), $this->testPage->getRevision()->getPrevious()->getId() @@ -447,6 +518,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ); $id = $this->testPage->getRevision()->getId(); + $this->hideDeprecated( 'Revision::fetchRevision' ); $res = Revision::fetchRevision( $this->testPage->getTitle() ); # note: order is unspecified @@ -455,8 +527,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $rows[$row->rev_id] = $row; } - $this->assertEquals( 1, count( $rows ), 'expected exactly one revision' ); - $this->assertArrayHasKey( $id, $rows, 'missing revision with id ' . $id ); + $this->assertEmpty( $rows, 'expected empty set' ); } /** @@ -541,6 +612,10 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'new null revision should have a different id from the original revision' ); $this->assertEquals( $orig->getTextId(), $rev->getTextId(), 'new null revision should have the same text id as the original revision' ); + $this->assertEquals( $orig->getSha1(), $rev->getSha1(), + 'new null revision should have the same SHA1 as the original revision' ); + $this->assertTrue( $orig->getRevisionRecord()->hasSameContent( $rev->getRevisionRecord() ), + 'new null revision should have the same content as the original revision' ); $this->assertEquals( __METHOD__, $rev->getContent()->getNativeData() ); } @@ -574,6 +649,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { } /** + * @covers Revision::userWasLastToEdit * @dataProvider provideUserWasLastToEdit */ public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) { @@ -606,7 +682,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'user' => $userA->getId(), 'text' => 'zero', 'content_model' => CONTENT_MODEL_WIKITEXT, - 'summary' => 'edit zero' + 'comment' => 'edit zero' ] ); $revisions[0]->insertOn( $dbw ); @@ -618,7 +694,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'user' => $userA->getId(), 'text' => 'one', 'content_model' => CONTENT_MODEL_WIKITEXT, - 'summary' => 'edit one' + 'comment' => 'edit one' ] ); $revisions[1]->insertOn( $dbw ); @@ -629,7 +705,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'user' => $userB->getId(), 'text' => 'two', 'content_model' => CONTENT_MODEL_WIKITEXT, - 'summary' => 'edit two' + 'comment' => 'edit two' ] ); $revisions[2]->insertOn( $dbw ); @@ -640,7 +716,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'user' => $userA->getId(), 'text' => 'three', 'content_model' => CONTENT_MODEL_WIKITEXT, - 'summary' => 'edit three' + 'comment' => 'edit three' ] ); $revisions[3]->insertOn( $dbw ); @@ -651,13 +727,24 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'user' => $userA->getId(), 'text' => 'zero', 'content_model' => CONTENT_MODEL_WIKITEXT, - 'summary' => 'edit four' + 'comment' => 'edit four' ] ); $revisions[4]->insertOn( $dbw ); // test it --------------------------------- $since = $revisions[$sinceIdx]->getTimestamp(); + $allRows = iterator_to_array( $dbw->select( + 'revision', + [ 'rev_id', 'rev_timestamp', 'rev_user' ], + [ + 'rev_page' => $page->getId(), + //'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $since ) ) + ], + __METHOD__, + [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] + ) ); + $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since ); $this->assertEquals( $expectedLast, $wasLast ); @@ -805,12 +892,16 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { 'text_id' => 123456789, // not in the test DB ] ); + MediaWiki\suppressWarnings(); // bad text_id will trigger a warning. + $this->assertNull( $rev->getContent(), "getContent() should return null if the revision's text blob could not be loaded." ); // NOTE: check this twice, once for lazy initialization, and once with the cached value. $this->assertNull( $rev->getContent(), "getContent() should return null if the revision's text blob could not be loaded." ); + + MediaWiki\suppressWarnings( 'end' ); } public function provideGetSize() { @@ -904,6 +995,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { */ public function testLoadFromId() { $rev = $this->testPage->getRevision(); + $this->hideDeprecated( 'Revision::loadFromId' ); $this->assertRevEquals( $rev, Revision::loadFromId( wfGetDB( DB_MASTER ), $rev->getId() ) @@ -1026,7 +1118,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $rev[1] = $this->testPage->getLatest(); $this->assertSame( - [ $rev[1] => strval( $textLength ) ], + [ $rev[1] => $textLength ], Revision::getParentLengths( wfGetDB( DB_MASTER ), [ $rev[1] ] @@ -1049,7 +1141,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $rev[2] = $this->testPage->getLatest(); $this->assertSame( - [ $rev[1] => strval( $textOneLength ), $rev[2] => strval( $textTwoLength ) ], + [ $rev[1] => $textOneLength, $rev[2] => $textTwoLength ], Revision::getParentLengths( wfGetDB( DB_MASTER ), [ $rev[1], $rev[2] ] @@ -1080,14 +1172,6 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { ); } - /** - * @covers Revision::getTitle - */ - public function testGetTitle_forBadRevision() { - $rev = new Revision( [] ); - $this->assertNull( $rev->getTitle() ); - } - /** * @covers Revision::isMinor */ @@ -1263,14 +1347,21 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { $rev = $this->testPage->getRevision(); // Clear any previous cache for the revision during creation - $key = $cache->makeGlobalKey( 'revision', $db->getDomainID(), $rev->getPage(), $rev->getId() ); + $key = $cache->makeGlobalKey( 'revision-row-1.29', + $db->getDomainID(), + $rev->getPage(), + $rev->getId() + ); $cache->delete( $key, WANObjectCache::HOLDOFF_NONE ); $this->assertFalse( $cache->get( $key ) ); // Get the new revision and make sure it is in the cache and correct $newRev = Revision::newKnownCurrent( $db, $rev->getPage(), $rev->getId() ); $this->assertRevEquals( $rev, $newRev ); - $this->assertRevEquals( $rev, $cache->get( $key ) ); + + $cachedRow = $cache->get( $key ); + $this->assertNotFalse( $cachedRow ); + $this->assertEquals( $rev->getId(), $cachedRow->rev_id ); } public function provideUserCanBitfield() { @@ -1377,7 +1468,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase { ] ); $user = $this->getTestUser( $userGroups )->getUser(); - $revision = new Revision( [ 'deleted' => $bitField ] ); + $revision = new Revision( [ 'deleted' => $bitField ], 0, $this->testPage->getTitle() ); $this->assertSame( $expected, diff --git a/tests/phpunit/includes/RevisionTest.php b/tests/phpunit/includes/RevisionTest.php index 3d0556ea32..0db76ff193 100644 --- a/tests/phpunit/includes/RevisionTest.php +++ b/tests/phpunit/includes/RevisionTest.php @@ -1,6 +1,10 @@ <?php -use Wikimedia\TestingAccessWrapper; +use MediaWiki\Storage\BlobStoreFactory; +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\SqlBlobStore; +use Wikimedia\Rdbms\IDatabase; +use Wikimedia\Rdbms\LoadBalancer; /** * Test cases in RevisionTest should not interact with the Database. @@ -20,20 +24,58 @@ class RevisionTest extends MediaWikiTestCase { 'content' => new JavaScriptContent( 'hellow world.' ) ], ]; + // FIXME: test with and without user ID, and with a user object. + // We can't prepare that here though, since we don't yet have a dummy DB + } + + /** + * @param string $model + * @return Title + */ + public function getMockTitle( $model = CONTENT_MODEL_WIKITEXT ) { + $mock = $this->getMockBuilder( Title::class ) + ->disableOriginalConstructor() + ->getMock(); + $mock->expects( $this->any() ) + ->method( 'getNamespace' ) + ->will( $this->returnValue( $this->getDefaultWikitextNS() ) ); + $mock->expects( $this->any() ) + ->method( 'getPrefixedText' ) + ->will( $this->returnValue( 'RevisionTest' ) ); + $mock->expects( $this->any() ) + ->method( 'getDBkey' ) + ->will( $this->returnValue( 'RevisionTest' ) ); + $mock->expects( $this->any() ) + ->method( 'getArticleID' ) + ->will( $this->returnValue( 23 ) ); + $mock->expects( $this->any() ) + ->method( 'getModel' ) + ->will( $this->returnValue( $model ) ); + + return $mock; } /** * @dataProvider provideConstructFromArray * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ - public function testConstructFromArray( array $rowArray ) { - $rev = new Revision( $rowArray ); + public function testConstructFromArray( $rowArray ) { + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertNotNull( $rev->getContent(), 'no content object available' ); $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() ); $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() ); } + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromEmptyArray() { + $rev = new Revision( [], 0, $this->getMockTitle() ); + $this->assertNull( $rev->getContent(), 'no content object should be available' ); + } + public function provideConstructFromArray_userSetAsExpected() { yield 'no user defaults to wgUser' => [ [ @@ -52,30 +94,20 @@ class RevisionTest extends MediaWikiTestCase { 99, 'SomeTextUserName', ]; - // Note: the below XXX test cases are odd and probably result in unexpected behaviour if used - // in production code. - yield 'XXX: user text only' => [ + yield 'user text only' => [ [ 'content' => new JavaScriptContent( 'hello world.' ), 'user_text' => '111.111.111.111', ], - null, + 0, '111.111.111.111', ]; - yield 'XXX: user id only' => [ - [ - 'content' => new JavaScriptContent( 'hello world.' ), - 'user' => 9989, - ], - 9989, - null, - ]; } /** * @dataProvider provideConstructFromArray_userSetAsExpected * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray * * @param array $rowArray * @param mixed $expectedUserId null to expect the current wgUser ID @@ -95,7 +127,7 @@ class RevisionTest extends MediaWikiTestCase { $expectedUserName = $testUser->getName(); } - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertEquals( $expectedUserId, $rev->getUser() ); $this->assertEquals( $expectedUserName, $rev->getUserText() ); } @@ -105,28 +137,37 @@ class RevisionTest extends MediaWikiTestCase { [ 'content' => new WikitextContent( 'GOAT' ), 'text_id' => 'someid', - ], + ], 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.' ) + new MWException( 'content field must contain a Content object.' ) ]; yield 'with bad content object (string)' => [ [ 'content' => 'ImAGoat' ], - new MWException( '`content` field must contain a Content object.' ) + new MWException( 'content field must contain a Content object.' ) ]; yield 'bad row format' => [ 'imastring, not a row', - new MWException( 'Revision constructor passed invalid row format.' ) + new InvalidArgumentException( + '$row must be a row object, an associative array, or a RevisionRecord' + ) ]; } /** * @dataProvider provideConstructFromArrayThrowsExceptions * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) { $this->setExpectedException( @@ -134,14 +175,25 @@ class RevisionTest extends MediaWikiTestCase { $expectedException->getMessage(), $expectedException->getCode() ); - new Revision( $rowArray ); + new Revision( $rowArray, 0, $this->getMockTitle() ); + } + + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromNothing() { + $this->setExpectedException( + InvalidArgumentException::class + ); + new Revision( [] ); } public function provideConstructFromRow() { yield 'Full construction' => [ [ - 'rev_id' => '2', - 'rev_page' => '1', + 'rev_id' => '42', + 'rev_page' => '23', 'rev_text_id' => '2', 'rev_timestamp' => '20171017114835', 'rev_user_text' => '127.0.0.1', @@ -158,8 +210,8 @@ class RevisionTest extends MediaWikiTestCase { 'rev_content_model' => 'GOATMODEL', ], function ( RevisionTest $testCase, Revision $rev ) { - $testCase->assertSame( 2, $rev->getId() ); - $testCase->assertSame( 1, $rev->getPage() ); + $testCase->assertSame( 42, $rev->getId() ); + $testCase->assertSame( 23, $rev->getPage() ); $testCase->assertSame( 2, $rev->getTextId() ); $testCase->assertSame( '20171017114835', $rev->getTimestamp() ); $testCase->assertSame( '127.0.0.1', $rev->getUserText() ); @@ -174,10 +226,10 @@ class RevisionTest extends MediaWikiTestCase { $testCase->assertSame( 'GOATMODEL', $rev->getContentModel() ); } ]; - yield 'null fields' => [ + yield 'default field values' => [ [ - 'rev_id' => '2', - 'rev_page' => '1', + 'rev_id' => '42', + 'rev_page' => '23', 'rev_text_id' => '2', 'rev_timestamp' => '20171017114835', 'rev_user_text' => '127.0.0.1', @@ -189,11 +241,24 @@ class RevisionTest extends MediaWikiTestCase { 'rev_comment_cid' => null, ], function ( RevisionTest $testCase, Revision $rev ) { - $testCase->assertNull( $rev->getSize() ); - $testCase->assertNull( $rev->getParentId() ); - $testCase->assertNull( $rev->getSha1() ); - $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat() ); - $testCase->assertSame( 'wikitext', $rev->getContentModel() ); + // parent ID may be null + $testCase->assertSame( null, $rev->getParentId(), 'revision id' ); + + // given fields + $testCase->assertSame( $rev->getTimestamp(), '20171017114835', 'timestamp' ); + $testCase->assertSame( $rev->getUserText(), '127.0.0.1', 'user name' ); + $testCase->assertSame( $rev->getUser(), 0, 'user id' ); + $testCase->assertSame( $rev->getComment(), 'Goat Comment!' ); + $testCase->assertSame( false, $rev->isMinor(), 'minor edit' ); + $testCase->assertSame( 0, $rev->getVisibility(), 'visibility flags' ); + + // computed fields + $testCase->assertNotNull( $rev->getSize(), 'size' ); + $testCase->assertNotNull( $rev->getSha1(), 'hash' ); + + // NOTE: model and format will be detected based on the namespace of the (mock) title + $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat(), 'format' ); + $testCase->assertSame( 'wikitext', $rev->getContentModel(), 'model' ); } ]; } @@ -201,11 +266,34 @@ class RevisionTest extends MediaWikiTestCase { /** * @dataProvider provideConstructFromRow * @covers Revision::__construct - * @covers Revision::constructFromDbRowObject + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ public function testConstructFromRow( array $arrayData, $assertions ) { + $data = 'Hello goat.'; // needs to match model and format + + $blobStore = $this->getMockBuilder( SqlBlobStore::class ) + ->disableOriginalConstructor() + ->getMock(); + + $blobStore->method( 'getBlob' ) + ->will( $this->returnValue( $data ) ); + + $blobStore->method( 'getTextIdFromAddress' ) + ->will( $this->returnCallback( + function ( $address ) { + // Turn "tt:1234" into 12345. + // Note that this must be functional so we can test getTextId(). + // Ideally, we'd un-mock getTextIdFromAddress and use its actual implementation. + $parts = explode( ':', $address ); + return (int)array_pop( $parts ); + } + ) ); + + // Note override internal service, so RevisionStore uses it as well. + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $row = (object)$arrayData; - $rev = new Revision( $row ); + $rev = new Revision( $row, 0, $this->getMockTitle() ); $assertions( $this, $rev ); } @@ -235,7 +323,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getId */ public function testGetId( $rowArray, $expectedId ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertEquals( $expectedId, $rev->getId() ); } @@ -249,7 +337,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::setId */ public function testSetId( $input, $expected ) { - $rev = new Revision( [] ); + $rev = new Revision( [], 0, $this->getMockTitle() ); $rev->setId( $input ); $this->assertSame( $expected, $rev->getId() ); } @@ -264,7 +352,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::setUserIdAndName */ public function testSetUserIdAndName( $inputId, $expectedId, $name ) { - $rev = new Revision( [] ); + $rev = new Revision( [], 0, $this->getMockTitle() ); $rev->setUserIdAndName( $inputId, $name ); $this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) ); $this->assertEquals( $name, $rev->getUserText( Revision::RAW ) ); @@ -281,7 +369,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getTextId() */ public function testGetTextId( $rowArray, $expected ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertSame( $expected, $rev->getTextId() ); } @@ -296,7 +384,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getParentId() */ public function testGetParentId( $rowArray, $expected ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertSame( $expected, $rev->getParentId() ); } @@ -329,9 +417,58 @@ class RevisionTest extends MediaWikiTestCase { $this->testGetRevisionText( $expected, $rowData ); } + private function getWANObjectCache() { + return new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ); + } + + /** + * @return SqlBlobStore + */ + private function getBlobStore() { + /** @var LoadBalancer $lb */ + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $cache = $this->getWANObjectCache(); + + $blobStore = new SqlBlobStore( $lb, $cache ); + return $blobStore; + } + + private function mockBlobStoreFactory( $blobStore ) { + /** @var LoadBalancer $lb */ + $factory = $this->getMockBuilder( BlobStoreFactory::class ) + ->disableOriginalConstructor() + ->getMock(); + $factory->expects( $this->any() ) + ->method( 'newBlobStore' ) + ->willReturn( $blobStore ); + $factory->expects( $this->any() ) + ->method( 'newSqlBlobStore' ) + ->willReturn( $blobStore ); + return $factory; + } + + /** + * @return RevisionStore + */ + private function getRevisionStore() { + /** @var LoadBalancer $lb */ + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $cache = $this->getWANObjectCache(); + + $blobStore = new RevisionStore( $lb, $this->getBlobStore(), $cache ); + return $blobStore; + } + public function provideGetRevisionTextWithLegacyEncoding() { yield 'Utf8Native' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'utf-8', @@ -340,6 +477,7 @@ class RevisionTest extends MediaWikiTestCase { ]; yield 'Utf8Legacy' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => '', @@ -352,8 +490,11 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText * @dataProvider provideGetRevisionTextWithLegacyEncoding */ - public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) { - $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + public function testGetRevisionWithLegacyEncoding( $expected, $lang, $encoding, $rowData ) { + $blobStore = $this->getBlobStore(); + $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $this->testGetRevisionText( $expected, $rowData ); } @@ -365,6 +506,7 @@ class RevisionTest extends MediaWikiTestCase { */ yield 'Utf8NativeGzip' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'gzip,utf-8', @@ -373,6 +515,7 @@ class RevisionTest extends MediaWikiTestCase { ]; yield 'Utf8LegacyGzip' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'gzip', @@ -385,9 +528,13 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding */ - public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) { + public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $lang, $encoding, $rowData ) { $this->checkPHPExtension( 'zlib' ); - $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + + $blobStore = $this->getBlobStore(); + $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $this->testGetRevisionText( $expected, $rowData ); } @@ -413,7 +560,10 @@ class RevisionTest extends MediaWikiTestCase { */ public function testCompressRevisionTextUtf8Gzip() { $this->checkPHPExtension( 'zlib' ); - $this->setMwGlobals( 'wgCompressRevisions', true ); + + $blobStore = $this->getBlobStore(); + $blobStore->setCompressBlobs( true ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $row = new stdClass; $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; @@ -428,20 +578,41 @@ class RevisionTest extends MediaWikiTestCase { Revision::getRevisionText( $row ), "getRevisionText" ); } - public function provideFetchFromConds() { - yield [ 0, [] ]; - yield [ Revision::READ_LOCKING, [ 'FOR UPDATE' ] ]; - } - /** - * @dataProvider provideFetchFromConds - * @covers Revision::fetchFromConds + * @covers Revision::loadFromTitle */ - public function testFetchFromConds( $flags, array $options ) { - $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); - $conditions = [ 'conditionsArray' ]; + public function testLoadFromTitle() { + $title = $this->getMockTitle(); + + $conditions = [ + 'rev_id=page_latest', + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey() + ]; + + $row = (object)[ + 'rev_id' => '42', + 'rev_page' => $title->getArticleID(), + 'rev_text_id' => '2', + 'rev_timestamp' => '20171017114835', + 'rev_user_text' => '127.0.0.1', + 'rev_user' => '0', + 'rev_minor_edit' => '0', + 'rev_deleted' => '0', + 'rev_len' => '46', + 'rev_parent_id' => '1', + 'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'rev_comment_text' => 'Goat Comment!', + 'rev_comment_data' => null, + 'rev_comment_cid' => null, + 'rev_content_format' => 'GOATFORMAT', + 'rev_content_model' => 'GOATMODEL', + ]; $db = $this->getMock( IDatabase::class ); + $db->expects( $this->any() ) + ->method( 'getDomainId' ) + ->will( $this->returnValue( wfWikiID() ) ); $db->expects( $this->once() ) ->method( 'selectRow' ) ->with( @@ -450,17 +621,24 @@ class RevisionTest extends MediaWikiTestCase { $this->isType( 'array' ), $this->equalTo( $conditions ), // Method name - $this->equalTo( 'Revision::fetchFromConds' ), - $this->equalTo( $options ), + $this->stringContains( 'fetchRevisionRowFromConds' ), + // We don't really care about the options here + $this->isType( 'array' ), // We don't really care about the join conds are they come from the joinCond methods $this->isType( 'array' ) ) - ->willReturn( 'RETURNVALUE' ); + ->willReturn( $row ); - $wrapper = TestingAccessWrapper::newFromClass( Revision::class ); - $result = $wrapper->fetchFromConds( $db, $conditions, $flags ); + $revision = Revision::loadFromTitle( $db, $title ); - $this->assertEquals( 'RETURNVALUE', $result ); + $this->assertEquals( $title->getArticleID(), $revision->getTitle()->getArticleID() ); + $this->assertEquals( $row->rev_id, $revision->getId() ); + $this->assertEquals( $row->rev_len, $revision->getSize() ); + $this->assertEquals( $row->rev_sha1, $revision->getSha1() ); + $this->assertEquals( $row->rev_parent_id, $revision->getParentId() ); + $this->assertEquals( $row->rev_timestamp, $revision->getTimestamp() ); + $this->assertEquals( $row->rev_comment_text, $revision->getComment() ); + $this->assertEquals( $row->rev_user_text, $revision->getUserText() ); } public function provideDecompressRevisionText() { @@ -491,25 +669,25 @@ class RevisionTest extends MediaWikiTestCase { ]; yield '(ISO-8859-1 encoding), string in string out' => [ 'ISO-8859-1', - iconv( 'utf8', 'ISO-8859-1', "1®Àþ1" ), + iconv( 'utf-8', 'ISO-8859-1', "1®Àþ1" ), [], '1®Àþ1', ]; yield '(ISO-8859-1 encoding), serialized object in with gzip flags returns string' => [ 'ISO-8859-1', - gzdeflate( iconv( 'utf8', 'ISO-8859-1', "4®Àþ4" ) ), + gzdeflate( iconv( 'utf-8', 'ISO-8859-1', "4®Àþ4" ) ), [ 'gzip' ], '4®Àþ4', ]; yield '(ISO-8859-1 encoding), serialized object in with object flags returns string' => [ 'ISO-8859-1', - serialize( new TitleValue( 0, iconv( 'utf8', 'ISO-8859-1', "3®Àþ3" ) ) ), + serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "3®Àþ3" ) ) ), [ 'object' ], '3®Àþ3', ]; yield '(ISO-8859-1 encoding), serialized object in with object & gzip flags returns string' => [ 'ISO-8859-1', - gzdeflate( serialize( new TitleValue( 0, iconv( 'utf8', 'ISO-8859-1', "2®Àþ2" ) ) ) ), + gzdeflate( serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "2®Àþ2" ) ) ) ), [ 'gzip', 'object' ], '2®Àþ2', ]; @@ -525,8 +703,12 @@ class RevisionTest extends MediaWikiTestCase { * @param mixed $expected */ public function testDecompressRevisionText( $legacyEncoding, $text, $flags, $expected ) { - $this->setMwGlobals( 'wgLegacyEncoding', $legacyEncoding ); - $this->setMwGlobals( 'wgLanguageCode', 'en' ); + $blobStore = $this->getBlobStore(); + if ( $legacyEncoding ) { + $blobStore->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) ); + } + + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $this->assertSame( $expected, Revision::decompressRevisionText( $text, $flags ) @@ -622,14 +804,20 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText */ public function testGetRevisionText_external_oldId() { - $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ); + $cache = $this->getWANObjectCache(); $this->setService( 'MainWANObjectCache', $cache ); + $this->setService( 'ExternalStoreFactory', new ExternalStoreFactory( [ 'ForTesting' ] ) ); - $cacheKey = $cache->makeKey( 'revisiontext', 'textid', '7777' ); + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $blobStore = new SqlBlobStore( $lb, $cache ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $this->assertSame( 'AAAABBAAA', @@ -641,6 +829,8 @@ class RevisionTest extends MediaWikiTestCase { ] ) ); + + $cacheKey = $cache->makeKey( 'revisiontext', 'textid', 'tt:7777' ); $this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) ); } @@ -836,6 +1026,8 @@ class RevisionTest extends MediaWikiTestCase { 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', @@ -864,6 +1056,8 @@ class RevisionTest extends MediaWikiTestCase { 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', @@ -897,6 +1091,8 @@ class RevisionTest extends MediaWikiTestCase { 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', @@ -933,6 +1129,8 @@ class RevisionTest extends MediaWikiTestCase { 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', @@ -969,6 +1167,8 @@ class RevisionTest extends MediaWikiTestCase { 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', @@ -1000,6 +1200,11 @@ class RevisionTest extends MediaWikiTestCase { */ public function testGetArchiveQueryInfo( $globals, $expected ) { $this->setMwGlobals( $globals ); + + $revisionStore = $this->getRevisionStore(); + $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] ); + $this->setService( 'RevisionStore', $revisionStore ); + $this->assertEquals( $expected, Revision::getArchiveQueryInfo() @@ -1351,6 +1556,11 @@ class RevisionTest extends MediaWikiTestCase { */ public function testGetQueryInfo( $globals, $options, $expected ) { $this->setMwGlobals( $globals ); + + $revisionStore = $this->getRevisionStore(); + $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] ); + $this->setService( 'RevisionStore', $revisionStore ); + $this->assertEquals( $expected, Revision::getQueryInfo( $options ) diff --git a/tests/phpunit/includes/SampleTest.php b/tests/phpunit/includes/SampleTest.php index 02935a5369..31eebe3c0e 100644 --- a/tests/phpunit/includes/SampleTest.php +++ b/tests/phpunit/includes/SampleTest.php @@ -1,6 +1,6 @@ <?php -class TestSample extends MediaWikiLangTestCase { +class SampleTest extends MediaWikiLangTestCase { /** * Anything that needs to happen before your tests should go here. diff --git a/tests/phpunit/includes/Storage/BlobStoreFactoryTest.php b/tests/phpunit/includes/Storage/BlobStoreFactoryTest.php new file mode 100644 index 0000000000..46ba7a5da1 --- /dev/null +++ b/tests/phpunit/includes/Storage/BlobStoreFactoryTest.php @@ -0,0 +1,46 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use MediaWiki\MediaWikiServices; +use MediaWiki\Storage\BlobStore; +use MediaWiki\Storage\SqlBlobStore; +use MediaWikiTestCase; +use Wikimedia\TestingAccessWrapper; + +/** + * @covers MediaWiki\Storage\BlobStore + */ +class BlobStoreFactoryTest extends MediaWikiTestCase { + + public function provideWikiIds() { + yield [ false ]; + yield [ 'someWiki' ]; + } + + /** + * @dataProvider provideWikiIds + */ + public function testNewBlobStore( $wikiId ) { + $factory = MediaWikiServices::getInstance()->getBlobStoreFactory(); + $store = $factory->newBlobStore( $wikiId ); + $this->assertInstanceOf( BlobStore::class, $store ); + + // This only works as we currently know this is a SqlBlobStore object + $wrapper = TestingAccessWrapper::newFromObject( $store ); + $this->assertEquals( $wikiId, $wrapper->wikiId ); + } + + /** + * @dataProvider provideWikiIds + */ + public function testNewSqlBlobStore( $wikiId ) { + $factory = MediaWikiServices::getInstance()->getBlobStoreFactory(); + $store = $factory->newSqlBlobStore( $wikiId ); + $this->assertInstanceOf( SqlBlobStore::class, $store ); + + $wrapper = TestingAccessWrapper::newFromObject( $store ); + $this->assertEquals( $wikiId, $wrapper->wikiId ); + } + +} diff --git a/tests/phpunit/includes/Storage/MutableRevisionRecordTest.php b/tests/phpunit/includes/Storage/MutableRevisionRecordTest.php new file mode 100644 index 0000000000..79cac5ebab --- /dev/null +++ b/tests/phpunit/includes/Storage/MutableRevisionRecordTest.php @@ -0,0 +1,120 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use CommentStoreComment; +use MediaWiki\Storage\MutableRevisionRecord; +use MediaWiki\Storage\RevisionAccessException; +use MediaWiki\Storage\RevisionRecord; +use MediaWiki\Storage\SlotRecord; +use MediaWikiTestCase; +use Title; +use WikitextContent; + +/** + * @covers \MediaWiki\Storage\MutableRevisionRecord + */ +class MutableRevisionRecordTest extends MediaWikiTestCase { + + public function testSimpleSetGetId() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertNull( $record->getId() ); + $record->setId( 888 ); + $this->assertSame( 888, $record->getId() ); + } + + public function testSimpleSetGetUser() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $user = $this->getTestSysop()->getUser(); + $this->assertNull( $record->getUser() ); + $record->setUser( $user ); + $this->assertSame( $user, $record->getUser() ); + } + + public function testSimpleSetGetPageId() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertSame( 0, $record->getPageId() ); + $record->setPageId( 999 ); + $this->assertSame( 999, $record->getPageId() ); + } + + public function testSimpleSetGetParentId() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertNull( $record->getParentId() ); + $record->setParentId( 100 ); + $this->assertSame( 100, $record->getParentId() ); + } + + public function testSimpleGetMainContentWhenEmpty() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->setExpectedException( RevisionAccessException::class ); + $this->assertNull( $record->getContent( 'main' ) ); + } + + public function testSimpleSetGetMainContent() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $content = new WikitextContent( 'Badger' ); + $record->setContent( 'main', $content ); + $this->assertSame( $content, $record->getContent( 'main' ) ); + } + + public function testSimpleGetSlotWhenEmpty() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->setExpectedException( RevisionAccessException::class ); + $record->getSlot( 'main' ); + } + + public function testSimpleSetGetSlot() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $slot = new SlotRecord( + (object)[ 'role_name' => 'main' ], + new WikitextContent( 'x' ) + ); + $record->setSlot( $slot ); + $this->assertSame( $slot, $record->getSlot( 'main' ) ); + } + + public function testSimpleSetGetMinor() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertFalse( $record->isMinor() ); + $record->setMinorEdit( true ); + $this->assertSame( true, $record->isMinor() ); + } + + public function testSimpleSetGetTimestamp() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertNull( $record->getTimestamp() ); + $record->setTimestamp( '20180101010101' ); + $this->assertSame( '20180101010101', $record->getTimestamp() ); + } + + public function testSimpleSetGetVisibility() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertSame( 0, $record->getVisibility() ); + $record->setVisibility( RevisionRecord::DELETED_USER ); + $this->assertSame( RevisionRecord::DELETED_USER, $record->getVisibility() ); + } + + public function testSimpleSetGetSha1() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertSame( 'phoiac9h4m842xq45sp7s6u21eteeq1', $record->getSha1() ); + $record->setSha1( 'someHash' ); + $this->assertSame( 'someHash', $record->getSha1() ); + } + + public function testSimpleSetGetSize() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $this->assertSame( 0, $record->getSize() ); + $record->setSize( 775 ); + $this->assertSame( 775, $record->getSize() ); + } + + public function testSimpleSetGetComment() { + $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) ); + $comment = new CommentStoreComment( 1, 'foo' ); + $this->assertNull( $record->getComment() ); + $record->setComment( $comment ); + $this->assertSame( $comment, $record->getComment() ); + } + +} diff --git a/tests/phpunit/includes/Storage/MutableRevisionSlotsTest.php b/tests/phpunit/includes/Storage/MutableRevisionSlotsTest.php new file mode 100644 index 0000000000..c2a275fe8e --- /dev/null +++ b/tests/phpunit/includes/Storage/MutableRevisionSlotsTest.php @@ -0,0 +1,75 @@ +<?php + +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 { + + public function testSetMultipleSlots() { + $slots = new MutableRevisionSlots(); + + $this->assertSame( [], $slots->getSlots() ); + + $slotA = SlotRecord::newUnsaved( 'some', new WikitextContent( 'A' ) ); + $slots->setSlot( $slotA ); + $this->assertSame( $slotA, $slots->getSlot( 'some' ) ); + $this->assertSame( [ 'some' => $slotA ], $slots->getSlots() ); + + $slotB = SlotRecord::newUnsaved( 'other', new WikitextContent( 'B' ) ); + $slots->setSlot( $slotB ); + $this->assertSame( $slotB, $slots->getSlot( 'other' ) ); + $this->assertSame( [ 'some' => $slotA, 'other' => $slotB ], $slots->getSlots() ); + } + + public function testSetExistingSlotOverwritesSlot() { + $slots = new MutableRevisionSlots(); + + $this->assertSame( [], $slots->getSlots() ); + + $slotA = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) ); + $slots->setSlot( $slotA ); + $this->assertSame( $slotA, $slots->getSlot( 'main' ) ); + $this->assertSame( [ 'main' => $slotA ], $slots->getSlots() ); + + $slotB = SlotRecord::newUnsaved( 'main', new WikitextContent( 'B' ) ); + $slots->setSlot( $slotB ); + $this->assertSame( $slotB, $slots->getSlot( 'main' ) ); + $this->assertSame( [ 'main' => $slotB ], $slots->getSlots() ); + } + + public function testSetContentOfExistingSlotOverwritesContent() { + $slots = new MutableRevisionSlots(); + + $this->assertSame( [], $slots->getSlots() ); + + $slotA = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) ); + $slots->setSlot( $slotA ); + $this->assertSame( $slotA, $slots->getSlot( 'main' ) ); + $this->assertSame( [ 'main' => $slotA ], $slots->getSlots() ); + + $newContent = new WikitextContent( 'B' ); + $slots->setContent( 'main', $newContent ); + $this->assertSame( $newContent, $slots->getContent( 'main' ) ); + } + + public function testRemoveExistingSlot() { + $slotA = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) ); + $slots = new MutableRevisionSlots( [ $slotA ] ); + + $this->assertSame( [ 'main' => $slotA ], $slots->getSlots() ); + + $slots->removeSlot( 'main' ); + $this->assertSame( [], $slots->getSlots() ); + $this->setExpectedException( RevisionAccessException::class ); + $slots->getSlot( 'main' ); + } + +} diff --git a/tests/phpunit/includes/Storage/RevisionSlotsTest.php b/tests/phpunit/includes/Storage/RevisionSlotsTest.php new file mode 100644 index 0000000000..4dfae4bb8d --- /dev/null +++ b/tests/phpunit/includes/Storage/RevisionSlotsTest.php @@ -0,0 +1,117 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use MediaWiki\Storage\RevisionAccessException; +use MediaWiki\Storage\RevisionSlots; +use MediaWiki\Storage\SlotRecord; +use MediaWikiTestCase; +use WikitextContent; + +class RevisionSlotsTest extends MediaWikiTestCase { + + /** + * @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 ] ); + + $this->assertSame( $mainSlot, $slots->getSlot( 'main' ) ); + $this->assertSame( $auxSlot, $slots->getSlot( 'aux' ) ); + $this->setExpectedException( RevisionAccessException::class ); + $slots->getSlot( 'nothere' ); + } + + /** + * @covers \MediaWiki\Storage\RevisionSlots::getContent + */ + public function testGetContent() { + $mainContent = new WikitextContent( 'A' ); + $auxContent = new WikitextContent( 'B' ); + $mainSlot = SlotRecord::newUnsaved( 'main', $mainContent ); + $auxSlot = SlotRecord::newUnsaved( 'aux', $auxContent ); + $slots = new RevisionSlots( [ $mainSlot, $auxSlot ] ); + + $this->assertSame( $mainContent, $slots->getContent( 'main' ) ); + $this->assertSame( $auxContent, $slots->getContent( 'aux' ) ); + $this->setExpectedException( RevisionAccessException::class ); + $slots->getContent( 'nothere' ); + } + + /** + * @covers \MediaWiki\Storage\RevisionSlots::getSlotRoles + */ + public function testGetSlotRoles_someSlots() { + $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) ); + $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) ); + $slots = new RevisionSlots( [ $mainSlot, $auxSlot ] ); + + $this->assertSame( [ 'main', 'aux' ], $slots->getSlotRoles() ); + } + + /** + * @covers \MediaWiki\Storage\RevisionSlots::getSlotRoles + */ + public function testGetSlotRoles_noSlots() { + $slots = new RevisionSlots( [] ); + + $this->assertSame( [], $slots->getSlotRoles() ); + } + + /** + * @covers \MediaWiki\Storage\RevisionSlots::getSlots + */ + public function testGetSlots() { + $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) ); + $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) ); + $slotsArray = [ $mainSlot, $auxSlot ]; + $slots = new RevisionSlots( $slotsArray ); + + $this->assertEquals( [ 'main' => $mainSlot, 'aux' => $auxSlot ], $slots->getSlots() ); + } + + public function provideComputeSize() { + yield [ 1, [ 'A' ] ]; + yield [ 2, [ 'AA' ] ]; + yield [ 4, [ 'AA', 'X', 'H' ] ]; + } + + /** + * @dataProvider provideComputeSize + * @covers \MediaWiki\Storage\RevisionSlots::computeSize + */ + public function testComputeSize( $expected, $contentStrings ) { + $slotsArray = []; + foreach ( $contentStrings as $key => $contentString ) { + $slotsArray[] = SlotRecord::newUnsaved( strval( $key ), new WikitextContent( $contentString ) ); + } + $slots = new RevisionSlots( $slotsArray ); + + $this->assertSame( $expected, $slots->computeSize() ); + } + + public function provideComputeSha1() { + yield [ 'ctqm7794fr2dp1taki8a88ovwnvmnmj', [ 'A' ] ]; + yield [ 'eyq8wiwlcofnaiy4eid97gyfy60uw51', [ 'AA' ] ]; + yield [ 'lavctqfpxartyjr31f853drgfl4kj1g', [ 'AA', 'X', 'H' ] ]; + } + + /** + * @dataProvider provideComputeSha1 + * @covers \MediaWiki\Storage\RevisionSlots::computeSha1 + * @note this test is a bit brittle as the hashes are hardcoded, perhaps just check that strings + * are returned and different Slots objects return different strings? + */ + public function testComputeSha1( $expected, $contentStrings ) { + $slotsArray = []; + foreach ( $contentStrings as $key => $contentString ) { + $slotsArray[] = SlotRecord::newUnsaved( strval( $key ), new WikitextContent( $contentString ) ); + } + $slots = new RevisionSlots( $slotsArray ); + + $this->assertSame( $expected, $slots->computeSha1() ); + } + +} diff --git a/tests/phpunit/includes/Storage/RevisionStoreDbTest.php b/tests/phpunit/includes/Storage/RevisionStoreDbTest.php new file mode 100644 index 0000000000..ee8fdc781f --- /dev/null +++ b/tests/phpunit/includes/Storage/RevisionStoreDbTest.php @@ -0,0 +1,994 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use CommentStoreComment; +use Exception; +use InvalidArgumentException; +use MediaWiki\Linker\LinkTarget; +use MediaWiki\MediaWikiServices; +use MediaWiki\Storage\IncompleteRevisionException; +use MediaWiki\Storage\MutableRevisionRecord; +use MediaWiki\Storage\RevisionRecord; +use MediaWiki\Storage\SlotRecord; +use MediaWikiTestCase; +use Revision; +use TestUserRegistry; +use Title; +use WikiPage; +use WikitextContent; + +/** + * @group Database + */ +class RevisionStoreDbTest extends MediaWikiTestCase { + + private function assertLinkTargetsEqual( LinkTarget $l1, LinkTarget $l2 ) { + $this->assertEquals( $l1->getDBkey(), $l2->getDBkey() ); + $this->assertEquals( $l1->getNamespace(), $l2->getNamespace() ); + $this->assertEquals( $l1->getFragment(), $l2->getFragment() ); + $this->assertEquals( $l1->getInterwiki(), $l2->getInterwiki() ); + } + + private function assertRevisionRecordsEqual( RevisionRecord $r1, RevisionRecord $r2 ) { + $this->assertEquals( $r1->getUser()->getName(), $r2->getUser()->getName() ); + $this->assertEquals( $r1->getUser()->getId(), $r2->getUser()->getId() ); + $this->assertEquals( $r1->getComment(), $r2->getComment() ); + $this->assertEquals( $r1->getPageAsLinkTarget(), $r2->getPageAsLinkTarget() ); + $this->assertEquals( $r1->getTimestamp(), $r2->getTimestamp() ); + $this->assertEquals( $r1->getVisibility(), $r2->getVisibility() ); + $this->assertEquals( $r1->getSha1(), $r2->getSha1() ); + $this->assertEquals( $r1->getParentId(), $r2->getParentId() ); + $this->assertEquals( $r1->getSize(), $r2->getSize() ); + $this->assertEquals( $r1->getPageId(), $r2->getPageId() ); + $this->assertEquals( $r1->getSlotRoles(), $r2->getSlotRoles() ); + $this->assertEquals( $r1->getWikiId(), $r2->getWikiId() ); + $this->assertEquals( $r1->isMinor(), $r2->isMinor() ); + foreach ( $r1->getSlotRoles() as $role ) { + $this->assertEquals( $r1->getSlot( $role ), $r2->getSlot( $role ) ); + $this->assertEquals( $r1->getContent( $role ), $r2->getContent( $role ) ); + } + foreach ( [ + RevisionRecord::DELETED_TEXT, + RevisionRecord::DELETED_COMMENT, + RevisionRecord::DELETED_USER, + RevisionRecord::DELETED_RESTRICTED, + ] as $field ) { + $this->assertEquals( $r1->isDeleted( $field ), $r2->isDeleted( $field ) ); + } + } + + /** + * @param mixed[] $details + * + * @return RevisionRecord + */ + private function getRevisionRecordFromDetailsArray( $title, $details = [] ) { + // Convert some values that can't be provided by dataProviders + $page = WikiPage::factory( $title ); + if ( isset( $details['user'] ) && $details['user'] === true ) { + $details['user'] = $this->getTestUser()->getUser(); + } + if ( isset( $details['page'] ) && $details['page'] === true ) { + $details['page'] = $page->getId(); + } + if ( isset( $details['parent'] ) && $details['parent'] === true ) { + $details['parent'] = $page->getLatest(); + } + + // Create the RevisionRecord with any available data + $rev = new MutableRevisionRecord( $title ); + isset( $details['slot'] ) ? $rev->setSlot( $details['slot'] ) : null; + isset( $details['parent'] ) ? $rev->setParentId( $details['parent'] ) : null; + isset( $details['page'] ) ? $rev->setPageId( $details['page'] ) : null; + isset( $details['size'] ) ? $rev->setSize( $details['size'] ) : null; + isset( $details['sha1'] ) ? $rev->setSha1( $details['sha1'] ) : null; + isset( $details['comment'] ) ? $rev->setComment( $details['comment'] ) : null; + isset( $details['timestamp'] ) ? $rev->setTimestamp( $details['timestamp'] ) : null; + isset( $details['minor'] ) ? $rev->setMinorEdit( $details['minor'] ) : null; + isset( $details['user'] ) ? $rev->setUser( $details['user'] ) : null; + isset( $details['visibility'] ) ? $rev->setVisibility( $details['visibility'] ) : null; + isset( $details['id'] ) ? $rev->setId( $details['id'] ) : null; + + return $rev; + } + + private function getRandomCommentStoreComment() { + return CommentStoreComment::newUnsavedComment( __METHOD__ . '.' . rand( 0, 1000 ) ); + } + + public function provideInsertRevisionOn_successes() { + yield 'Bare minimum revision insertion' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'parent' => true, + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + 'user' => true, + ], + ]; + yield 'Detailed revision insertion' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'parent' => true, + 'page' => true, + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + 'user' => true, + 'minor' => true, + 'visibility' => RevisionRecord::DELETED_RESTRICTED, + ], + ]; + } + + /** + * @dataProvider provideInsertRevisionOn_successes + * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn + */ + public function testInsertRevisionOn_successes( Title $title, array $revDetails = [] ) { + $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails ); + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $return = $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) ); + + $this->assertLinkTargetsEqual( $title, $return->getPageAsLinkTarget() ); + $this->assertRevisionRecordsEqual( $rev, $return ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn + */ + public function testInsertRevisionOn_blobAddressExists() { + $title = Title::newFromText( 'UTPage' ); + $revDetails = [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'parent' => true, + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + 'user' => true, + ]; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + + // Insert the first revision + $revOne = $this->getRevisionRecordFromDetailsArray( $title, $revDetails ); + $firstReturn = $store->insertRevisionOn( $revOne, wfGetDB( DB_MASTER ) ); + $this->assertLinkTargetsEqual( $title, $firstReturn->getPageAsLinkTarget() ); + $this->assertRevisionRecordsEqual( $revOne, $firstReturn ); + + // Insert a second revision inheriting the same blob address + $revDetails['slot'] = SlotRecord::newInherited( $firstReturn->getSlot( 'main' ) ); + $revTwo = $this->getRevisionRecordFromDetailsArray( $title, $revDetails ); + $secondReturn = $store->insertRevisionOn( $revTwo, wfGetDB( DB_MASTER ) ); + $this->assertLinkTargetsEqual( $title, $secondReturn->getPageAsLinkTarget() ); + $this->assertRevisionRecordsEqual( $revTwo, $secondReturn ); + + // Assert that the same blob address has been used. + $this->assertEquals( + $firstReturn->getSlot( 'main' )->getAddress(), + $secondReturn->getSlot( 'main' )->getAddress() + ); + // And that different revisions have been created. + $this->assertNotSame( + $firstReturn->getId(), + $secondReturn->getId() + ); + } + + public function provideInsertRevisionOn_failures() { + yield 'no slot' => [ + Title::newFromText( 'UTPage' ), + [ + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + 'user' => true, + ], + new InvalidArgumentException( 'At least one slot needs to be defined!' ) + ]; + yield 'slot that is not main slot' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'lalala', new WikitextContent( 'Chicken' ) ), + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + 'user' => true, + ], + new InvalidArgumentException( 'Only the main slot is supported for now!' ) + ]; + yield 'no timestamp' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'comment' => $this->getRandomCommentStoreComment(), + 'user' => true, + ], + new IncompleteRevisionException( 'timestamp field must not be NULL!' ) + ]; + yield 'no comment' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'timestamp' => '20171117010101', + 'user' => true, + ], + new IncompleteRevisionException( 'comment must not be NULL!' ) + ]; + yield 'no user' => [ + Title::newFromText( 'UTPage' ), + [ + 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ), + 'comment' => $this->getRandomCommentStoreComment(), + 'timestamp' => '20171117010101', + ], + new IncompleteRevisionException( 'user must not be NULL!' ) + ]; + } + + /** + * @dataProvider provideInsertRevisionOn_failures + * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn + */ + public function testInsertRevisionOn_failures( + Title $title, + array $revDetails = [], + Exception $exception ) { + $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails ); + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + + $this->setExpectedException( + get_class( $exception ), + $exception->getMessage(), + $exception->getCode() + ); + $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) ); + } + + public function provideNewNullRevision() { + yield [ + Title::newFromText( 'UTPage' ), + CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment1' ), + true, + ]; + yield [ + Title::newFromText( 'UTPage' ), + CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment2', [ 'a' => 1 ] ), + false, + ]; + } + + /** + * @dataProvider provideNewNullRevision + * @covers \MediaWiki\Storage\RevisionStore::newNullRevision + */ + public function testNewNullRevision( Title $title, $comment, $minor ) { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser(); + $record = $store->newNullRevision( + wfGetDB( DB_MASTER ), + $title, + $comment, + $minor, + $user + ); + + $this->assertEquals( $title->getNamespace(), $record->getPageAsLinkTarget()->getNamespace() ); + $this->assertEquals( $title->getDBkey(), $record->getPageAsLinkTarget()->getDBkey() ); + $this->assertEquals( $comment, $record->getComment() ); + $this->assertEquals( $minor, $record->isMinor() ); + $this->assertEquals( $user->getName(), $record->getUser()->getName() ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::newNullRevision + */ + public function testNewNullRevision_nonExistingTitle() { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $record = $store->newNullRevision( + wfGetDB( DB_MASTER ), + Title::newFromText( __METHOD__ . '.iDontExist!' ), + CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment' ), + false, + TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser() + ); + $this->assertNull( $record ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::isUnpatrolled + */ + public function testIsUnpatrolled_returnsRecentChangesId() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $status = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revisionRecord = $store->getRevisionById( $rev->getId() ); + $result = $store->isUnpatrolled( $revisionRecord ); + + $this->assertGreaterThan( 0, $result ); + $this->assertSame( + $page->getRevision()->getRecentChange()->getAttribute( 'rc_id' ), + $result + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::isUnpatrolled + */ + public function testIsUnpatrolled_returnsZeroIfPatrolled() { + // This assumes that sysops are auto patrolled + $sysop = $this->getTestSysop()->getUser(); + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $status = $page->doEditContent( + new WikitextContent( __METHOD__ ), + __METHOD__, + 0, + false, + $sysop + ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revisionRecord = $store->getRevisionById( $rev->getId() ); + $result = $store->isUnpatrolled( $revisionRecord ); + + $this->assertSame( 0, $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getRecentChange + */ + public function testGetRecentChange() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $content = new WikitextContent( __METHOD__ ); + $status = $page->doEditContent( $content, __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revRecord = $store->getRevisionById( $rev->getId() ); + $recentChange = $store->getRecentChange( $revRecord ); + + $this->assertEquals( $rev->getId(), $recentChange->getAttribute( 'rc_this_oldid' ) ); + $this->assertEquals( $rev->getRecentChange(), $recentChange ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getRevisionById + */ + public function testGetRevisionById() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $content = new WikitextContent( __METHOD__ ); + $status = $page->doEditContent( $content, __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revRecord = $store->getRevisionById( $rev->getId() ); + + $this->assertSame( $rev->getId(), $revRecord->getId() ); + $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) ); + $this->assertSame( __METHOD__, $revRecord->getComment()->text ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getRevisionByTitle + */ + public function testGetRevisionByTitle() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $content = new WikitextContent( __METHOD__ ); + $status = $page->doEditContent( $content, __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revRecord = $store->getRevisionByTitle( $page->getTitle() ); + + $this->assertSame( $rev->getId(), $revRecord->getId() ); + $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) ); + $this->assertSame( __METHOD__, $revRecord->getComment()->text ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getRevisionByPageId + */ + public function testGetRevisionByPageId() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $content = new WikitextContent( __METHOD__ ); + $status = $page->doEditContent( $content, __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revRecord = $store->getRevisionByPageId( $page->getId() ); + + $this->assertSame( $rev->getId(), $revRecord->getId() ); + $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) ); + $this->assertSame( __METHOD__, $revRecord->getComment()->text ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getRevisionFromTimestamp + */ + public function testGetRevisionFromTimestamp() { + // Make sure there is 1 second between the last revision and the rev we create... + // Otherwise we might not get the correct revision and the test may fail... + // :( + sleep( 1 ); + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $content = new WikitextContent( __METHOD__ ); + $status = $page->doEditContent( $content, __METHOD__ ); + /** @var Revision $rev */ + $rev = $status->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $revRecord = $store->getRevisionFromTimestamp( + $page->getTitle(), + $rev->getTimestamp() + ); + + $this->assertSame( $rev->getId(), $revRecord->getId() ); + $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) ); + $this->assertSame( __METHOD__, $revRecord->getComment()->text ); + } + + private function revisionToRow( Revision $rev ) { + $page = WikiPage::factory( $rev->getTitle() ); + + return (object)[ + 'rev_id' => (string)$rev->getId(), + 'rev_page' => (string)$rev->getPage(), + 'rev_text_id' => (string)$rev->getTextId(), + 'rev_timestamp' => (string)$rev->getTimestamp(), + 'rev_user_text' => (string)$rev->getUserText(), + 'rev_user' => (string)$rev->getUser(), + 'rev_minor_edit' => $rev->isMinor() ? '1' : '0', + 'rev_deleted' => (string)$rev->getVisibility(), + 'rev_len' => (string)$rev->getSize(), + 'rev_parent_id' => (string)$rev->getParentId(), + 'rev_sha1' => (string)$rev->getSha1(), + 'rev_comment_text' => $rev->getComment(), + 'rev_comment_data' => null, + 'rev_comment_cid' => null, + 'rev_content_format' => $rev->getContentFormat(), + 'rev_content_model' => $rev->getContentModel(), + 'page_namespace' => (string)$page->getTitle()->getNamespace(), + 'page_title' => $page->getTitle()->getDBkey(), + 'page_id' => (string)$page->getId(), + 'page_latest' => (string)$page->getLatest(), + 'page_is_redirect' => $page->isRedirect() ? '1' : '0', + 'page_len' => (string)$page->getContent()->getSize(), + 'user_name' => (string)$rev->getUserText(), + ]; + } + + private function assertRevisionRecordMatchesRevision( + Revision $rev, + RevisionRecord $record + ) { + $this->assertSame( $rev->getId(), $record->getId() ); + $this->assertSame( $rev->getPage(), $record->getPageId() ); + $this->assertSame( $rev->getTimestamp(), $record->getTimestamp() ); + $this->assertSame( $rev->getUserText(), $record->getUser()->getName() ); + $this->assertSame( $rev->getUser(), $record->getUser()->getId() ); + $this->assertSame( $rev->isMinor(), $record->isMinor() ); + $this->assertSame( $rev->getVisibility(), $record->getVisibility() ); + $this->assertSame( $rev->getSize(), $record->getSize() ); + /** + * @note As of MW 1.31, the database schema allows the parent ID to be + * NULL to indicate that it is unknown. + */ + $expectedParent = $rev->getParentId(); + if ( $expectedParent === null ) { + $expectedParent = 0; + } + $this->assertSame( $expectedParent, $record->getParentId() ); + $this->assertSame( $rev->getSha1(), $record->getSha1() ); + $this->assertSame( $rev->getComment(), $record->getComment()->text ); + $this->assertSame( $rev->getContentFormat(), $record->getContent( 'main' )->getDefaultFormat() ); + $this->assertSame( $rev->getContentModel(), $record->getContent( 'main' )->getModel() ); + $this->assertLinkTargetsEqual( $rev->getTitle(), $record->getPageAsLinkTarget() ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow + * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29 + */ + public function testNewRevisionFromRow_anonEdit() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + /** @var Revision $rev */ + $rev = $page->doEditContent( + new WikitextContent( __METHOD__. 'a' ), + __METHOD__. 'a' + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $record = $store->newRevisionFromRow( + $this->revisionToRow( $rev ), + [], + $page->getTitle() + ); + $this->assertRevisionRecordMatchesRevision( $rev, $record ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow + * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29 + */ + public function testNewRevisionFromRow_userEdit() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + /** @var Revision $rev */ + $rev = $page->doEditContent( + new WikitextContent( __METHOD__. 'b' ), + __METHOD__ . 'b', + 0, + false, + $this->getTestUser()->getUser() + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $record = $store->newRevisionFromRow( + $this->revisionToRow( $rev ), + [], + $page->getTitle() + ); + $this->assertRevisionRecordMatchesRevision( $rev, $record ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow + */ + public function testNewRevisionFromArchiveRow() { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $title = Title::newFromText( __METHOD__ ); + $page = WikiPage::factory( $title ); + /** @var Revision $orig */ + $orig = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + $page->doDeleteArticle( __METHOD__ ); + + $db = wfGetDB( DB_MASTER ); + $arQuery = $store->getArchiveQueryInfo(); + $res = $db->select( + $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ], + __METHOD__, [], $arQuery['joins'] + ); + $this->assertTrue( is_object( $res ), 'query failed' ); + + $row = $res->fetchObject(); + $res->free(); + $record = $store->newRevisionFromArchiveRow( $row ); + + $this->assertRevisionRecordMatchesRevision( $orig, $record ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromId + */ + public function testLoadRevisionFromId() { + $title = Title::newFromText( __METHOD__ ); + $page = WikiPage::factory( $title ); + /** @var Revision $rev */ + $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->loadRevisionFromId( wfGetDB( DB_MASTER ), $rev->getId() ); + $this->assertRevisionRecordMatchesRevision( $rev, $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromPageId + */ + public function testLoadRevisionFromPageId() { + $title = Title::newFromText( __METHOD__ ); + $page = WikiPage::factory( $title ); + /** @var Revision $rev */ + $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->loadRevisionFromPageId( wfGetDB( DB_MASTER ), $page->getId() ); + $this->assertRevisionRecordMatchesRevision( $rev, $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromTitle + */ + public function testLoadRevisionFromTitle() { + $title = Title::newFromText( __METHOD__ ); + $page = WikiPage::factory( $title ); + /** @var Revision $rev */ + $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->loadRevisionFromTitle( wfGetDB( DB_MASTER ), $title ); + $this->assertRevisionRecordMatchesRevision( $rev, $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromTimestamp + */ + public function testLoadRevisionFromTimestamp() { + $title = Title::newFromText( __METHOD__ ); + $page = WikiPage::factory( $title ); + /** @var Revision $revOne */ + $revOne = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + // Sleep to ensure different timestamps... )(evil) + sleep( 1 ); + /** @var Revision $revTwo */ + $revTwo = $page->doEditContent( new WikitextContent( __METHOD__ . 'a' ), '' ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $this->assertNull( + $store->loadRevisionFromTimestamp( wfGetDB( DB_MASTER ), $title, '20150101010101' ) + ); + $this->assertSame( + $revOne->getId(), + $store->loadRevisionFromTimestamp( + wfGetDB( DB_MASTER ), + $title, + $revOne->getTimestamp() + )->getId() + ); + $this->assertSame( + $revTwo->getId(), + $store->loadRevisionFromTimestamp( + wfGetDB( DB_MASTER ), + $title, + $revTwo->getTimestamp() + )->getId() + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::listRevisionSizes + */ + public function testGetParentLengths() { + $page = WikiPage::factory( Title::newFromText( __METHOD__ ) ); + /** @var Revision $revOne */ + $revOne = $page->doEditContent( + new WikitextContent( __METHOD__ ), __METHOD__ + )->value['revision']; + /** @var Revision $revTwo */ + $revTwo = $page->doEditContent( + new WikitextContent( __METHOD__ . '2' ), __METHOD__ + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $this->assertSame( + [ + $revOne->getId() => strlen( __METHOD__ ), + ], + $store->listRevisionSizes( + wfGetDB( DB_MASTER ), + [ $revOne->getId() ] + ) + ); + $this->assertSame( + [ + $revOne->getId() => strlen( __METHOD__ ), + $revTwo->getId() => strlen( __METHOD__ ) + 1, + ], + $store->listRevisionSizes( + wfGetDB( DB_MASTER ), + [ $revOne->getId(), $revTwo->getId() ] + ) + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getPreviousRevision + */ + public function testGetPreviousRevision() { + $page = WikiPage::factory( Title::newFromText( __METHOD__ ) ); + /** @var Revision $revOne */ + $revOne = $page->doEditContent( + new WikitextContent( __METHOD__ ), __METHOD__ + )->value['revision']; + /** @var Revision $revTwo */ + $revTwo = $page->doEditContent( + new WikitextContent( __METHOD__ . '2' ), __METHOD__ + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $this->assertNull( + $store->getPreviousRevision( $store->getRevisionById( $revOne->getId() ) ) + ); + $this->assertSame( + $revOne->getId(), + $store->getPreviousRevision( $store->getRevisionById( $revTwo->getId() ) )->getId() + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getNextRevision + */ + public function testGetNextRevision() { + $page = WikiPage::factory( Title::newFromText( __METHOD__ ) ); + /** @var Revision $revOne */ + $revOne = $page->doEditContent( + new WikitextContent( __METHOD__ ), __METHOD__ + )->value['revision']; + /** @var Revision $revTwo */ + $revTwo = $page->doEditContent( + new WikitextContent( __METHOD__ . '2' ), __METHOD__ + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $this->assertSame( + $revTwo->getId(), + $store->getNextRevision( $store->getRevisionById( $revOne->getId() ) )->getId() + ); + $this->assertNull( + $store->getNextRevision( $store->getRevisionById( $revTwo->getId() ) ) + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId + */ + public function testGetTimestampFromId_found() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + /** @var Revision $rev */ + $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->getTimestampFromId( + $page->getTitle(), + $rev->getId() + ); + + $this->assertSame( $rev->getTimestamp(), $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId + */ + public function testGetTimestampFromId_notFound() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + /** @var Revision $rev */ + $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ) + ->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->getTimestampFromId( + $page->getTitle(), + $rev->getId() + 1 + ); + + $this->assertFalse( $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::countRevisionsByPageId + */ + public function testCountRevisionsByPageId() { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $page = WikiPage::factory( Title::newFromText( __METHOD__ ) ); + + $this->assertSame( + 0, + $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() ) + ); + $page->doEditContent( new WikitextContent( 'a' ), 'a' ); + $this->assertSame( + 1, + $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() ) + ); + $page->doEditContent( new WikitextContent( 'b' ), 'b' ); + $this->assertSame( + 2, + $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() ) + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::countRevisionsByTitle + */ + public function testCountRevisionsByTitle() { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $page = WikiPage::factory( Title::newFromText( __METHOD__ ) ); + + $this->assertSame( + 0, + $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() ) + ); + $page->doEditContent( new WikitextContent( 'a' ), 'a' ); + $this->assertSame( + 1, + $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() ) + ); + $page->doEditContent( new WikitextContent( 'b' ), 'b' ); + $this->assertSame( + 2, + $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() ) + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::userWasLastToEdit + */ + public function testUserWasLastToEdit_false() { + $sysop = $this->getTestSysop()->getUser(); + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ ); + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->userWasLastToEdit( + wfGetDB( DB_MASTER ), + $page->getId(), + $sysop->getId(), + '20160101010101' + ); + $this->assertFalse( $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::userWasLastToEdit + */ + public function testUserWasLastToEdit_true() { + $startTime = wfTimestampNow(); + $sysop = $this->getTestSysop()->getUser(); + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + $page->doEditContent( + new WikitextContent( __METHOD__ ), + __METHOD__, + 0, + false, + $sysop + ); + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $result = $store->userWasLastToEdit( + wfGetDB( DB_MASTER ), + $page->getId(), + $sysop->getId(), + $startTime + ); + $this->assertTrue( $result ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getKnownCurrentRevision + */ + public function testGetKnownCurrentRevision() { + $page = WikiPage::factory( Title::newFromText( 'UTPage' ) ); + /** @var Revision $rev */ + $rev = $page->doEditContent( + new WikitextContent( __METHOD__. 'b' ), + __METHOD__ . 'b', + 0, + false, + $this->getTestUser()->getUser() + )->value['revision']; + + $store = MediaWikiServices::getInstance()->getRevisionStore(); + $record = $store->getKnownCurrentRevision( + $page->getTitle(), + $rev->getId() + ); + + $this->assertRevisionRecordMatchesRevision( $rev, $record ); + } + + public function provideNewMutableRevisionFromArray() { + yield 'Basic array, with page & id' => [ + [ + 'id' => 2, + 'page' => 1, + 'text_id' => 2, + 'timestamp' => '20171017114835', + 'user_text' => '111.0.1.2', + 'user' => 0, + 'minor_edit' => false, + 'deleted' => 0, + 'len' => 46, + 'parent_id' => 1, + 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'comment' => 'Goat Comment!', + 'content_format' => 'text/x-wiki', + 'content_model' => 'wikitext', + ] + ]; + yield 'Basic array, content object' => [ + [ + 'id' => 2, + 'page' => 1, + 'timestamp' => '20171017114835', + 'user_text' => '111.0.1.2', + 'user' => 0, + 'minor_edit' => false, + 'deleted' => 0, + 'len' => 46, + 'parent_id' => 1, + 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'comment' => 'Goat Comment!', + 'content' => new WikitextContent( 'Some Content' ), + ] + ]; + yield 'Basic array, with title' => [ + [ + 'title' => Title::newFromText( 'SomeText' ), + 'text_id' => 2, + 'timestamp' => '20171017114835', + 'user_text' => '111.0.1.2', + 'user' => 0, + 'minor_edit' => false, + 'deleted' => 0, + 'len' => 46, + 'parent_id' => 1, + 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'comment' => 'Goat Comment!', + 'content_format' => 'text/x-wiki', + 'content_model' => 'wikitext', + ] + ]; + yield 'Basic array, no user field' => [ + [ + 'id' => 2, + 'page' => 1, + 'text_id' => 2, + 'timestamp' => '20171017114835', + 'user_text' => '111.0.1.3', + 'minor_edit' => false, + 'deleted' => 0, + 'len' => 46, + 'parent_id' => 1, + 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'comment' => 'Goat Comment!', + 'content_format' => 'text/x-wiki', + 'content_model' => 'wikitext', + ] + ]; + } + + /** + * @dataProvider provideNewMutableRevisionFromArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testNewMutableRevisionFromArray( array $array ) { + $store = MediaWikiServices::getInstance()->getRevisionStore(); + + $result = $store->newMutableRevisionFromArray( $array ); + + if ( isset( $array['id'] ) ) { + $this->assertSame( $array['id'], $result->getId() ); + } + if ( isset( $array['page'] ) ) { + $this->assertSame( $array['page'], $result->getPageId() ); + } + $this->assertSame( $array['timestamp'], $result->getTimestamp() ); + $this->assertSame( $array['user_text'], $result->getUser()->getName() ); + if ( isset( $array['user'] ) ) { + $this->assertSame( $array['user'], $result->getUser()->getId() ); + } + $this->assertSame( (bool)$array['minor_edit'], $result->isMinor() ); + $this->assertSame( $array['deleted'], $result->getVisibility() ); + $this->assertSame( $array['len'], $result->getSize() ); + $this->assertSame( $array['parent_id'], $result->getParentId() ); + $this->assertSame( $array['sha1'], $result->getSha1() ); + $this->assertSame( $array['comment'], $result->getComment()->text ); + if ( isset( $array['content'] ) ) { + $this->assertTrue( + $result->getSlot( 'main' )->getContent()->equals( $array['content'] ) + ); + } else { + $this->assertSame( + $array['content_format'], + $result->getSlot( 'main' )->getContent()->getDefaultFormat() + ); + $this->assertSame( $array['content_model'], $result->getSlot( 'main' )->getModel() ); + } + } + +} diff --git a/tests/phpunit/includes/Storage/RevisionStoreRecordTest.php b/tests/phpunit/includes/Storage/RevisionStoreRecordTest.php new file mode 100644 index 0000000000..aa59a5b50b --- /dev/null +++ b/tests/phpunit/includes/Storage/RevisionStoreRecordTest.php @@ -0,0 +1,814 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use CommentStoreComment; +use InvalidArgumentException; +use LogicException; +use MediaWiki\Storage\RevisionRecord; +use MediaWiki\Storage\RevisionSlots; +use MediaWiki\Storage\RevisionStoreRecord; +use MediaWiki\Storage\SlotRecord; +use MediaWiki\Storage\SuppressedDataException; +use MediaWiki\User\UserIdentity; +use MediaWiki\User\UserIdentityValue; +use MediaWikiTestCase; +use TextContent; +use Title; + +/** + * @covers \MediaWiki\Storage\RevisionStoreRecord + */ +class RevisionStoreRecordTest extends MediaWikiTestCase { + + /** + * @param array $rowOverrides + * + * @return RevisionStoreRecord + */ + public function newRevision( array $rowOverrides = [] ) { + $title = Title::newFromText( 'Dummy' ); + $title->resetArticleID( 17 ); + + $user = new UserIdentityValue( 11, 'Tester' ); + $comment = CommentStoreComment::newUnsavedComment( 'Hello World' ); + + $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) ); + $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) ); + $slots = new RevisionSlots( [ $main, $aux ] ); + + $row = [ + 'rev_id' => '7', + 'rev_page' => strval( $title->getArticleID() ), + 'rev_timestamp' => '20200101000000', + 'rev_deleted' => 0, + 'rev_minor_edit' => 0, + 'rev_parent_id' => '5', + 'rev_len' => $slots->computeSize(), + 'rev_sha1' => $slots->computeSha1(), + 'page_latest' => '18', + ]; + + $row = array_merge( $row, $rowOverrides ); + + return new RevisionStoreRecord( $title, $user, $comment, (object)$row, $slots ); + } + + public function provideConstructor() { + $title = Title::newFromText( 'Dummy' ); + $title->resetArticleID( 17 ); + + $user = new UserIdentityValue( 11, 'Tester' ); + $comment = CommentStoreComment::newUnsavedComment( 'Hello World' ); + + $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) ); + $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) ); + $slots = new RevisionSlots( [ $main, $aux ] ); + + $protoRow = [ + 'rev_id' => '7', + 'rev_page' => strval( $title->getArticleID() ), + 'rev_timestamp' => '20200101000000', + 'rev_deleted' => 0, + 'rev_minor_edit' => 0, + 'rev_parent_id' => '5', + 'rev_len' => $slots->computeSize(), + 'rev_sha1' => $slots->computeSha1(), + 'page_latest' => '18', + ]; + + $row = $protoRow; + yield 'all info' => [ + $title, + $user, + $comment, + (object)$row, + $slots, + 'acmewiki' + ]; + + $row = $protoRow; + $row['rev_minor_edit'] = '1'; + $row['rev_deleted'] = strval( RevisionRecord::DELETED_USER ); + + yield 'minor deleted' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + $row['page_latest'] = $row['rev_id']; + + yield 'latest' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + unset( $row['rev_parent'] ); + + yield 'no parent' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + unset( $row['rev_len'] ); + unset( $row['rev_sha1'] ); + + yield 'no length, no hash' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + yield 'no length, no hash' => [ + Title::newFromText( 'DummyDoesNotExist' ), + $user, + $comment, + (object)$row, + $slots + ]; + } + + /** + * @dataProvider provideConstructor + * + * @param Title $title + * @param UserIdentity $user + * @param CommentStoreComment $comment + * @param object $row + * @param RevisionSlots $slots + * @param bool $wikiId + */ + public function testConstructorAndGetters( + Title $title, + UserIdentity $user, + CommentStoreComment $comment, + $row, + RevisionSlots $slots, + $wikiId = false + ) { + $rec = new RevisionStoreRecord( $title, $user, $comment, $row, $slots, $wikiId ); + + $this->assertSame( $title, $rec->getPageAsLinkTarget(), 'getPageAsLinkTarget' ); + $this->assertSame( $user, $rec->getUser( RevisionRecord::RAW ), 'getUser' ); + $this->assertSame( $comment, $rec->getComment(), 'getComment' ); + + $this->assertSame( $slots->getSlotRoles(), $rec->getSlotRoles(), 'getSlotRoles' ); + $this->assertSame( $wikiId, $rec->getWikiId(), 'getWikiId' ); + + $this->assertSame( (int)$row->rev_id, $rec->getId(), 'getId' ); + $this->assertSame( (int)$row->rev_page, $rec->getPageId(), 'getId' ); + $this->assertSame( $row->rev_timestamp, $rec->getTimestamp(), 'getTimestamp' ); + $this->assertSame( (int)$row->rev_deleted, $rec->getVisibility(), 'getVisibility' ); + $this->assertSame( (bool)$row->rev_minor_edit, $rec->isMinor(), 'getIsMinor' ); + + if ( isset( $row->rev_parent_id ) ) { + $this->assertSame( (int)$row->rev_parent_id, $rec->getParentId(), 'getParentId' ); + } else { + $this->assertSame( 0, $rec->getParentId(), 'getParentId' ); + } + + if ( isset( $row->rev_len ) ) { + $this->assertSame( (int)$row->rev_len, $rec->getSize(), 'getSize' ); + } else { + $this->assertSame( $slots->computeSize(), $rec->getSize(), 'getSize' ); + } + + if ( isset( $row->rev_sha1 ) ) { + $this->assertSame( $row->rev_sha1, $rec->getSha1(), 'getSha1' ); + } else { + $this->assertSame( $slots->computeSha1(), $rec->getSha1(), 'getSha1' ); + } + + if ( isset( $row->page_latest ) ) { + $this->assertSame( + (int)$row->rev_id === (int)$row->page_latest, + $rec->isCurrent(), + 'isCurrent' + ); + } else { + $this->assertSame( + false, + $rec->isCurrent(), + 'isCurrent' + ); + } + } + + public function provideConstructorFailure() { + $title = Title::newFromText( 'Dummy' ); + $title->resetArticleID( 17 ); + + $user = new UserIdentityValue( 11, 'Tester' ); + + $comment = CommentStoreComment::newUnsavedComment( 'Hello World' ); + + $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) ); + $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) ); + $slots = new RevisionSlots( [ $main, $aux ] ); + + $protoRow = [ + 'rev_id' => '7', + 'rev_page' => strval( $title->getArticleID() ), + 'rev_timestamp' => '20200101000000', + 'rev_deleted' => 0, + 'rev_minor_edit' => 0, + 'rev_parent_id' => '5', + 'rev_len' => $slots->computeSize(), + 'rev_sha1' => $slots->computeSha1(), + 'page_latest' => '18', + ]; + + yield 'not a row' => [ + $title, + $user, + $comment, + 'not a row', + $slots, + 'acmewiki' + ]; + + $row = $protoRow; + $row['rev_timestamp'] = 'kittens'; + + yield 'bad timestamp' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + $row['rev_page'] = 99; + + yield 'page ID mismatch' => [ + $title, + $user, + $comment, + (object)$row, + $slots + ]; + + $row = $protoRow; + + yield 'bad wiki' => [ + $title, + $user, + $comment, + (object)$row, + $slots, + 12345 + ]; + } + + /** + * @dataProvider provideConstructorFailure + * + * @param Title $title + * @param UserIdentity $user + * @param CommentStoreComment $comment + * @param object $row + * @param RevisionSlots $slots + * @param bool $wikiId + */ + public function testConstructorFailure( + Title $title, + UserIdentity $user, + CommentStoreComment $comment, + $row, + RevisionSlots $slots, + $wikiId = false + ) { + $this->setExpectedException( InvalidArgumentException::class ); + new RevisionStoreRecord( $title, $user, $comment, $row, $slots, $wikiId ); + } + + private function provideAudienceCheckData( $field ) { + yield 'field accessible for oversighter (ALL)' => [ + RevisionRecord::SUPPRESSED_ALL, + [ 'oversight' ], + true, + false + ]; + + yield 'field accessible for oversighter' => [ + RevisionRecord::DELETED_RESTRICTED | $field, + [ 'oversight' ], + true, + false + ]; + + yield 'field not accessible for sysops (ALL)' => [ + RevisionRecord::SUPPRESSED_ALL, + [ 'sysop' ], + false, + false + ]; + + yield 'field not accessible for sysops' => [ + RevisionRecord::DELETED_RESTRICTED | $field, + [ 'sysop' ], + false, + false + ]; + + yield 'field accessible for sysops' => [ + $field, + [ 'sysop' ], + true, + false + ]; + + yield 'field suppressed for logged in users' => [ + $field, + [ 'user' ], + false, + false + ]; + + yield 'unrelated field suppressed' => [ + $field === RevisionRecord::DELETED_COMMENT + ? RevisionRecord::DELETED_USER + : RevisionRecord::DELETED_COMMENT, + [ 'user' ], + true, + true + ]; + + yield 'nothing suppressed' => [ + 0, + [ 'user' ], + true, + true + ]; + } + + public function testSerialization_fails() { + $this->setExpectedException( LogicException::class ); + $rev = $this->newRevision(); + serialize( $rev ); + } + + public function provideGetComment_audience() { + return $this->provideAudienceCheckData( RevisionRecord::DELETED_COMMENT ); + } + + private function forceStandardPermissions() { + $this->setMwGlobals( + 'wgGroupPermissions', + [ + 'user' => [ + 'viewsuppressed' => false, + 'suppressrevision' => false, + 'deletedtext' => false, + 'deletedhistory' => false, + ], + 'sysop' => [ + 'viewsuppressed' => false, + 'suppressrevision' => false, + 'deletedtext' => true, + 'deletedhistory' => true, + ], + 'oversight' => [ + 'deletedtext' => true, + 'deletedhistory' => true, + 'viewsuppressed' => true, + 'suppressrevision' => true, + ], + ] + ); + } + + /** + * @dataProvider provideGetComment_audience + */ + public function testGetComment_audience( $visibility, $groups, $userCan, $publicCan ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $groups )->getUser(); + $rev = $this->newRevision( [ 'rev_deleted' => $visibility ] ); + + $this->assertNotNull( $rev->getComment( RevisionRecord::RAW ), 'raw can' ); + + $this->assertSame( + $publicCan, + $rev->getComment( RevisionRecord::FOR_PUBLIC ) !== null, + 'public can' + ); + $this->assertSame( + $userCan, + $rev->getComment( RevisionRecord::FOR_THIS_USER, $user ) !== null, + 'user can' + ); + } + + public function provideGetUser_audience() { + return $this->provideAudienceCheckData( RevisionRecord::DELETED_USER ); + } + + /** + * @dataProvider provideGetUser_audience + */ + public function testGetUser_audience( $visibility, $groups, $userCan, $publicCan ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $groups )->getUser(); + $rev = $this->newRevision( [ 'rev_deleted' => $visibility ] ); + + $this->assertNotNull( $rev->getUser( RevisionRecord::RAW ), 'raw can' ); + + $this->assertSame( + $publicCan, + $rev->getUser( RevisionRecord::FOR_PUBLIC ) !== null, + 'public can' + ); + $this->assertSame( + $userCan, + $rev->getUser( RevisionRecord::FOR_THIS_USER, $user ) !== null, + 'user can' + ); + } + + public function provideGetSlot_audience() { + return $this->provideAudienceCheckData( RevisionRecord::DELETED_TEXT ); + } + + /** + * @dataProvider provideGetSlot_audience + */ + public function testGetSlot_audience( $visibility, $groups, $userCan, $publicCan ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $groups )->getUser(); + $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->assertNotNull( + $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user ), + 'user can' + ); + + try { + $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC )->getContent(); + $exception = null; + } catch ( SuppressedDataException $ex ) { + $exception = $ex; + } + + $this->assertSame( + $publicCan, + $exception === null, + 'public can' + ); + + try { + $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user )->getContent(); + $exception = null; + } catch ( SuppressedDataException $ex ) { + $exception = $ex; + } + + $this->assertSame( + $userCan, + $exception === null, + 'user can' + ); + } + + public function provideGetSlot_audience_latest() { + return $this->provideAudienceCheckData( RevisionRecord::DELETED_TEXT ); + } + + /** + * @dataProvider provideGetSlot_audience_latest + */ + public function testGetSlot_audience_latest( $visibility, $groups, $userCan, $publicCan ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $groups )->getUser(); + $rev = $this->newRevision( + [ + 'rev_deleted' => $visibility, + 'rev_id' => 11, + 'page_latest' => 11, // revision is current + ] + ); + + // sanity check + $this->assertTrue( $rev->isCurrent(), 'isCurrent()' ); + + // 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->assertNotNull( + $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user ), + 'user can' + ); + + // NOTE: the content of the current revision is never suppressed! + // Check that getContent() doesn't throw SuppressedDataException + $rev->getSlot( 'main', RevisionRecord::RAW )->getContent(); + $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC )->getContent(); + $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user )->getContent(); + } + + /** + * @dataProvider provideGetSlot_audience + */ + public function testGetContent_audience( $visibility, $groups, $userCan, $publicCan ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $groups )->getUser(); + $rev = $this->newRevision( [ 'rev_deleted' => $visibility ] ); + + $this->assertNotNull( $rev->getContent( 'main', RevisionRecord::RAW ), 'raw can' ); + + $this->assertSame( + $publicCan, + $rev->getContent( 'main', RevisionRecord::FOR_PUBLIC ) !== null, + 'public can' + ); + $this->assertSame( + $userCan, + $rev->getContent( 'main', RevisionRecord::FOR_THIS_USER, $user ) !== null, + 'user can' + ); + } + + public function testGetSlot() { + $rev = $this->newRevision(); + + $slot = $rev->getSlot( 'main' ); + $this->assertNotNull( $slot, 'getSlot()' ); + $this->assertSame( 'main', $slot->getRole(), 'getRole()' ); + } + + public function testGetContent() { + $rev = $this->newRevision(); + + $content = $rev->getSlot( 'main' ); + $this->assertNotNull( $content, 'getContent()' ); + $this->assertSame( CONTENT_MODEL_TEXT, $content->getModel(), 'getModel()' ); + } + + public function provideUserCanBitfield() { + yield [ 0, 0, [], null, true ]; + // Bitfields match, user has no permissions + yield [ + RevisionRecord::DELETED_TEXT, + RevisionRecord::DELETED_TEXT, + [], + null, + false + ]; + yield [ + RevisionRecord::DELETED_COMMENT, + RevisionRecord::DELETED_COMMENT, + [], + null, + false, + ]; + yield [ + RevisionRecord::DELETED_USER, + RevisionRecord::DELETED_USER, + [], + null, + false + ]; + yield [ + RevisionRecord::DELETED_RESTRICTED, + RevisionRecord::DELETED_RESTRICTED, + [], + null, + false, + ]; + // Bitfields match, user (admin) does have permissions + yield [ + RevisionRecord::DELETED_TEXT, + RevisionRecord::DELETED_TEXT, + [ 'sysop' ], + null, + true, + ]; + yield [ + RevisionRecord::DELETED_COMMENT, + RevisionRecord::DELETED_COMMENT, + [ 'sysop' ], + null, + true, + ]; + yield [ + RevisionRecord::DELETED_USER, + RevisionRecord::DELETED_USER, + [ 'sysop' ], + null, + true, + ]; + // Bitfields match, user (admin) does not have permissions + yield [ + RevisionRecord::DELETED_RESTRICTED, + RevisionRecord::DELETED_RESTRICTED, + [ 'sysop' ], + null, + false, + ]; + // Bitfields match, user (oversight) does have permissions + yield [ + RevisionRecord::DELETED_RESTRICTED, + RevisionRecord::DELETED_RESTRICTED, + [ 'oversight' ], + null, + true, + ]; + // Check permissions using the title + yield [ + RevisionRecord::DELETED_TEXT, + RevisionRecord::DELETED_TEXT, + [ 'sysop' ], + Title::newFromText( __METHOD__ ), + true, + ]; + yield [ + RevisionRecord::DELETED_TEXT, + RevisionRecord::DELETED_TEXT, + [], + Title::newFromText( __METHOD__ ), + false, + ]; + } + + /** + * @dataProvider provideUserCanBitfield + * @covers \MediaWiki\Storage\RevisionRecord::userCanBitfield + */ + public function testUserCanBitfield( $bitField, $field, $userGroups, $title, $expected ) { + $this->forceStandardPermissions(); + + $user = $this->getTestUser( $userGroups )->getUser(); + + $this->assertSame( + $expected, + RevisionRecord::userCanBitfield( $bitField, $field, $user, $title ) + ); + } + + private function getSlotRecord( $role, $contentString ) { + return SlotRecord::newUnsaved( $role, new TextContent( $contentString ) ); + } + + public function provideHasSameContent() { + /** + * @param SlotRecord[] $slots + * @param int $revId + * @return RevisionStoreRecord + */ + $recordCreator = function ( array $slots, $revId ) { + $title = Title::newFromText( 'provideHasSameContent' ); + $title->resetArticleID( 19 ); + $slots = new RevisionSlots( $slots ); + + return new RevisionStoreRecord( + $title, + new UserIdentityValue( 11, __METHOD__ ), + CommentStoreComment::newUnsavedComment( __METHOD__ ), + (object)[ + 'rev_id' => strval( $revId ), + 'rev_page' => strval( $title->getArticleID() ), + 'rev_timestamp' => '20200101000000', + 'rev_deleted' => 0, + 'rev_minor_edit' => 0, + 'rev_parent_id' => '5', + 'rev_len' => $slots->computeSize(), + 'rev_sha1' => $slots->computeSha1(), + 'page_latest' => '18', + ], + $slots + ); + }; + + // Create some slots with content + $mainA = SlotRecord::newUnsaved( 'main', new TextContent( 'A' ) ); + $mainB = SlotRecord::newUnsaved( 'main', new TextContent( 'B' ) ); + $auxA = SlotRecord::newUnsaved( 'aux', new TextContent( 'A' ) ); + $auxB = SlotRecord::newUnsaved( 'aux', new TextContent( 'A' ) ); + + $initialRecord = $recordCreator( [ $mainA ], 12 ); + + return [ + 'same record object' => [ + true, + $initialRecord, + $initialRecord, + ], + 'same record content, different object' => [ + true, + $recordCreator( [ $mainA ], 12 ), + $recordCreator( [ $mainA ], 13 ), + ], + 'same record content, aux slot, different object' => [ + true, + $recordCreator( [ $auxA ], 12 ), + $recordCreator( [ $auxB ], 13 ), + ], + 'different content' => [ + false, + $recordCreator( [ $mainA ], 12 ), + $recordCreator( [ $mainB ], 13 ), + ], + 'different content and number of slots' => [ + false, + $recordCreator( [ $mainA ], 12 ), + $recordCreator( [ $mainA, $mainB ], 13 ), + ], + ]; + } + + /** + * @dataProvider provideHasSameContent + * @covers \MediaWiki\Storage\RevisionRecord::hasSameContent + * @group Database + */ + public function testHasSameContent( + $expected, + RevisionRecord $record1, + RevisionRecord $record2 + ) { + $this->assertSame( + $expected, + $record1->hasSameContent( $record2 ) + ); + } + + public function provideIsDeleted() { + yield 'no deletion' => [ + 0, + [ + RevisionRecord::DELETED_TEXT => false, + RevisionRecord::DELETED_COMMENT => false, + RevisionRecord::DELETED_USER => false, + RevisionRecord::DELETED_RESTRICTED => false, + ] + ]; + yield 'text deleted' => [ + RevisionRecord::DELETED_TEXT, + [ + RevisionRecord::DELETED_TEXT => true, + RevisionRecord::DELETED_COMMENT => false, + RevisionRecord::DELETED_USER => false, + RevisionRecord::DELETED_RESTRICTED => false, + ] + ]; + yield 'text and comment deleted' => [ + RevisionRecord::DELETED_TEXT + RevisionRecord::DELETED_COMMENT, + [ + RevisionRecord::DELETED_TEXT => true, + RevisionRecord::DELETED_COMMENT => true, + RevisionRecord::DELETED_USER => false, + RevisionRecord::DELETED_RESTRICTED => false, + ] + ]; + yield 'all 4 deleted' => [ + RevisionRecord::DELETED_TEXT + + RevisionRecord::DELETED_COMMENT + + RevisionRecord::DELETED_RESTRICTED + + RevisionRecord::DELETED_USER, + [ + RevisionRecord::DELETED_TEXT => true, + RevisionRecord::DELETED_COMMENT => true, + RevisionRecord::DELETED_USER => true, + RevisionRecord::DELETED_RESTRICTED => true, + ] + ]; + } + + /** + * @dataProvider provideIsDeleted + * @covers \MediaWiki\Storage\RevisionRecord::isDeleted + */ + public function testIsDeleted( $revDeleted, $assertionMap ) { + $rev = $this->newRevision( [ 'rev_deleted' => $revDeleted ] ); + foreach ( $assertionMap as $deletionLevel => $expected ) { + $this->assertSame( $expected, $rev->isDeleted( $deletionLevel ) ); + } + } + +} diff --git a/tests/phpunit/includes/Storage/RevisionStoreTest.php b/tests/phpunit/includes/Storage/RevisionStoreTest.php new file mode 100644 index 0000000000..18dbc25046 --- /dev/null +++ b/tests/phpunit/includes/Storage/RevisionStoreTest.php @@ -0,0 +1,294 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use MediaWiki\Storage\RevisionStore; +use MediaWiki\Storage\SqlBlobStore; +use MediaWikiTestCase; +use WANObjectCache; +use Wikimedia\Rdbms\LoadBalancer; + +class RevisionStoreTest extends MediaWikiTestCase { + + /** + * @param LoadBalancer $loadBalancer + * @param SqlBlobStore $blobStore + * @param WANObjectCache $WANObjectCache + * + * @return RevisionStore + */ + private function getRevisionStore( + $loadBalancer = null, + $blobStore = null, + $WANObjectCache = null + ) { + return new RevisionStore( + $loadBalancer ? $loadBalancer : $this->getMockLoadBalancer(), + $blobStore ? $blobStore : $this->getMockSqlBlobStore(), + $WANObjectCache ? $WANObjectCache : $this->getHashWANObjectCache() + ); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|LoadBalancer + */ + private function getMockLoadBalancer() { + return $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor()->getMock(); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|SqlBlobStore + */ + private function getMockSqlBlobStore() { + return $this->getMockBuilder( SqlBlobStore::class ) + ->disableOriginalConstructor()->getMock(); + } + + private function getHashWANObjectCache() { + return new WANObjectCache( [ 'cache' => new \HashBagOStuff() ] ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getContentHandlerUseDB + * @covers \MediaWiki\Storage\RevisionStore::setContentHandlerUseDB + */ + public function testGetSetContentHandlerDb() { + $store = $this->getRevisionStore(); + $this->assertTrue( $store->getContentHandlerUseDB() ); + $store->setContentHandlerUseDB( false ); + $this->assertFalse( $store->getContentHandlerUseDB() ); + $store->setContentHandlerUseDB( true ); + $this->assertTrue( $store->getContentHandlerUseDB() ); + } + + private function getDefaultQueryFields() { + return [ + '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', + ]; + } + + private function getCommentQueryFields() { + return [ + 'rev_comment_text' => 'rev_comment', + 'rev_comment_data' => 'NULL', + 'rev_comment_cid' => 'NULL', + ]; + } + + private function getContentHandlerQueryFields() { + return [ + 'rev_content_format', + 'rev_content_model', + ]; + } + + public function provideGetQueryInfo() { + yield [ + true, + [], + [ + 'tables' => [ 'revision' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields(), + $this->getContentHandlerQueryFields() + ), + 'joins' => [], + ] + ]; + yield [ + false, + [], + [ + 'tables' => [ 'revision' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields() + ), + 'joins' => [], + ] + ]; + yield [ + false, + [ 'page' ], + [ + 'tables' => [ 'revision', 'page' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields(), + [ + 'page_namespace', + 'page_title', + 'page_id', + 'page_latest', + 'page_is_redirect', + 'page_len', + ] + ), + 'joins' => [ + 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ], + ], + ] + ]; + yield [ + false, + [ 'user' ], + [ + 'tables' => [ 'revision', 'user' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields(), + [ + 'user_name', + ] + ), + 'joins' => [ + 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ], + ], + ] + ]; + yield [ + false, + [ 'text' ], + [ + 'tables' => [ 'revision', 'text' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields(), + [ + 'old_text', + 'old_flags', + ] + ), + 'joins' => [ + 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ], + ], + ] + ]; + yield [ + true, + [ 'page', 'user', 'text' ], + [ + 'tables' => [ 'revision', 'page', 'user', 'text' ], + 'fields' => array_merge( + $this->getDefaultQueryFields(), + $this->getCommentQueryFields(), + $this->getContentHandlerQueryFields(), + [ + 'page_namespace', + 'page_title', + 'page_id', + 'page_latest', + 'page_is_redirect', + 'page_len', + 'user_name', + 'old_text', + 'old_flags', + ] + ), + 'joins' => [ + 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ], + 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ], + 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ], + ], + ] + ]; + } + + /** + * @dataProvider provideGetQueryInfo + * @covers \MediaWiki\Storage\RevisionStore::getQueryInfo + */ + public function testGetQueryInfo( $contentHandlerUseDb, $options, $expected ) { + $store = $this->getRevisionStore(); + $store->setContentHandlerUseDB( $contentHandlerUseDb ); + $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->assertEquals( $expected, $store->getQueryInfo( $options ) ); + } + + private function getDefaultArchiveFields() { + return [ + 'ar_id', + 'ar_page_id', + 'ar_namespace', + 'ar_title', + 'ar_rev_id', + 'ar_text', + 'ar_text_id', + 'ar_timestamp', + 'ar_user_text', + 'ar_user', + 'ar_minor_edit', + 'ar_deleted', + 'ar_len', + 'ar_parent_id', + 'ar_sha1', + ]; + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo + */ + public function testGetArchiveQueryInfo_contentHandlerDb() { + $store = $this->getRevisionStore(); + $store->setContentHandlerUseDB( true ); + $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->assertEquals( + [ + 'tables' => [ + 'archive' + ], + 'fields' => array_merge( + $this->getDefaultArchiveFields(), + [ + 'ar_comment_text' => 'ar_comment', + 'ar_comment_data' => 'NULL', + 'ar_comment_cid' => 'NULL', + 'ar_content_format', + 'ar_content_model', + ] + ), + 'joins' => [], + ], + $store->getArchiveQueryInfo() + ); + } + + /** + * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo + */ + public function testGetArchiveQueryInfo_noContentHandlerDb() { + $store = $this->getRevisionStore(); + $store->setContentHandlerUseDB( false ); + $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->assertEquals( + [ + 'tables' => [ + 'archive' + ], + 'fields' => array_merge( + $this->getDefaultArchiveFields(), + [ + 'ar_comment_text' => 'ar_comment', + 'ar_comment_data' => 'NULL', + 'ar_comment_cid' => 'NULL', + ] + ), + 'joins' => [], + ], + $store->getArchiveQueryInfo() + ); + } + +} diff --git a/tests/phpunit/includes/Storage/SlotRecordTest.php b/tests/phpunit/includes/Storage/SlotRecordTest.php new file mode 100644 index 0000000000..27fcd0cff4 --- /dev/null +++ b/tests/phpunit/includes/Storage/SlotRecordTest.php @@ -0,0 +1,90 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use MediaWiki\Storage\SlotRecord; +use MediaWikiTestCase; +use RuntimeException; +use Wikimedia\Assert\ParameterTypeException; +use WikitextContent; + +/** + * @covers \MediaWiki\Storage\SlotRecord + */ +class SlotRecordTest extends MediaWikiTestCase { + + public function provideAContent() { + yield [ new WikitextContent( 'A' ) ]; + yield [ + function ( SlotRecord $slotRecord ) { + if ( $slotRecord->getAddress() === 'tt:456' ) { + return new WikitextContent( 'A' ); + } + throw new RuntimeException( 'Got Wrong SlotRecord for callback' ); + }, + ]; + } + + /** + * @dataProvider provideAContent + */ + public function testValidConstruction( $content ) { + $row = (object)[ + 'cont_size' => '1', + 'cont_sha1' => 'someHash', + 'cont_address' => 'tt:456', + 'model_name' => 'aModelname', + 'slot_revision' => '2', + 'format_name' => 'someFormatName', + 'role_name' => 'myRole', + 'slot_inherited' => '99' + ]; + + $record = new SlotRecord( $row, $content ); + + $this->assertSame( 'A', $record->getContent()->getNativeData() ); + $this->assertSame( 1, $record->getSize() ); + $this->assertSame( 'someHash', $record->getSha1() ); + $this->assertSame( 'aModelname', $record->getModel() ); + $this->assertSame( 2, $record->getRevision() ); + $this->assertSame( 'tt:456', $record->getAddress() ); + $this->assertSame( 'someFormatName', $record->getFormat() ); + $this->assertSame( 'myRole', $record->getRole() ); + $this->assertTrue( $record->hasAddress() ); + $this->assertTrue( $record->hasRevision() ); + $this->assertTrue( $record->isInherited() ); + } + + public function provideInvalidConstruction() { + yield 'both null' => [ null, null ]; + yield 'null row' => [ null, new WikitextContent( 'A' ) ]; + yield 'array row' => [ null, new WikitextContent( 'A' ) ]; + yield 'null content' => [ (object)[], null ]; + } + + /** + * @dataProvider provideInvalidConstruction + */ + public function testInvalidConstruction( $row, $content ) { + $this->setExpectedException( ParameterTypeException::class ); + new SlotRecord( $row, $content ); + } + + public function testHasAddress_false() { + $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) ); + $this->assertFalse( $record->hasAddress() ); + } + + public function testHasRevision_false() { + $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) ); + $this->assertFalse( $record->hasRevision() ); + } + + public function testInInherited_false() { + // TODO unskip me once fixed. + $this->markTestSkipped( 'Should probably return false, needs fixing?' ); + $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) ); + $this->assertFalse( $record->isInherited() ); + } + +} diff --git a/tests/phpunit/includes/Storage/SqlBlobStoreTest.php b/tests/phpunit/includes/Storage/SqlBlobStoreTest.php new file mode 100644 index 0000000000..6d2b09b7e0 --- /dev/null +++ b/tests/phpunit/includes/Storage/SqlBlobStoreTest.php @@ -0,0 +1,206 @@ +<?php + +namespace MediaWiki\Tests\Storage; + +use Language; +use MediaWiki\MediaWikiServices; +use MediaWiki\Storage\SqlBlobStore; +use MediaWikiTestCase; +use stdClass; +use TitleValue; + +/** + * @covers \MediaWiki\Storage\SqlBlobStore + * @group Database + */ +class SqlBlobStoreTest extends MediaWikiTestCase { + + /** + * @return SqlBlobStore + */ + public function getBlobStore( $legacyEncoding = false, $compressRevisions = false ) { + $services = MediaWikiServices::getInstance(); + + $store = new SqlBlobStore( + $services->getDBLoadBalancer(), + $services->getMainWANObjectCache() + ); + + if ( $compressRevisions ) { + $store->setCompressBlobs( $compressRevisions ); + } + if ( $legacyEncoding ) { + $store->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) ); + } + + return $store; + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::getCompressBlobs() + * @covers \MediaWiki\Storage\SqlBlobStore::setCompressBlobs() + */ + public function testGetSetCompressRevisions() { + $store = $this->getBlobStore(); + $this->assertFalse( $store->getCompressBlobs() ); + $store->setCompressBlobs( true ); + $this->assertTrue( $store->getCompressBlobs() ); + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::getLegacyEncoding() + * @covers \MediaWiki\Storage\SqlBlobStore::getLegacyEncodingConversionLang() + * @covers \MediaWiki\Storage\SqlBlobStore::setLegacyEncoding() + */ + public function testGetSetLegacyEncoding() { + $store = $this->getBlobStore(); + $this->assertFalse( $store->getLegacyEncoding() ); + $this->assertNull( $store->getLegacyEncodingConversionLang() ); + $en = Language::factory( 'en' ); + $store->setLegacyEncoding( 'foo', $en ); + $this->assertSame( 'foo', $store->getLegacyEncoding() ); + $this->assertSame( $en, $store->getLegacyEncodingConversionLang() ); + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::getCacheExpiry() + * @covers \MediaWiki\Storage\SqlBlobStore::setCacheExpiry() + */ + public function testGetSetCacheExpiry() { + $store = $this->getBlobStore(); + $this->assertSame( 604800, $store->getCacheExpiry() ); + $store->setCacheExpiry( 12 ); + $this->assertSame( 12, $store->getCacheExpiry() ); + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::getUseExternalStore() + * @covers \MediaWiki\Storage\SqlBlobStore::setUseExternalStore() + */ + public function testGetSetUseExternalStore() { + $store = $this->getBlobStore(); + $this->assertFalse( $store->getUseExternalStore() ); + $store->setUseExternalStore( true ); + $this->assertTrue( $store->getUseExternalStore() ); + } + + public function provideDecompress() { + yield '(no legacy encoding), false in false out' => [ false, false, [], false ]; + yield '(no legacy encoding), empty in empty out' => [ false, '', [], '' ]; + yield '(no legacy encoding), empty in empty out' => [ false, 'A', [], 'A' ]; + yield '(no legacy encoding), string in with gzip flag returns string' => [ + // gzip string below generated with gzdeflate( 'AAAABBAAA' ) + false, "sttttr\002\022\000", [ 'gzip' ], 'AAAABBAAA', + ]; + yield '(no legacy encoding), string in with object flag returns false' => [ + // gzip string below generated with serialize( 'JOJO' ) + false, "s:4:\"JOJO\";", [ 'object' ], false, + ]; + yield '(no legacy encoding), serialized object in with object flag returns string' => [ + false, + // Using a TitleValue object as it has a getText method (which is needed) + serialize( new TitleValue( 0, 'HHJJDDFF' ) ), + [ 'object' ], + 'HHJJDDFF', + ]; + yield '(no legacy encoding), serialized object in with object & gzip flag returns string' => [ + false, + // Using a TitleValue object as it has a getText method (which is needed) + gzdeflate( serialize( new TitleValue( 0, '8219JJJ840' ) ) ), + [ 'object', 'gzip' ], + '8219JJJ840', + ]; + yield '(ISO-8859-1 encoding), string in string out' => [ + 'ISO-8859-1', + iconv( 'utf-8', 'ISO-8859-1', "1®Àþ1" ), + [], + '1®Àþ1', + ]; + yield '(ISO-8859-1 encoding), serialized object in with gzip flags returns string' => [ + 'ISO-8859-1', + gzdeflate( iconv( 'utf-8', 'ISO-8859-1', "4®Àþ4" ) ), + [ 'gzip' ], + '4®Àþ4', + ]; + yield '(ISO-8859-1 encoding), serialized object in with object flags returns string' => [ + 'ISO-8859-1', + serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "3®Àþ3" ) ) ), + [ 'object' ], + '3®Àþ3', + ]; + yield '(ISO-8859-1 encoding), serialized object in with object & gzip flags returns string' => [ + 'ISO-8859-1', + gzdeflate( serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "2®Àþ2" ) ) ) ), + [ 'gzip', 'object' ], + '2®Àþ2', + ]; + } + + /** + * @dataProvider provideDecompress + * @covers \MediaWiki\Storage\SqlBlobStore::decompressData + * + * @param string|bool $legacyEncoding + * @param mixed $data + * @param array $flags + * @param mixed $expected + */ + public function testDecompressData( $legacyEncoding, $data, $flags, $expected ) { + $store = $this->getBlobStore( $legacyEncoding ); + $this->assertSame( + $expected, + $store->decompressData( $data, $flags ) + ); + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::compressData + */ + public function testCompressRevisionTextUtf8() { + $store = $this->getBlobStore(); + $row = new stdClass; + $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; + $row->old_flags = $store->compressData( $row->old_text ); + $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), + "Flags should contain 'utf-8'" ); + $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ), + "Flags should not contain 'gzip'" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + $row->old_text, "Direct check" ); + } + + /** + * @covers \MediaWiki\Storage\SqlBlobStore::compressData + */ + public function testCompressRevisionTextUtf8Gzip() { + $store = $this->getBlobStore( false, true ); + $this->checkPHPExtension( 'zlib' ); + + $row = new stdClass; + $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; + $row->old_flags = $store->compressData( $row->old_text ); + $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), + "Flags should contain 'utf-8'" ); + $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ), + "Flags should contain 'gzip'" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + gzinflate( $row->old_text ), "Direct check" ); + } + + public function provideBlobs() { + yield [ '' ]; + yield [ 'someText' ]; + } + + /** + * @dataProvider provideBlobs + * @covers \MediaWiki\Storage\SqlBlobStore::storeBlob + * @covers \MediaWiki\Storage\SqlBlobStore::getBlob + */ + public function testSimpleStoreGetBlobSimpleRoundtrip( $blob ) { + $store = $this->getBlobStore(); + $address = $store->storeBlob( $blob ); + $this->assertSame( $blob, $store->getBlob( $address ) ); + } + +} diff --git a/tests/phpunit/includes/TitleMethodsTest.php b/tests/phpunit/includes/TitleMethodsTest.php index b760c22489..54cdbe28e7 100644 --- a/tests/phpunit/includes/TitleMethodsTest.php +++ b/tests/phpunit/includes/TitleMethodsTest.php @@ -325,6 +325,9 @@ class TitleMethodsTest extends MediaWikiLangTestCase { $this->assertEquals( $expected, $title->getOtherPage()->getPrefixedText() ); } + /** + * @covers Title::clearCaches + */ public function testClearCaches() { $linkCache = LinkCache::singleton(); diff --git a/tests/phpunit/includes/TitleTest.php b/tests/phpunit/includes/TitleTest.php index 75e0c3ef01..d12e4b8643 100644 --- a/tests/phpunit/includes/TitleTest.php +++ b/tests/phpunit/includes/TitleTest.php @@ -552,6 +552,7 @@ class TitleTest extends MediaWikiTestCase { } /** + * @covers Title::newFromTitleValue * @dataProvider provideNewFromTitleValue */ public function testNewFromTitleValue( TitleValue $value ) { @@ -572,6 +573,7 @@ class TitleTest extends MediaWikiTestCase { } /** + * @covers Title::getTitleValue * @dataProvider provideGetTitleValue */ public function testGetTitleValue( $text ) { @@ -603,6 +605,7 @@ class TitleTest extends MediaWikiTestCase { } /** + * @covers Title::getFragment * @dataProvider provideGetFragment * * @param string $full @@ -913,6 +916,7 @@ class TitleTest extends MediaWikiTestCase { } /** + * @covers Title::getFragmentForURL * @dataProvider provideGetFragmentForURL * * @param string $titleStr diff --git a/tests/phpunit/includes/XmlJsTest.php b/tests/phpunit/includes/XmlJsTest.php index 29e97eb644..c2bc73ec4d 100644 --- a/tests/phpunit/includes/XmlJsTest.php +++ b/tests/phpunit/includes/XmlJsTest.php @@ -3,7 +3,7 @@ /** * @group Xml */ -class XmlJs extends PHPUnit_Framework_TestCase { +class XmlJsTest extends PHPUnit_Framework_TestCase { /** * @covers XmlJsCode::__construct diff --git a/tests/phpunit/includes/XmlTest.php b/tests/phpunit/includes/XmlTest.php index 4d4fa7bb9a..4a280da374 100644 --- a/tests/phpunit/includes/XmlTest.php +++ b/tests/phpunit/includes/XmlTest.php @@ -141,6 +141,57 @@ class XmlTest extends MediaWikiTestCase { $this->assertEquals( '</element>', Xml::closeElement( 'element' ), 'closeElement() shortcut' ); } + public function provideMonthSelector() { + global $wgLang; + + $header = '<select name="month" id="month" class="mw-month-selector">'; + $header2 = '<select name="month" id="monthSelector" class="mw-month-selector">'; + $monthsString = ''; + for ( $i = 1; $i < 13; $i++ ) { + $monthName = $wgLang->getMonthName( $i ); + $monthsString .= "<option value=\"{$i}\">{$monthName}</option>"; + if ( $i !== 12 ) { + $monthsString .= "\n"; + } + } + $monthsString2 = str_replace( + '<option value="12">December</option>', + '<option value="12" selected="">December</option>', + $monthsString + ); + $end = '</select>'; + + $allMonths = "<option value=\"AllMonths\">all</option>\n"; + return [ + [ $header . $monthsString . $end, '', null, 'month' ], + [ $header . $monthsString2 . $end, 12, null, 'month' ], + [ $header2 . $monthsString . $end, '', null, 'monthSelector' ], + [ $header . $allMonths . $monthsString . $end, '', 'AllMonths', 'month' ], + + ]; + } + + /** + * @covers Xml::monthSelector + * @dataProvider provideMonthSelector + */ + public function testMonthSelector( $expected, $selected, $allmonths, $id ) { + $this->assertEquals( + $expected, + Xml::monthSelector( $selected, $allmonths, $id ) + ); + } + + /** + * @covers Xml::span + */ + public function testSpan() { + $this->assertEquals( + '<span class="foo" id="testSpan">element</span>', + Xml::span( 'element', 'foo', [ 'id' => 'testSpan' ] ) + ); + } + /** * @covers Xml::dateMenu */ @@ -533,4 +584,34 @@ class XmlTest extends MediaWikiTestCase { 'Entire element with legend and attributes' ); } + + /** + * @covers Xml::buildTable + */ + public function testBuildTable() { + $firstRow = [ 'foo', 'bar' ]; + $secondRow = [ 'Berlin', 'Tehran' ]; + $headers = [ 'header1', 'header2' ]; + $expected = '<table id="testTable"><thead id="testTable"><th>header1</th>' . + '<th>header2</th></thead><tr><td>foo</td><td>bar</td></tr><tr><td>Berlin</td>' . + '<td>Tehran</td></tr></table>'; + $this->assertEquals( + $expected, + Xml::buildTable( + [ $firstRow, $secondRow ], + [ 'id' => 'testTable' ], + $headers + ) + ); + } + + /** + * @covers Xml::buildTableRow + */ + public function testBuildTableRow() { + $this->assertEquals( + '<tr id="testRow"><td>foo</td><td>bar</td></tr>', + Xml::buildTableRow( [ 'id' => 'testRow' ], [ 'foo', 'bar' ] ) + ); + } } diff --git a/tests/phpunit/includes/api/ApiComparePagesTest.php b/tests/phpunit/includes/api/ApiComparePagesTest.php index 989d6bb536..9399ef8ea8 100644 --- a/tests/phpunit/includes/api/ApiComparePagesTest.php +++ b/tests/phpunit/includes/api/ApiComparePagesTest.php @@ -29,7 +29,7 @@ class ApiComparePagesTest extends ApiTestCase { $status = $page->doEditContent( $content, 'Test for ApiComparePagesTest: ' . $text, 0, false, $user ); - if ( !$status->isOk() ) { + if ( !$status->isOK() ) { $this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) ); } return $status->value['revision']->getId(); diff --git a/tests/phpunit/includes/api/ApiParseTest.php b/tests/phpunit/includes/api/ApiParseTest.php index 07bf299093..e236437722 100644 --- a/tests/phpunit/includes/api/ApiParseTest.php +++ b/tests/phpunit/includes/api/ApiParseTest.php @@ -21,7 +21,7 @@ class ApiParseTest extends ApiTestCase { ContentHandler::makeContent( 'Test for revdel', $title, CONTENT_MODEL_WIKITEXT ), __METHOD__ . ' Test for revdel', 0, false, $user ); - if ( !$status->isOk() ) { + if ( !$status->isOK() ) { $this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) ); } self::$pageId = $status->value['revision']->getPage(); @@ -31,7 +31,7 @@ class ApiParseTest extends ApiTestCase { ContentHandler::makeContent( 'Test for oldid', $title, CONTENT_MODEL_WIKITEXT ), __METHOD__ . ' Test for oldid', 0, false, $user ); - if ( !$status->isOk() ) { + if ( !$status->isOK() ) { $this->fail( "Failed to edit $title: " . $status->getWikiText( false, false, 'en' ) ); } self::$revIds['oldid'] = $status->value['revision']->getId(); @@ -40,7 +40,7 @@ class ApiParseTest extends ApiTestCase { ContentHandler::makeContent( 'Test for latest', $title, CONTENT_MODEL_WIKITEXT ), __METHOD__ . ' Test for latest', 0, false, $user ); - if ( !$status->isOk() ) { + if ( !$status->isOK() ) { $this->fail( "Failed to edit $title: " . $status->getWikiText( false, false, 'en' ) ); } self::$revIds['latest'] = $status->value['revision']->getId(); diff --git a/tests/phpunit/includes/api/ApiQueryRecentChangesIntegrationTest.php b/tests/phpunit/includes/api/ApiQueryRecentChangesIntegrationTest.php new file mode 100644 index 0000000000..19f66fa9f8 --- /dev/null +++ b/tests/phpunit/includes/api/ApiQueryRecentChangesIntegrationTest.php @@ -0,0 +1,973 @@ +<?php + +use MediaWiki\Linker\LinkTarget; +use MediaWiki\MediaWikiServices; + +/** + * @group API + * @group Database + * @group medium + * + * @covers ApiQueryRecentChanges + */ +class ApiQueryRecentChangesIntegrationTest extends ApiTestCase { + + public function __construct( $name = null, array $data = [], $dataName = '' ) { + parent::__construct( $name, $data, $dataName ); + + $this->tablesUsed[] = 'recentchanges'; + $this->tablesUsed[] = 'page'; + } + + protected function setUp() { + parent::setUp(); + + self::$users['ApiQueryRecentChangesIntegrationTestUser'] = $this->getMutableTestUser(); + $this->doLogin( 'ApiQueryRecentChangesIntegrationTestUser' ); + wfGetDB( DB_MASTER )->delete( 'recentchanges', '*', __METHOD__ ); + } + + private function getLoggedInTestUser() { + return self::$users['ApiQueryRecentChangesIntegrationTestUser']->getUser(); + } + + private function doPageEdit( User $user, LinkTarget $target, $summary ) { + static $i = 0; + + $title = Title::newFromLinkTarget( $target ); + $page = WikiPage::factory( $title ); + $page->doEditContent( + ContentHandler::makeContent( __CLASS__ . $i++, $title ), + $summary, + 0, + false, + $user + ); + } + + private function doMinorPageEdit( User $user, LinkTarget $target, $summary ) { + $title = Title::newFromLinkTarget( $target ); + $page = WikiPage::factory( $title ); + $page->doEditContent( + ContentHandler::makeContent( __CLASS__, $title ), + $summary, + EDIT_MINOR, + false, + $user + ); + } + + private function doBotPageEdit( User $user, LinkTarget $target, $summary ) { + $title = Title::newFromLinkTarget( $target ); + $page = WikiPage::factory( $title ); + $page->doEditContent( + ContentHandler::makeContent( __CLASS__, $title ), + $summary, + EDIT_FORCE_BOT, + false, + $user + ); + } + + private function doAnonPageEdit( LinkTarget $target, $summary ) { + $title = Title::newFromLinkTarget( $target ); + $page = WikiPage::factory( $title ); + $page->doEditContent( + ContentHandler::makeContent( __CLASS__, $title ), + $summary, + 0, + false, + User::newFromId( 0 ) + ); + } + + private function deletePage( LinkTarget $target, $reason ) { + $title = Title::newFromLinkTarget( $target ); + $page = WikiPage::factory( $title ); + $page->doDeleteArticleReal( $reason ); + } + + /** + * Performs a batch of page edits as a specified user + * @param User $user + * @param array $editData associative array, keys: + * - target => LinkTarget page to edit + * - summary => string edit summary + * - minorEdit => bool mark as minor edit if true (defaults to false) + * - botEdit => bool mark as bot edit if true (defaults to false) + */ + private function doPageEdits( User $user, array $editData ) { + foreach ( $editData as $singleEditData ) { + if ( array_key_exists( 'minorEdit', $singleEditData ) && $singleEditData['minorEdit'] ) { + $this->doMinorPageEdit( + $user, + $singleEditData['target'], + $singleEditData['summary'] + ); + continue; + } + if ( array_key_exists( 'botEdit', $singleEditData ) && $singleEditData['botEdit'] ) { + $this->doBotPageEdit( + $user, + $singleEditData['target'], + $singleEditData['summary'] + ); + continue; + } + $this->doPageEdit( + $user, + $singleEditData['target'], + $singleEditData['summary'] + ); + } + } + + private function doListRecentChangesRequest( array $params = [] ) { + return $this->doApiRequest( + array_merge( + [ 'action' => 'query', 'list' => 'recentchanges' ], + $params + ), + null, + false + ); + } + + private function doGeneratorRecentChangesRequest( array $params = [] ) { + return $this->doApiRequest( + array_merge( + [ 'action' => 'query', 'generator' => 'recentchanges' ], + $params + ) + ); + } + + private function getItemsFromApiResponse( array $response ) { + return $response[0]['query']['recentchanges']; + } + + private function getTitleFormatter() { + return new MediaWikiTitleCodec( + Language::factory( 'en' ), + MediaWikiServices::getInstance()->getGenderCache() + ); + } + + private function getPrefixedText( LinkTarget $target ) { + $formatter = $this->getTitleFormatter(); + return $formatter->getPrefixedText( $target ); + } + + public function testListRecentChanges_returnsRCInfo() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $result = $this->doListRecentChangesRequest(); + + $this->assertArrayHasKey( 'query', $result[0] ); + $this->assertArrayHasKey( 'recentchanges', $result[0]['query'] ); + + $items = $this->getItemsFromApiResponse( $result ); + $this->assertCount( 1, $items ); + $item = $items[0]; + $this->assertArraySubset( + [ + 'type' => 'new', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ], + $item + ); + $this->assertArrayNotHasKey( 'bot', $item ); + $this->assertArrayNotHasKey( 'new', $item ); + $this->assertArrayNotHasKey( 'minor', $item ); + $this->assertArrayHasKey( 'pageid', $item ); + $this->assertArrayHasKey( 'revid', $item ); + $this->assertArrayHasKey( 'old_revid', $item ); + } + + public function testIdsPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'ids', ] ); + $items = $this->getItemsFromApiResponse( $result ); + + $this->assertCount( 1, $items ); + $this->assertArrayHasKey( 'pageid', $items[0] ); + $this->assertArrayHasKey( 'revid', $items[0] ); + $this->assertArrayHasKey( 'old_revid', $items[0] ); + $this->assertEquals( 'new', $items[0]['type'] ); + } + + public function testTitlePropParameter() { + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $subjectTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $talkTarget, + 'summary' => 'Create Talk page', + ], + ] + ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $talkTarget->getNamespace(), + 'title' => $this->getPrefixedText( $talkTarget ), + ], + [ + 'type' => 'new', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testFlagsPropParameter() { + $normalEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $minorEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPageM' ); + $botEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPageB' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $normalEditTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $minorEditTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $minorEditTarget, + 'summary' => 'Change content', + 'minorEdit' => true, + ], + [ + 'target' => $botEditTarget, + 'summary' => 'Create the page with a bot', + 'botEdit' => true, + ], + ] + ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'flags', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'new' => true, + 'minor' => false, + 'bot' => true, + ], + [ + 'type' => 'edit', + 'new' => false, + 'minor' => true, + 'bot' => false, + ], + [ + 'type' => 'new', + 'new' => true, + 'minor' => false, + 'bot' => false, + ], + [ + 'type' => 'new', + 'new' => true, + 'minor' => false, + 'bot' => false, + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testUserPropParameter() { + $userEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $anonEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPageA' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $userEditTarget, 'Create the page' ); + $this->doAnonPageEdit( $anonEditTarget, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'user', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'anon' => true, + 'user' => User::newFromId( 0 )->getName(), + ], + [ + 'type' => 'new', + 'user' => $this->getLoggedInTestUser()->getName(), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testUserIdPropParameter() { + $user = $this->getLoggedInTestUser(); + $userEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $anonEditTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPageA' ); + $this->doPageEdit( $user, $userEditTarget, 'Create the page' ); + $this->doAnonPageEdit( $anonEditTarget, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'userid', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'anon' => true, + 'userid' => 0, + ], + [ + 'type' => 'new', + 'userid' => $user->getId(), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testCommentPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the <b>page</b>' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'comment', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'comment' => 'Create the <b>page</b>', + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testParsedCommentPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the <b>page</b>' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'parsedcomment', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'parsedcomment' => 'Create the <b>page</b>', + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testTimestampPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'timestamp', ] ); + $items = $this->getItemsFromApiResponse( $result ); + + $this->assertCount( 1, $items ); + $this->assertArrayHasKey( 'timestamp', $items[0] ); + $this->assertInternalType( 'string', $items[0]['timestamp'] ); + } + + public function testSizesPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'sizes', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'oldlen' => 0, + 'newlen' => 38, + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + private function createPageAndDeleteIt( LinkTarget $target ) { + $this->doPageEdit( $this->getLoggedInTestUser(), + $target, + 'Create the page that will be deleted' + ); + $this->deletePage( $target, 'Important Reason' ); + } + + public function testLoginfoPropParameter() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->createPageAndDeleteIt( $target ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'loginfo', ] ); + + $items = $this->getItemsFromApiResponse( $result ); + $this->assertCount( 1, $items ); + $this->assertArraySubset( + [ + 'type' => 'log', + 'logtype' => 'delete', + 'logaction' => 'delete', + 'logparams' => [], + ], + $items[0] + ); + $this->assertArrayHasKey( 'logid', $items[0] ); + } + + public function testEmptyPropParameter() { + $user = $this->getLoggedInTestUser(); + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $user, $target, 'Create the page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => '', ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + ] + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testNamespaceParam() { + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $subjectTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $talkTarget, + 'summary' => 'Create the talk page', + ], + ] + ); + + $result = $this->doListRecentChangesRequest( [ 'rcnamespace' => '0', ] ); + + $items = $this->getItemsFromApiResponse( $result ); + $this->assertCount( 1, $items ); + $this->assertArraySubset( + [ + 'ns' => 0, + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + $items[0] + ); + } + + public function testShowAnonParams() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doAnonPageEdit( $target, 'Create the page' ); + + $resultAnon = $this->doListRecentChangesRequest( [ + 'rcprop' => 'user', + 'rcshow' => WatchedItemQueryService::FILTER_ANON + ] ); + $resultNotAnon = $this->doListRecentChangesRequest( [ + 'rcprop' => 'user', + 'rcshow' => WatchedItemQueryService::FILTER_NOT_ANON + ] ); + + $items = $this->getItemsFromApiResponse( $resultAnon ); + $this->assertCount( 1, $items ); + $this->assertArraySubset( [ 'anon' => true ], $items[0] ); + $this->assertEmpty( $this->getItemsFromApiResponse( $resultNotAnon ) ); + } + + public function testNewAndEditTypeParameters() { + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $subjectTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $subjectTarget, + 'summary' => 'Change the content', + ], + [ + 'target' => $talkTarget, + 'summary' => 'Create Talk page', + ], + ] + ); + + $resultNew = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rctype' => 'new' ] ); + $resultEdit = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rctype' => 'edit' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $talkTarget->getNamespace(), + 'title' => $this->getPrefixedText( $talkTarget ), + ], + [ + 'type' => 'new', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + ], + $this->getItemsFromApiResponse( $resultNew ) + ); + $this->assertEquals( + [ + [ + 'type' => 'edit', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + ], + $this->getItemsFromApiResponse( $resultEdit ) + ); + } + + public function testLogTypeParameters() { + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->createPageAndDeleteIt( $subjectTarget ); + $this->doPageEdit( $this->getLoggedInTestUser(), $talkTarget, 'Create Talk page' ); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rctype' => 'log' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'log', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + private function getExternalRC( LinkTarget $target ) { + $title = Title::newFromLinkTarget( $target ); + + $rc = new RecentChange; + $rc->mTitle = $title; + $rc->mAttribs = [ + 'rc_timestamp' => wfTimestamp( TS_MW ), + 'rc_namespace' => $title->getNamespace(), + 'rc_title' => $title->getDBkey(), + 'rc_type' => RC_EXTERNAL, + 'rc_source' => 'foo', + 'rc_minor' => 0, + 'rc_cur_id' => $title->getArticleID(), + 'rc_user' => 0, + 'rc_user_text' => 'External User', + 'rc_comment' => '', + 'rc_comment_text' => '', + 'rc_comment_data' => null, + 'rc_this_oldid' => $title->getLatestRevID(), + 'rc_last_oldid' => $title->getLatestRevID(), + 'rc_bot' => 0, + 'rc_ip' => '', + 'rc_patrolled' => 0, + 'rc_new' => 0, + 'rc_old_len' => $title->getLength(), + 'rc_new_len' => $title->getLength(), + 'rc_deleted' => 0, + 'rc_logid' => 0, + 'rc_log_type' => null, + 'rc_log_action' => '', + 'rc_params' => '', + ]; + $rc->mExtra = [ + 'prefixedDBkey' => $title->getPrefixedDBkey(), + 'lastTimestamp' => 0, + 'oldSize' => $title->getLength(), + 'newSize' => $title->getLength(), + 'pageStatus' => 'changed' + ]; + + return $rc; + } + + public function testExternalTypeParameters() { + $user = $this->getLoggedInTestUser(); + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $user, $subjectTarget, 'Create the page' ); + $this->doPageEdit( $user, $talkTarget, 'Create Talk page' ); + + $rc = $this->getExternalRC( $subjectTarget ); + $rc->save(); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rctype' => 'external' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'external', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testCategorizeTypeParameter() { + $user = $this->getLoggedInTestUser(); + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $categoryTarget = new TitleValue( NS_CATEGORY, 'ApiQueryRecentChangesIntegrationTestCategory' ); + $this->doPageEdits( + $user, + [ + [ + 'target' => $categoryTarget, + 'summary' => 'Create the category', + ], + [ + 'target' => $subjectTarget, + 'summary' => 'Create the page and add it to the category', + ], + ] + ); + $title = Title::newFromLinkTarget( $subjectTarget ); + $revision = Revision::newFromTitle( $title ); + + $rc = RecentChange::newForCategorization( + $revision->getTimestamp(), + Title::newFromLinkTarget( $categoryTarget ), + $user, + $revision->getComment(), + $title, + 0, + $revision->getId(), + null, + false + ); + $rc->save(); + + $result = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rctype' => 'categorize' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'categorize', + 'ns' => $categoryTarget->getNamespace(), + 'title' => $this->getPrefixedText( $categoryTarget ), + ], + ], + $this->getItemsFromApiResponse( $result ) + ); + } + + public function testLimitParam() { + $target1 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $target2 = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $target3 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage2' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $target1, + 'summary' => 'Create the page', + ], + [ + 'target' => $target2, + 'summary' => 'Create Talk page', + ], + [ + 'target' => $target3, + 'summary' => 'Create the page', + ], + ] + ); + + $resultWithoutLimit = $this->doListRecentChangesRequest( [ 'rcprop' => 'title' ] ); + $resultWithLimit = $this->doListRecentChangesRequest( [ 'rclimit' => 2, 'rcprop' => 'title' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $target3->getNamespace(), + 'title' => $this->getPrefixedText( $target3 ) + ], + [ + 'type' => 'new', + 'ns' => $target2->getNamespace(), + 'title' => $this->getPrefixedText( $target2 ) + ], + [ + 'type' => 'new', + 'ns' => $target1->getNamespace(), + 'title' => $this->getPrefixedText( $target1 ) + ], + ], + $this->getItemsFromApiResponse( $resultWithoutLimit ) + ); + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $target3->getNamespace(), + 'title' => $this->getPrefixedText( $target3 ) + ], + [ + 'type' => 'new', + 'ns' => $target2->getNamespace(), + 'title' => $this->getPrefixedText( $target2 ) + ], + ], + $this->getItemsFromApiResponse( $resultWithLimit ) + ); + $this->assertArrayHasKey( 'continue', $resultWithLimit[0] ); + $this->assertArrayHasKey( 'rccontinue', $resultWithLimit[0]['continue'] ); + } + + public function testAllRevParam() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $target, + 'summary' => 'Create the page', + ], + [ + 'target' => $target, + 'summary' => 'Change the content', + ], + ] + ); + + $resultAllRev = $this->doListRecentChangesRequest( [ 'rcprop' => 'title', 'rcallrev' => '', ] ); + $resultNoAllRev = $this->doListRecentChangesRequest( [ 'rcprop' => 'title' ] ); + + $this->assertEquals( + [ + [ + 'type' => 'edit', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ], + [ + 'type' => 'new', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ], + ], + $this->getItemsFromApiResponse( $resultNoAllRev ) + ); + $this->assertEquals( + [ + [ + 'type' => 'edit', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ], + [ + 'type' => 'new', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ], + ], + $this->getItemsFromApiResponse( $resultAllRev ) + ); + } + + public function testDirParams() { + $subjectTarget = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $talkTarget = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $subjectTarget, + 'summary' => 'Create the page', + ], + [ + 'target' => $talkTarget, + 'summary' => 'Create Talk page', + ], + ] + ); + + $resultDirOlder = $this->doListRecentChangesRequest( + [ 'rcdir' => 'older', 'rcprop' => 'title' ] + ); + $resultDirNewer = $this->doListRecentChangesRequest( + [ 'rcdir' => 'newer', 'rcprop' => 'title' ] + ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $talkTarget->getNamespace(), + 'title' => $this->getPrefixedText( $talkTarget ) + ], + [ + 'type' => 'new', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ) + ], + ], + $this->getItemsFromApiResponse( $resultDirOlder ) + ); + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $subjectTarget->getNamespace(), + 'title' => $this->getPrefixedText( $subjectTarget ) + ], + [ + 'type' => 'new', + 'ns' => $talkTarget->getNamespace(), + 'title' => $this->getPrefixedText( $talkTarget ) + ], + ], + $this->getItemsFromApiResponse( $resultDirNewer ) + ); + } + + public function testStartEndParams() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $resultStart = $this->doListRecentChangesRequest( [ + 'rcstart' => '20010115000000', + 'rcdir' => 'newer', + 'rcprop' => 'title', + ] ); + $resultEnd = $this->doListRecentChangesRequest( [ + 'rcend' => '20010115000000', + 'rcdir' => 'newer', + 'rcprop' => 'title', + ] ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + ] + ], + $this->getItemsFromApiResponse( $resultStart ) + ); + $this->assertEmpty( $this->getItemsFromApiResponse( $resultEnd ) ); + } + + public function testContinueParam() { + $target1 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $target2 = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage' ); + $target3 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage2' ); + $this->doPageEdits( + $this->getLoggedInTestUser(), + [ + [ + 'target' => $target1, + 'summary' => 'Create the page', + ], + [ + 'target' => $target2, + 'summary' => 'Create Talk page', + ], + [ + 'target' => $target3, + 'summary' => 'Create the page', + ], + ] + ); + + $firstResult = $this->doListRecentChangesRequest( [ 'rclimit' => 2, 'rcprop' => 'title' ] ); + $this->assertArrayHasKey( 'continue', $firstResult[0] ); + $this->assertArrayHasKey( 'rccontinue', $firstResult[0]['continue'] ); + + $continuationParam = $firstResult[0]['continue']['rccontinue']; + + $continuedResult = $this->doListRecentChangesRequest( + [ 'rccontinue' => $continuationParam, 'rcprop' => 'title' ] + ); + + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $target3->getNamespace(), + 'title' => $this->getPrefixedText( $target3 ), + ], + [ + 'type' => 'new', + 'ns' => $target2->getNamespace(), + 'title' => $this->getPrefixedText( $target2 ), + ], + ], + $this->getItemsFromApiResponse( $firstResult ) + ); + $this->assertEquals( + [ + [ + 'type' => 'new', + 'ns' => $target1->getNamespace(), + 'title' => $this->getPrefixedText( $target1 ) + ] + ], + $this->getItemsFromApiResponse( $continuedResult ) + ); + } + + public function testGeneratorRecentChangesPropInfo_returnsRCPages() { + $target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' ); + $this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' ); + + $result = $this->doGeneratorRecentChangesRequest( [ 'prop' => 'info' ] ); + + $this->assertArrayHasKey( 'query', $result[0] ); + $this->assertArrayHasKey( 'pages', $result[0]['query'] ); + + // $result[0]['query']['pages'] uses page ids as keys. Page ids don't matter here, so drop them + $pages = array_values( $result[0]['query']['pages'] ); + + $this->assertCount( 1, $pages ); + $this->assertArraySubset( + [ + 'ns' => $target->getNamespace(), + 'title' => $this->getPrefixedText( $target ), + 'new' => true, + ], + $pages[0] + ); + } + +} diff --git a/tests/phpunit/includes/collation/CollationFaTest.php b/tests/phpunit/includes/collation/CollationFaTest.php index 53a4f7b7f2..f7455419ad 100644 --- a/tests/phpunit/includes/collation/CollationFaTest.php +++ b/tests/phpunit/includes/collation/CollationFaTest.php @@ -1,4 +1,8 @@ <?php + +/** + * @covers CollationFa + */ class CollationFaTest extends MediaWikiTestCase { /* @@ -9,9 +13,7 @@ class CollationFaTest extends MediaWikiTestCase { public function setUp() { parent::setUp(); - if ( !extension_loaded( 'intl' ) ) { - $this->markTestSkipped( "PHP extension 'intl' is not loaded, skipping." ); - } + $this->checkPHPExtension( 'intl' ); } /** diff --git a/tests/phpunit/includes/collation/CustomUppercaseCollationTest.php b/tests/phpunit/includes/collation/CustomUppercaseCollationTest.php index 5d5317be7b..d68892819d 100644 --- a/tests/phpunit/includes/collation/CustomUppercaseCollationTest.php +++ b/tests/phpunit/includes/collation/CustomUppercaseCollationTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers CustomUppercaseCollation + */ class CustomUppercaseCollationTest extends MediaWikiTestCase { public function setUp() { diff --git a/tests/phpunit/includes/config/EtcdConfigTest.php b/tests/phpunit/includes/config/EtcdConfigTest.php index ebe1972547..3e70a07819 100644 --- a/tests/phpunit/includes/config/EtcdConfigTest.php +++ b/tests/phpunit/includes/config/EtcdConfigTest.php @@ -2,7 +2,7 @@ use Wikimedia\TestingAccessWrapper; -class EtcConfigTest extends PHPUnit_Framework_TestCase { +class EtcdConfigTest extends PHPUnit_Framework_TestCase { private function createConfigMock( array $options = [] ) { return $this->getMockBuilder( EtcdConfig::class ) diff --git a/tests/phpunit/includes/content/ContentHandlerTest.php b/tests/phpunit/includes/content/ContentHandlerTest.php index 1bd2eb04af..1462c3627e 100644 --- a/tests/phpunit/includes/content/ContentHandlerTest.php +++ b/tests/phpunit/includes/content/ContentHandlerTest.php @@ -332,7 +332,9 @@ class ContentHandlerTest extends MediaWikiTestCase { } } - /* + /** + * @covers ContentHandler::getAutosummary + * * Test if we become a "Created blank page" summary from getAutoSummary if no Content added to * page. */ @@ -374,11 +376,17 @@ class ContentHandlerTest extends MediaWikiTestCase { } */ + /** + * @covers ContentHandler::supportsCategories + */ public function testSupportsCategories() { $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT ); $this->assertTrue( $handler->supportsCategories(), 'content model supports categories' ); } + /** + * @covers ContentHandler::supportsDirectEditing + */ public function testSupportsDirectEditing() { $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_JSON ); $this->assertFalse( $handler->supportsDirectEditing(), 'direct editing is not supported' ); @@ -407,6 +415,7 @@ class ContentHandlerTest extends MediaWikiTestCase { } /** + * @covers ContentHandler::getForModelID * @dataProvider provideGetModelForID */ public function testGetModelForID( $modelId, $handlerClass ) { @@ -415,6 +424,9 @@ class ContentHandlerTest extends MediaWikiTestCase { $this->assertInstanceOf( $handlerClass, $handler ); } + /** + * @covers ContentHandler::getFieldsForSearchIndex + */ public function testGetFieldsForSearchIndex() { $searchEngine = $this->newSearchEngine(); diff --git a/tests/phpunit/includes/content/CssContentTest.php b/tests/phpunit/includes/content/CssContentTest.php index d2078d7a23..1e82fdd4d3 100644 --- a/tests/phpunit/includes/content/CssContentTest.php +++ b/tests/phpunit/includes/content/CssContentTest.php @@ -83,6 +83,7 @@ class CssContentTest extends JavaScriptContentTest { } /** + * @covers CssContent::getRedirectTarget * @dataProvider provideGetRedirectTarget */ public function testGetRedirectTarget( $title, $text ) { diff --git a/tests/phpunit/includes/content/FileContentHandlerTest.php b/tests/phpunit/includes/content/FileContentHandlerTest.php index 65efcc9e80..ad9d41958c 100644 --- a/tests/phpunit/includes/content/FileContentHandlerTest.php +++ b/tests/phpunit/includes/content/FileContentHandlerTest.php @@ -2,6 +2,8 @@ /** * @group ContentHandler + * + * @covers FileContentHandler */ class FileContentHandlerTest extends MediaWikiLangTestCase { /** diff --git a/tests/phpunit/includes/content/JavaScriptContentTest.php b/tests/phpunit/includes/content/JavaScriptContentTest.php index 1c746bcdda..434e17cfb4 100644 --- a/tests/phpunit/includes/content/JavaScriptContentTest.php +++ b/tests/phpunit/includes/content/JavaScriptContentTest.php @@ -294,6 +294,7 @@ class JavaScriptContentTest extends TextContentTest { } /** + * @covers JavaScriptContent::getRedirectTarget * @dataProvider provideGetRedirectTarget */ public function testGetRedirectTarget( $title, $text ) { diff --git a/tests/phpunit/includes/content/TextContentHandlerTest.php b/tests/phpunit/includes/content/TextContentHandlerTest.php index 7d9f74eca0..a85215bea4 100644 --- a/tests/phpunit/includes/content/TextContentHandlerTest.php +++ b/tests/phpunit/includes/content/TextContentHandlerTest.php @@ -4,6 +4,9 @@ * @group ContentHandler */ class TextContentHandlerTest extends MediaWikiLangTestCase { + /** + * @covers TextContentHandler::supportsDirectEditing + */ public function testSupportsDirectEditing() { $handler = new TextContentHandler(); $this->assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' ); diff --git a/tests/phpunit/includes/content/WikitextContentHandlerTest.php b/tests/phpunit/includes/content/WikitextContentHandlerTest.php index 77cfb92bbc..02f82f4dac 100644 --- a/tests/phpunit/includes/content/WikitextContentHandlerTest.php +++ b/tests/phpunit/includes/content/WikitextContentHandlerTest.php @@ -115,6 +115,9 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase { $this->assertEquals( $supported, $this->handler->isSupportedFormat( $format ) ); } + /** + * @covers WikitextContentHandler::supportsDirectEditing + */ public function testSupportsDirectEditing() { $handler = new WikiTextContentHandler(); $this->assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' ); @@ -349,6 +352,9 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase { } */ + /** + * @covers WikitextContentHandler::getDataForSearchIndex + */ public function testDataIndexFieldsFile() { $mockEngine = $this->createMock( 'SearchEngine' ); $title = Title::newFromText( 'Somefile.jpg', NS_FILE ); diff --git a/tests/phpunit/includes/content/WikitextStructureTest.php b/tests/phpunit/includes/content/WikitextStructureTest.php index f1b54f6abe..1bdbe0157b 100644 --- a/tests/phpunit/includes/content/WikitextStructureTest.php +++ b/tests/phpunit/includes/content/WikitextStructureTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers WikiTextStructure + */ class WikitextStructureTest extends MediaWikiLangTestCase { private function getMockTitle() { diff --git a/tests/phpunit/includes/db/LoadBalancerTest.php b/tests/phpunit/includes/db/LoadBalancerTest.php index f8ab7f481b..a8e7f898a4 100644 --- a/tests/phpunit/includes/db/LoadBalancerTest.php +++ b/tests/phpunit/includes/db/LoadBalancerTest.php @@ -1,5 +1,7 @@ <?php +use Wikimedia\Rdbms\DBError; +use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\LoadBalancer; /** @@ -24,7 +26,7 @@ use Wikimedia\Rdbms\LoadBalancer; * @file */ class LoadBalancerTest extends MediaWikiTestCase { - public function testLBSimpleServer() { + public function testWithoutReplica() { global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir; $servers = [ @@ -48,10 +50,11 @@ class LoadBalancerTest extends MediaWikiTestCase { $dbw = $lb->getConnection( DB_MASTER ); $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' ); $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on master" ); + $this->assertWriteAllowed( $dbw ); $dbr = $lb->getConnection( DB_REPLICA ); $this->assertTrue( $dbr->getLBInfo( 'master' ), 'DB_REPLICA also gets the master' ); - $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" ); + $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" ); $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTO ); $this->assertFalse( $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTO" ); @@ -69,7 +72,7 @@ class LoadBalancerTest extends MediaWikiTestCase { $lb->closeAll(); } - public function testLBSimpleServers() { + public function testWithReplica() { global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir; $servers = [ @@ -83,7 +86,7 @@ class LoadBalancerTest extends MediaWikiTestCase { 'load' => 0, 'flags' => DBO_TRX // REPEATABLE-READ for consistency ], - [ // emulated slave + [ // emulated replica 'host' => $wgDBserver, 'dbname' => $wgDBname, 'user' => $wgDBuser, @@ -108,14 +111,16 @@ class LoadBalancerTest extends MediaWikiTestCase { $dbw->getLBInfo( 'clusterMasterHost' ), 'cluster master set' ); $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on master" ); + $this->assertWriteAllowed( $dbw ); $dbr = $lb->getConnection( DB_REPLICA ); - $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' ); + $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'replica shows as replica' ); $this->assertEquals( ( $wgDBserver != '' ) ? $wgDBserver : 'localhost', $dbr->getLBInfo( 'clusterMasterHost' ), 'cluster master set' ); - $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" ); + $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" ); + $this->assertWriteForbidden( $dbr ); $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTO ); $this->assertFalse( $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTO" ); @@ -132,4 +137,30 @@ class LoadBalancerTest extends MediaWikiTestCase { $lb->closeAll(); } + + private function assertWriteForbidden( IDatabase $db ) { + try { + $db->delete( 'user', [ 'user_id' => 57634126 ], 'TEST' ); + $this->fail( 'Write operation should have failed!' ); + } catch ( DBError $ex ) { + // check that the exception message contains "Write operation" + $constraint = new PHPUnit_Framework_Constraint_StringContains( 'Write operation' ); + + if ( !$constraint->evaluate( $ex->getMessage(), '', true ) ) { + // re-throw original error, to preserve stack trace + throw $ex; + } + } finally { + $db->rollback( __METHOD__, 'flush' ); + } + } + + private function assertWriteAllowed( IDatabase $db ) { + try { + $this->assertNotSame( false, $db->delete( 'user', [ 'user_id' => 57634126 ] ) ); + } finally { + $db->rollback( __METHOD__, 'flush' ); + } + } + } diff --git a/tests/phpunit/includes/debug/MWDebugTest.php b/tests/phpunit/includes/debug/MWDebugTest.php index 5c65483150..25cfd3cb8b 100644 --- a/tests/phpunit/includes/debug/MWDebugTest.php +++ b/tests/phpunit/includes/debug/MWDebugTest.php @@ -110,7 +110,7 @@ class MWDebugTest extends MediaWikiTestCase { $this->assertArrayHasKey( $expectedKey, $data['debuginfo'], "debuginfo has $expectedKey" ); } - $xml = ApiFormatXml::recXmlPrint( 'help', $data ); + $xml = ApiFormatXml::recXmlPrint( 'help', $data, null ); // exception not thrown $this->assertInternalType( 'string', $xml ); diff --git a/tests/phpunit/includes/debug/logger/monolog/AvroFormatterTest.php b/tests/phpunit/includes/debug/logger/monolog/AvroFormatterTest.php index 938397ab1f..915a18630e 100644 --- a/tests/phpunit/includes/debug/logger/monolog/AvroFormatterTest.php +++ b/tests/phpunit/includes/debug/logger/monolog/AvroFormatterTest.php @@ -23,6 +23,9 @@ namespace MediaWiki\Logger\Monolog; use MediaWikiTestCase; use PHPUnit_Framework_Error_Notice; +/** + * @covers \MediaWiki\Logger\Monolog\AvroFormatter + */ class AvroFormatterTest extends MediaWikiTestCase { protected function setUp() { diff --git a/tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php b/tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php index 88cd2dd264..a69b0bfbb4 100644 --- a/tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php +++ b/tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php @@ -24,6 +24,9 @@ use MediaWikiTestCase; use Monolog\Logger; use Wikimedia\TestingAccessWrapper; +/** + * @covers \MediaWiki\Logger\Monolog\KafkaHandler + */ class KafkaHandlerTest extends MediaWikiTestCase { protected function setUp() { diff --git a/tests/phpunit/includes/deferred/LinksUpdateTest.php b/tests/phpunit/includes/deferred/LinksUpdateTest.php index 4c0a5fa9c3..ddc0798f1a 100644 --- a/tests/phpunit/includes/deferred/LinksUpdateTest.php +++ b/tests/phpunit/includes/deferred/LinksUpdateTest.php @@ -1,6 +1,7 @@ <?php /** + * @covers LinksUpdate * @group LinksUpdate * @group Database * ^--- make sure temporary tables are used. diff --git a/tests/phpunit/includes/editpage/TextboxBuilderTest.php b/tests/phpunit/includes/editpage/TextboxBuilderTest.php new file mode 100644 index 0000000000..668baddfe6 --- /dev/null +++ b/tests/phpunit/includes/editpage/TextboxBuilderTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright (C) 2017 Kunal Mehta <legoktm@member.fsf.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +namespace MediaWiki\Tests\EditPage; + +use Language; +use MediaWiki\EditPage\TextboxBuilder; +use MediaWikiTestCase; +use Title; +use User; + +/** + * @covers \MediaWiki\EditPage\TextboxBuilder + */ +class TextboxBuilderTest extends MediaWikiTestCase { + + public function provideAddNewLineAtEnd() { + return [ + [ '', '' ], + [ 'foo', "foo\n" ], + ]; + } + + /** + * @dataProvider provideAddNewLineAtEnd + */ + public function testAddNewLineAtEnd( $input, $expected ) { + $builder = new TextboxBuilder(); + $this->assertSame( $expected, $builder->addNewLineAtEnd( $input ) ); + } + + public function testBuildTextboxAttribs() { + $user = new User(); + $user->setOption( 'editfont', 'monospace' ); + + $title = $this->getMockBuilder( Title::class ) + ->disableOriginalConstructor() + ->getMock(); + $title->expects( $this->any() ) + ->method( 'getPageLanguage' ) + ->will( $this->returnValue( Language::factory( 'en' ) ) ); + + $builder = new TextboxBuilder(); + $attribs = $builder->buildTextboxAttribs( + 'mw-textbox1', + [ 'class' => 'foo bar', 'data-foo' => '123', 'rows' => 30 ], + $user, + $title + ); + + $this->assertInternalType( 'array', $attribs ); + // custom attrib showed up + $this->assertArrayHasKey( 'data-foo', $attribs ); + // classes merged properly (string) + $this->assertSame( 'foo bar mw-editfont-monospace', $attribs['class'] ); + // overrides in custom attrib worked + $this->assertSame( 30, $attribs['rows'] ); + $this->assertSame( 'en', $attribs['lang'] ); + + $attribs2 = $builder->buildTextboxAttribs( + 'mw-textbox2', [ 'class' => [ 'foo', 'bar' ] ], $user, $title + ); + // classes merged properly (array) + $this->assertSame( [ 'foo', 'bar', 'mw-editfont-monospace' ], $attribs2['class'] ); + + $attribs3 = $builder->buildTextboxAttribs( + 'mw-textbox3', [], $user, $title + ); + // classes ok when nothing to be merged + $this->assertSame( 'mw-editfont-monospace', $attribs3['class'] ); + } +} diff --git a/tests/phpunit/includes/filerepo/MigrateFileRepoLayoutTest.php b/tests/phpunit/includes/filerepo/MigrateFileRepoLayoutTest.php index 800c2fc76e..205df9cdcd 100644 --- a/tests/phpunit/includes/filerepo/MigrateFileRepoLayoutTest.php +++ b/tests/phpunit/includes/filerepo/MigrateFileRepoLayoutTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers MigrateFileRepoLayout + */ class MigrateFileRepoLayoutTest extends MediaWikiTestCase { protected $tmpPrefix; protected $migratorMock; diff --git a/tests/phpunit/includes/filerepo/RepoGroupTest.php b/tests/phpunit/includes/filerepo/RepoGroupTest.php index 82ff12e3e3..6f04c669a9 100644 --- a/tests/phpunit/includes/filerepo/RepoGroupTest.php +++ b/tests/phpunit/includes/filerepo/RepoGroupTest.php @@ -1,4 +1,8 @@ <?php + +/** + * @covers RepoGroup + */ class RepoGroupTest extends MediaWikiTestCase { function testHasForeignRepoNegative() { diff --git a/tests/phpunit/includes/filerepo/file/FileTest.php b/tests/phpunit/includes/filerepo/file/FileTest.php index 5b5f1b09fe..62e102652d 100644 --- a/tests/phpunit/includes/filerepo/file/FileTest.php +++ b/tests/phpunit/includes/filerepo/file/FileTest.php @@ -6,6 +6,7 @@ class FileTest extends MediaWikiMediaTestCase { * @param string $filename * @param bool $expected * @dataProvider providerCanAnimate + * @covers File::canAnimateThumbIfAppropriate */ function testCanAnimateThumbIfAppropriate( $filename, $expected ) { $this->setMwGlobals( 'wgMaxAnimatedGifArea', 9000 ); diff --git a/tests/phpunit/includes/htmlform/HTMLAutoCompleteSelectFieldTest.php b/tests/phpunit/includes/htmlform/HTMLAutoCompleteSelectFieldTest.php index 33e3a257d2..99bea68dbf 100644 --- a/tests/phpunit/includes/htmlform/HTMLAutoCompleteSelectFieldTest.php +++ b/tests/phpunit/includes/htmlform/HTMLAutoCompleteSelectFieldTest.php @@ -4,7 +4,7 @@ * * @covers HTMLAutoCompleteSelectField */ -class HtmlAutoCompleteSelectFieldTest extends MediaWikiTestCase { +class HTMLAutoCompleteSelectFieldTest extends MediaWikiTestCase { public $options = [ 'Bulgaria' => 'BGR', diff --git a/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php b/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php index f97716b9e5..c1aa1d7109 100644 --- a/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php +++ b/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php @@ -4,7 +4,7 @@ * Unit tests for the HTMLCheckMatrix * @covers HTMLCheckMatrix */ -class HtmlCheckMatrixTest extends MediaWikiTestCase { +class HTMLCheckMatrixTest extends MediaWikiTestCase { static private $defaultOptions = [ 'rows' => [ 'r1', 'r2' ], 'columns' => [ 'c1', 'c2' ], diff --git a/tests/phpunit/includes/htmlform/HTMLFormTest.php b/tests/phpunit/includes/htmlform/HTMLFormTest.php index b7e0053c72..98511ca79c 100644 --- a/tests/phpunit/includes/htmlform/HTMLFormTest.php +++ b/tests/phpunit/includes/htmlform/HTMLFormTest.php @@ -1,6 +1,8 @@ <?php - +/** + * @covers HTMLForm + */ class HTMLFormTest extends MediaWikiTestCase { public function testGetHTML_empty() { $form = new HTMLForm( [] ); diff --git a/tests/phpunit/includes/http/HttpTest.php b/tests/phpunit/includes/http/HttpTest.php index 3790e3a1b3..2d73bac548 100644 --- a/tests/phpunit/includes/http/HttpTest.php +++ b/tests/phpunit/includes/http/HttpTest.php @@ -497,9 +497,7 @@ class HttpTest extends MediaWikiTestCase { * @dataProvider provideCurlConstants */ public function testCurlConstants( $value ) { - if ( !extension_loaded( 'curl' ) ) { - $this->markTestSkipped( "PHP extension 'curl' is not loaded, skipping." ); - } + $this->checkPHPExtension( 'curl' ); $this->assertTrue( defined( $value ), $value . ' not defined' ); } diff --git a/tests/phpunit/includes/jobqueue/JobTest.php b/tests/phpunit/includes/jobqueue/JobTest.php index e2aacae2f6..395d12c5d9 100644 --- a/tests/phpunit/includes/jobqueue/JobTest.php +++ b/tests/phpunit/includes/jobqueue/JobTest.php @@ -101,7 +101,7 @@ class JobTest extends MediaWikiTestCase { * @covers Job::factory */ public function testJobFactory( $handler ) { - $this->mergeMWGlobalArrayValue( 'wgJobClasses', [ 'testdummy' => $handler ] ); + $this->mergeMwGlobalArrayValue( 'wgJobClasses', [ 'testdummy' => $handler ] ); $job = Job::factory( 'testdummy', Title::newMainPage(), [] ); $this->assertInstanceOf( NullJob::class, $job ); diff --git a/tests/phpunit/includes/jobqueue/RefreshLinksPartitionTest.php b/tests/phpunit/includes/jobqueue/RefreshLinksPartitionTest.php index 43d626db3d..f874f6dea5 100644 --- a/tests/phpunit/includes/jobqueue/RefreshLinksPartitionTest.php +++ b/tests/phpunit/includes/jobqueue/RefreshLinksPartitionTest.php @@ -16,6 +16,7 @@ class RefreshLinksPartitionTest extends MediaWikiTestCase { /** * @dataProvider provider_backlinks + * @covers BacklinkJobUtils::partitionBacklinkJob */ public function testRefreshLinks( $ns, $dbKey, $pages ) { $title = Title::makeTitle( $ns, $dbKey ); diff --git a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php index ca12f6c777..12b2c04ab6 100644 --- a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php +++ b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php @@ -59,6 +59,20 @@ class JavaScriptMinifierTest extends PHPUnit_Framework_TestCase { [ "0xFF.\nx;", "0xFF.x;" ], [ "5.3.\nx;", "5.3.x;" ], + // Cover failure case for incomplete hex literal + [ "0x;", false, false ], + + // Cover failure case for number with no digits after E + [ "1.4E", false, false ], + + // Cover failure case for number with several E + [ "1.4EE2", false, false ], + [ "1.4EE", false, false ], + + // Cover failure case for number with several E (nonconsecutive) + // FIXME: This is invalid, but currently tolerated + [ "1.4E2E3", "1.4E2 E3", false ], + // Semicolon insertion between an expression having an inline // comment after it, and a statement on the next line (T29046). [ @@ -138,6 +152,7 @@ class JavaScriptMinifierTest extends PHPUnit_Framework_TestCase { [ "var a = 5.;", "var a=5.;" ], [ "5.0.toString();", "5.0.toString();" ], [ "5..toString();", "5..toString();" ], + // Cover failure case for too many decimal points [ "5...toString();", false ], [ "5.\n.toString();", '5..toString();' ], @@ -153,8 +168,9 @@ class JavaScriptMinifierTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provideCases * @covers JavaScriptMinifier::minify + * @covers JavaScriptMinifier::parseError */ - public function testJavaScriptMinifierOutput( $code, $expectedOutput, $expectedValid = true ) { + public function testMinifyOutput( $code, $expectedOutput, $expectedValid = true ) { $minified = JavaScriptMinifier::minify( $code ); // JSMin+'s parser will throw an exception if output is not valid JS. diff --git a/tests/phpunit/includes/libs/mime/MimeAnalyzerTest.php b/tests/phpunit/includes/libs/mime/MimeAnalyzerTest.php index 64f496049b..742eb95dc4 100644 --- a/tests/phpunit/includes/libs/mime/MimeAnalyzerTest.php +++ b/tests/phpunit/includes/libs/mime/MimeAnalyzerTest.php @@ -3,7 +3,7 @@ * @group Media * @covers MimeAnalyzer */ -class MimeMagicTest extends PHPUnit_Framework_TestCase { +class MimeAnalyzerTest extends PHPUnit_Framework_TestCase { /** @var MimeAnalyzer */ private $mimeAnalyzer; diff --git a/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php index f2fe07de40..4320d80249 100644 --- a/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php +++ b/tests/phpunit/includes/libs/objectcache/BagOStuffTest.php @@ -163,6 +163,9 @@ class BagOStuffTest extends MediaWikiTestCase { $this->assertTrue( $this->cache->add( $key, 'test' ) ); } + /** + * @covers BagOStuff::get + */ public function testGet() { $value = [ 'this' => 'is', 'a' => 'test' ]; diff --git a/tests/phpunit/includes/logging/BlockLogFormatterTest.php b/tests/phpunit/includes/logging/BlockLogFormatterTest.php index 4158ea2348..03671ac870 100644 --- a/tests/phpunit/includes/logging/BlockLogFormatterTest.php +++ b/tests/phpunit/includes/logging/BlockLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers BlockLogFormatter + */ class BlockLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/DeleteLogFormatterTest.php b/tests/phpunit/includes/logging/DeleteLogFormatterTest.php index 2337899976..0e6855d9a9 100644 --- a/tests/phpunit/includes/logging/DeleteLogFormatterTest.php +++ b/tests/phpunit/includes/logging/DeleteLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers DeleteLogFormatter + */ class DeleteLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/ImportLogFormatterTest.php b/tests/phpunit/includes/logging/ImportLogFormatterTest.php index ec12078064..80e4c0bc8a 100644 --- a/tests/phpunit/includes/logging/ImportLogFormatterTest.php +++ b/tests/phpunit/includes/logging/ImportLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers ImportLogFormatter + */ class ImportLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/MergeLogFormatterTest.php b/tests/phpunit/includes/logging/MergeLogFormatterTest.php index 8b9abe42c1..1978f1b52d 100644 --- a/tests/phpunit/includes/logging/MergeLogFormatterTest.php +++ b/tests/phpunit/includes/logging/MergeLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers MergeLogFormatter + */ class MergeLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/MoveLogFormatterTest.php b/tests/phpunit/includes/logging/MoveLogFormatterTest.php index 3433a6a433..ebda46b29d 100644 --- a/tests/phpunit/includes/logging/MoveLogFormatterTest.php +++ b/tests/phpunit/includes/logging/MoveLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers MoveLogFormatter + */ class MoveLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php b/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php index 333fd88da6..8317e91bb2 100644 --- a/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php +++ b/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php @@ -1,6 +1,7 @@ <?php /** + * @covers NewUsersLogFormatter * @group Database */ class NewUsersLogFormatterTest extends LogFormatterTestCase { diff --git a/tests/phpunit/includes/logging/PageLangLogFormatterTest.php b/tests/phpunit/includes/logging/PageLangLogFormatterTest.php index 2156bdb4ab..081901567e 100644 --- a/tests/phpunit/includes/logging/PageLangLogFormatterTest.php +++ b/tests/phpunit/includes/logging/PageLangLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers PageLangLogFormatter + */ class PageLangLogFormatterTest extends LogFormatterTestCase { protected function setUp() { diff --git a/tests/phpunit/includes/logging/PatrolLogFormatterTest.php b/tests/phpunit/includes/logging/PatrolLogFormatterTest.php index b680454354..0d78ed9cb9 100644 --- a/tests/phpunit/includes/logging/PatrolLogFormatterTest.php +++ b/tests/phpunit/includes/logging/PatrolLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers PatrolLogFormatter + */ class PatrolLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/ProtectLogFormatterTest.php b/tests/phpunit/includes/logging/ProtectLogFormatterTest.php index 1fa7fc2470..1c076cab04 100644 --- a/tests/phpunit/includes/logging/ProtectLogFormatterTest.php +++ b/tests/phpunit/includes/logging/ProtectLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers ProtectLogFormatter + */ class ProtectLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/RightsLogFormatterTest.php b/tests/phpunit/includes/logging/RightsLogFormatterTest.php index f48507d8ab..d081c61bb7 100644 --- a/tests/phpunit/includes/logging/RightsLogFormatterTest.php +++ b/tests/phpunit/includes/logging/RightsLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers RightsLogFormatter + */ class RightsLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/logging/UploadLogFormatterTest.php b/tests/phpunit/includes/logging/UploadLogFormatterTest.php index 00d93d148b..2b4067f17d 100644 --- a/tests/phpunit/includes/logging/UploadLogFormatterTest.php +++ b/tests/phpunit/includes/logging/UploadLogFormatterTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers UploadLogFormatter + */ class UploadLogFormatterTest extends LogFormatterTestCase { /** diff --git a/tests/phpunit/includes/media/GIFTest.php b/tests/phpunit/includes/media/GIFTest.php index aaa3ac4ded..7cd9caf52f 100644 --- a/tests/phpunit/includes/media/GIFTest.php +++ b/tests/phpunit/includes/media/GIFTest.php @@ -153,6 +153,7 @@ class GIFHandlerTest extends MediaWikiMediaTestCase { * @param string $filename * @param float $expectedLength * @dataProvider provideGetLength + * @covers GIFHandler::getLength */ public function testGetLength( $filename, $expectedLength ) { $file = $this->dataFile( $filename, 'image/gif' ); diff --git a/tests/phpunit/includes/media/PNGTest.php b/tests/phpunit/includes/media/PNGTest.php index 32d54df71e..4933f32567 100644 --- a/tests/phpunit/includes/media/PNGTest.php +++ b/tests/phpunit/includes/media/PNGTest.php @@ -142,6 +142,7 @@ class PNGHandlerTest extends MediaWikiMediaTestCase { * @param string $filename * @param float $expectedLength * @dataProvider provideGetLength + * @covers PNGHandler::getLength */ public function testGetLength( $filename, $expectedLength ) { $file = $this->dataFile( $filename, 'image/png' ); diff --git a/tests/phpunit/includes/media/SVGTest.php b/tests/phpunit/includes/media/SVGTest.php index 9fd640f56e..b68dd0ee0a 100644 --- a/tests/phpunit/includes/media/SVGTest.php +++ b/tests/phpunit/includes/media/SVGTest.php @@ -3,7 +3,7 @@ /** * @group Media */ -class SvgTest extends MediaWikiMediaTestCase { +class SVGTest extends MediaWikiMediaTestCase { /** * @var SvgHandler diff --git a/tests/phpunit/includes/media/WebPTest.php b/tests/phpunit/includes/media/WebPTest.php index b8dadaf487..ea06bbb4e0 100644 --- a/tests/phpunit/includes/media/WebPTest.php +++ b/tests/phpunit/includes/media/WebPTest.php @@ -1,4 +1,8 @@ <?php + +/** + * @covers WebPHandler + */ class WebPHandlerTest extends MediaWikiTestCase { public function setUp() { parent::setUp(); diff --git a/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php b/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php index 9cb2f9493b..7eb55820e5 100644 --- a/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php +++ b/tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php @@ -70,6 +70,7 @@ class MemcachedBagOStuffTest extends MediaWikiTestCase { /** * @dataProvider validKeyProvider + * @covers MemcachedBagOStuff::validateKeyEncoding */ public function testValidateKeyEncoding( $key ) { $this->assertSame( $key, $this->cache->validateKeyEncoding( $key ) ); @@ -86,6 +87,7 @@ class MemcachedBagOStuffTest extends MediaWikiTestCase { /** * @dataProvider invalidKeyProvider + * @covers MemcachedBagOStuff::validateKeyEncoding */ public function testValidateKeyEncodingThrowsException( $key ) { $this->setExpectedException( 'Exception' ); diff --git a/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php b/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php index f722fe13f8..4ef4aabb92 100644 --- a/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php +++ b/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php @@ -1,6 +1,8 @@ <?php /** * @group BagOStuff + * + * @covers RESTBagOStuff */ class RESTBagOStuffTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/page/ImagePage404Test.php b/tests/phpunit/includes/page/ImagePage404Test.php index 48c4392d05..4faace2160 100644 --- a/tests/phpunit/includes/page/ImagePage404Test.php +++ b/tests/phpunit/includes/page/ImagePage404Test.php @@ -28,6 +28,7 @@ class ImagePage404Test extends MediaWikiMediaTestCase { } /** + * @covers ImagePage::getThumbSizes * @dataProvider providerGetThumbSizes * @param string $filename * @param int $expectedNumberThumbs How many thumbnails to show diff --git a/tests/phpunit/includes/page/ImagePageTest.php b/tests/phpunit/includes/page/ImagePageTest.php index 2b30cfa5eb..8e49bf9865 100644 --- a/tests/phpunit/includes/page/ImagePageTest.php +++ b/tests/phpunit/includes/page/ImagePageTest.php @@ -21,6 +21,7 @@ class ImagePageTest extends MediaWikiMediaTestCase { } /** + * @covers ImagePage::getDisplayWidthHeight * @dataProvider providerGetDisplayWidthHeight * @param array $dim Array [maxWidth, maxHeight, width, height] * @param array $expected Array [width, height] The width and height we expect to display at @@ -65,6 +66,7 @@ class ImagePageTest extends MediaWikiMediaTestCase { } /** + * @covers ImagePage::getThumbSizes * @dataProvider providerGetThumbSizes * @param string $filename * @param int $expectedNumberThumbs How many thumbnails to show diff --git a/tests/phpunit/includes/page/WikiPageDbTestBase.php b/tests/phpunit/includes/page/WikiPageDbTestBase.php index b8480dd014..2b803aec15 100644 --- a/tests/phpunit/includes/page/WikiPageDbTestBase.php +++ b/tests/phpunit/includes/page/WikiPageDbTestBase.php @@ -1678,7 +1678,7 @@ more stuff public function testInsertOn_idSpecified() { $title = Title::newFromText( __METHOD__ ); $page = new WikiPage( $title ); - $id = 3478952189; + $id = 1478952189; $result = $page->insertOn( $this->db, $id ); diff --git a/tests/phpunit/includes/parser/ParserOptionsTest.php b/tests/phpunit/includes/parser/ParserOptionsTest.php index ad899bd7a9..93ab35c424 100644 --- a/tests/phpunit/includes/parser/ParserOptionsTest.php +++ b/tests/phpunit/includes/parser/ParserOptionsTest.php @@ -3,6 +3,9 @@ use Wikimedia\TestingAccessWrapper; use Wikimedia\ScopedCallback; +/** + * @covers ParserOptions + */ class ParserOptionsTest extends MediaWikiTestCase { private static function clearCache() { diff --git a/tests/phpunit/includes/parser/SanitizerTest.php b/tests/phpunit/includes/parser/SanitizerTest.php index d7e72e164b..2cf9553f94 100644 --- a/tests/phpunit/includes/parser/SanitizerTest.php +++ b/tests/phpunit/includes/parser/SanitizerTest.php @@ -406,6 +406,7 @@ class SanitizerTest extends MediaWikiTestCase { /** * @dataProvider provideIsReservedDataAttribute + * @covers Sanitizer::isReservedDataAttribute */ public function testIsReservedDataAttribute( $attr, $expected ) { $this->assertSame( $expected, Sanitizer::isReservedDataAttribute( $attr ) ); diff --git a/tests/phpunit/includes/parser/TagHooksTest.php b/tests/phpunit/includes/parser/TagHooksTest.php index 06fe272b29..7e31cba60f 100644 --- a/tests/phpunit/includes/parser/TagHooksTest.php +++ b/tests/phpunit/includes/parser/TagHooksTest.php @@ -28,7 +28,7 @@ * @covers PPNode_Hash_Array * @covers PPNode_Hash_Attr */ -class TagHookTest extends MediaWikiTestCase { +class TagHooksTest extends MediaWikiTestCase { public static function provideValidNames() { return [ [ 'foo' ], diff --git a/tests/phpunit/includes/poolcounter/PoolCounterTest.php b/tests/phpunit/includes/poolcounter/PoolCounterTest.php index d57ad04125..6caf3e5450 100644 --- a/tests/phpunit/includes/poolcounter/PoolCounterTest.php +++ b/tests/phpunit/includes/poolcounter/PoolCounterTest.php @@ -9,6 +9,9 @@ abstract class PoolCounterAbstractMock extends PoolCounter { } } +/** + * @covers PoolCounter + */ class PoolCounterTest extends MediaWikiTestCase { public function testConstruct() { $poolCounterConfig = [ diff --git a/tests/phpunit/includes/shell/CommandTest.php b/tests/phpunit/includes/shell/CommandTest.php index f7275e14e8..385dd500a6 100644 --- a/tests/phpunit/includes/shell/CommandTest.php +++ b/tests/phpunit/includes/shell/CommandTest.php @@ -4,6 +4,7 @@ use MediaWiki\Shell\Command; use Wikimedia\TestingAccessWrapper; /** + * @covers \MediaWiki\Shell\Command * @group Shell */ class CommandTest extends PHPUnit_Framework_TestCase { diff --git a/tests/phpunit/includes/shell/FirejailCommandTest.php b/tests/phpunit/includes/shell/FirejailCommandTest.php index c9db74f5f9..7d6d7f817d 100644 --- a/tests/phpunit/includes/shell/FirejailCommandTest.php +++ b/tests/phpunit/includes/shell/FirejailCommandTest.php @@ -29,38 +29,39 @@ class FirejailCommandTest extends PHPUnit_Framework_TestCase { // @codingStandardsIgnoreStart $env = "'MW_INCLUDE_STDERR=;MW_CPU_LIMIT=180; MW_CGROUP='\'''\''; MW_MEM_LIMIT=307200; MW_FILE_SIZE_LIMIT=102400; MW_WALL_CLOCK_LIMIT=180; MW_USE_LOG_PIPE=yes'"; // @codingStandardsIgnoreEnd - $limit = "$IP/includes/shell/limit.sh"; + $limit = "/bin/bash '$IP/includes/shell/limit.sh'"; $profile = "--profile=$IP/includes/shell/firejail.profile"; - $default = '--noroot --seccomp=@default --private-dev'; + $blacklist = '--blacklist=' . realpath( MW_CONFIG_FILE ); + $default = "$blacklist --noroot --seccomp=@default --private-dev"; return [ [ 'No restrictions', - 'ls', 0, "/bin/bash '$limit' ''\''ls'\''' $env" + 'ls', 0, "$limit ''\''ls'\''' $env" ], [ 'default restriction', 'ls', Shell::RESTRICT_DEFAULT, - "firejail --quiet $profile $default -- /bin/bash '$limit' ''\''ls'\''' $env" + "$limit 'firejail --quiet $profile $default -- '\''ls'\''' $env" ], [ 'no network', 'ls', Shell::NO_NETWORK, - "firejail --quiet $profile --net=none -- /bin/bash '$limit' ''\''ls'\''' $env" + "$limit 'firejail --quiet $profile --net=none -- '\''ls'\''' $env" ], [ 'default restriction & no network', 'ls', Shell::RESTRICT_DEFAULT | Shell::NO_NETWORK, - "firejail --quiet $profile $default --net=none -- /bin/bash '$limit' ''\''ls'\''' $env" + "$limit 'firejail --quiet $profile $default --net=none -- '\''ls'\''' $env" ], [ 'seccomp', 'ls', Shell::SECCOMP, - "firejail --quiet $profile --seccomp=@default -- /bin/bash '$limit' ''\''ls'\''' $env" + "$limit 'firejail --quiet $profile --seccomp=@default -- '\''ls'\''' $env" ], [ 'seccomp & no execve', 'ls', Shell::SECCOMP | Shell::NO_EXECVE, - "firejail --quiet $profile --seccomp=@default,execve -- /bin/bash '$limit' ''\''ls'\''' $env" + "$limit 'firejail --quiet $profile --shell=none --seccomp=@default,execve -- '\''ls'\''' $env" ], ]; } @@ -75,7 +76,7 @@ class FirejailCommandTest extends PHPUnit_Framework_TestCase { ->params( $params ) ->restrict( $flags ); $wrapper = TestingAccessWrapper::newFromObject( $command ); - $output = $wrapper->buildFinalCommand(); + $output = $wrapper->buildFinalCommand( $wrapper->command ); $this->assertEquals( $expected, $output[0], $desc ); } diff --git a/tests/phpunit/includes/shell/ShellTest.php b/tests/phpunit/includes/shell/ShellTest.php index 7c96c3c856..2f68dbd2a6 100644 --- a/tests/phpunit/includes/shell/ShellTest.php +++ b/tests/phpunit/includes/shell/ShellTest.php @@ -3,6 +3,7 @@ use MediaWiki\Shell\Shell; /** + * @covers \MediaWiki\Shell\Shell * @group Shell */ class ShellTest extends PHPUnit_Framework_TestCase { diff --git a/tests/phpunit/includes/specials/ContribsPagerTest.php b/tests/phpunit/includes/specials/ContribsPagerTest.php index 9366282fa8..1147805c01 100644 --- a/tests/phpunit/includes/specials/ContribsPagerTest.php +++ b/tests/phpunit/includes/specials/ContribsPagerTest.php @@ -18,6 +18,7 @@ class ContribsPagerTest extends MediaWikiTestCase { } /** + * @covers ContribsPager::processDateFilter * @dataProvider dateFilterOptionProcessingProvider * @param array $inputOpts Input options * @param array $expectedOpts Expected options diff --git a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php index ede2791745..a8459383f6 100644 --- a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php +++ b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php @@ -3,6 +3,9 @@ * @group Database */ +/** + * @covers MIMEsearchPage + */ class SpecialMIMESearchTest extends MediaWikiTestCase { /** @var MIMEsearchPage */ diff --git a/tests/phpunit/includes/specials/SpecialUncategorizedcategoriesTest.php b/tests/phpunit/includes/specials/SpecialUncategorizedcategoriesTest.php index 64e78f2828..2ecef79bf1 100644 --- a/tests/phpunit/includes/specials/SpecialUncategorizedcategoriesTest.php +++ b/tests/phpunit/includes/specials/SpecialUncategorizedcategoriesTest.php @@ -5,6 +5,7 @@ class UncategorizedCategoriesPageTest extends MediaWikiTestCase { /** * @dataProvider provideTestGetQueryInfoData + * @covers UncategorizedCategoriesPage::getQueryInfo */ public function testGetQueryInfo( $msgContent, $expected ) { $msg = new RawMessage( $msgContent ); diff --git a/tests/phpunit/includes/upload/UploadBaseTest.php b/tests/phpunit/includes/upload/UploadBaseTest.php index dd68cdcab7..bc7493d5a9 100644 --- a/tests/phpunit/includes/upload/UploadBaseTest.php +++ b/tests/phpunit/includes/upload/UploadBaseTest.php @@ -103,6 +103,8 @@ class UploadBaseTest extends MediaWikiTestCase { } /** + * @covers UploadBase::verifyUpload + * * test uploading a 100 bytes file with $wgMaxUploadSize = 100 * * This method should be abstracted so we can test different settings. @@ -126,6 +128,7 @@ class UploadBaseTest extends MediaWikiTestCase { } /** + * @covers UploadBase::checkSvgScriptCallback * @dataProvider provideCheckSvgScriptCallback */ public function testCheckSvgScriptCallback( $svg, $wellFormed, $filterMatch, $message ) { @@ -512,6 +515,7 @@ class UploadBaseTest extends MediaWikiTestCase { } /** + * @covers UploadBase::detectScriptInSvg * @dataProvider provideDetectScriptInSvg */ public function testDetectScriptInSvg( $svg, $expected, $message ) { @@ -552,6 +556,7 @@ class UploadBaseTest extends MediaWikiTestCase { } /** + * @covers UploadBase::checkXMLEncodingMissmatch * @dataProvider provideCheckXMLEncodingMissmatch */ public function testCheckXMLEncodingMissmatch( $fileContents, $evil ) { diff --git a/tests/phpunit/includes/user/PasswordResetTest.php b/tests/phpunit/includes/user/PasswordResetTest.php index 68b79591f6..1f578ab0c8 100644 --- a/tests/phpunit/includes/user/PasswordResetTest.php +++ b/tests/phpunit/includes/user/PasswordResetTest.php @@ -3,6 +3,7 @@ use MediaWiki\Auth\AuthManager; /** + * @covers PasswordReset * @group Database */ class PasswordResetTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/user/UserTest.php b/tests/phpunit/includes/user/UserTest.php index 2721c18e9f..f004e7913b 100644 --- a/tests/phpunit/includes/user/UserTest.php +++ b/tests/phpunit/includes/user/UserTest.php @@ -903,6 +903,9 @@ class UserTest extends MediaWikiTestCase { $block->delete(); } + /** + * @covers User::isPingLimitable + */ public function testIsPingLimitable() { $request = new FauxRequest(); $request->setIP( '1.2.3.4' ); @@ -939,6 +942,7 @@ class UserTest extends MediaWikiTestCase { } /** + * @covers User::getExperienceLevel * @dataProvider provideExperienceLevel */ public function testExperienceLevel( $editCount, $memberSince, $expLevel ) { @@ -968,6 +972,9 @@ class UserTest extends MediaWikiTestCase { $this->assertEquals( $expLevel, $user->getExperienceLevel() ); } + /** + * @covers User::getExperienceLevel + */ public function testExperienceLevelAnon() { $user = User::newFromName( '10.11.12.13', false ); diff --git a/tests/phpunit/includes/utils/BatchRowUpdateTest.php b/tests/phpunit/includes/utils/BatchRowUpdateTest.php index 017e97d357..d80a61c4fb 100644 --- a/tests/phpunit/includes/utils/BatchRowUpdateTest.php +++ b/tests/phpunit/includes/utils/BatchRowUpdateTest.php @@ -4,6 +4,10 @@ * Tests for BatchRowUpdate and its components * * @group db + * + * @covers BatchRowUpdate + * @covers BatchRowIterator + * @covers BatchRowWriter */ class BatchRowUpdateTest extends MediaWikiTestCase { diff --git a/tests/phpunit/includes/utils/MWCryptHKDFTest.php b/tests/phpunit/includes/utils/MWCryptHKDFTest.php index 86c19ae451..ac638c6a40 100644 --- a/tests/phpunit/includes/utils/MWCryptHKDFTest.php +++ b/tests/phpunit/includes/utils/MWCryptHKDFTest.php @@ -4,6 +4,10 @@ * @group HKDF */ +/** + * @covers CryptHKDF + * @covers MWCryptHKDF + */ class MWCryptHKDFTest extends MediaWikiTestCase { protected function setUp() { diff --git a/tests/phpunit/includes/watcheditem/WatchedItemIntegrationTest.php b/tests/phpunit/includes/watcheditem/WatchedItemIntegrationTest.php deleted file mode 100644 index 01e7ecb9d3..0000000000 --- a/tests/phpunit/includes/watcheditem/WatchedItemIntegrationTest.php +++ /dev/null @@ -1,145 +0,0 @@ -<?php -use MediaWiki\MediaWikiServices; - -/** - * @author Addshore - * - * @group Database - * - * @covers WatchedItem - */ -class WatchedItemIntegrationTest extends MediaWikiTestCase { - - public function setUp() { - parent::setUp(); - self::$users['WatchedItemIntegrationTestUser'] - = new TestUser( 'WatchedItemIntegrationTestUser' ); - - $this->hideDeprecated( 'WatchedItem::fromUserTitle' ); - $this->hideDeprecated( 'WatchedItem::addWatch' ); - $this->hideDeprecated( 'WatchedItem::removeWatch' ); - $this->hideDeprecated( 'WatchedItem::isWatched' ); - $this->hideDeprecated( 'WatchedItem::duplicateEntries' ); - $this->hideDeprecated( 'WatchedItem::batchAddWatch' ); - } - - private function getUser() { - return self::$users['WatchedItemIntegrationTestUser']->getUser(); - } - - public function testWatchAndUnWatchItem() { - $user = $this->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - // Cleanup after previous tests - WatchedItem::fromUserTitle( $user, $title )->removeWatch(); - - $this->assertFalse( - WatchedItem::fromUserTitle( $user, $title )->isWatched(), - 'Page should not initially be watched' - ); - WatchedItem::fromUserTitle( $user, $title )->addWatch(); - $this->assertTrue( - WatchedItem::fromUserTitle( $user, $title )->isWatched(), - 'Page should be watched' - ); - WatchedItem::fromUserTitle( $user, $title )->removeWatch(); - $this->assertFalse( - WatchedItem::fromUserTitle( $user, $title )->isWatched(), - 'Page should be unwatched' - ); - } - - public function testUpdateAndResetNotificationTimestamp() { - $user = $this->getUser(); - $otherUser = ( new TestUser( 'WatchedItemIntegrationTestUser_otherUser' ) )->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - WatchedItem::fromUserTitle( $user, $title )->addWatch(); - $this->assertNull( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() ); - - EmailNotification::updateWatchlistTimestamp( $otherUser, $title, '20150202010101' ); - $this->assertEquals( - '20150202010101', - WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() - ); - - MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp( - $user, $title - ); - $this->assertNull( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() ); - } - - public function testDuplicateAllAssociatedEntries() { - $user = $this->getUser(); - $titleOld = Title::newFromText( 'WatchedItemIntegrationTestPageOld' ); - $titleNew = Title::newFromText( 'WatchedItemIntegrationTestPageNew' ); - WatchedItem::fromUserTitle( $user, $titleOld->getSubjectPage() )->addWatch(); - WatchedItem::fromUserTitle( $user, $titleOld->getTalkPage() )->addWatch(); - // Cleanup after previous tests - WatchedItem::fromUserTitle( $user, $titleNew->getSubjectPage() )->removeWatch(); - WatchedItem::fromUserTitle( $user, $titleNew->getTalkPage() )->removeWatch(); - - WatchedItem::duplicateEntries( $titleOld, $titleNew ); - - $this->assertTrue( - WatchedItem::fromUserTitle( $user, $titleOld->getSubjectPage() )->isWatched() - ); - $this->assertTrue( - WatchedItem::fromUserTitle( $user, $titleOld->getTalkPage() )->isWatched() - ); - $this->assertTrue( - WatchedItem::fromUserTitle( $user, $titleNew->getSubjectPage() )->isWatched() - ); - $this->assertTrue( - WatchedItem::fromUserTitle( $user, $titleNew->getTalkPage() )->isWatched() - ); - } - - public function testIsWatched_falseOnNotAllowed() { - $user = $this->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - WatchedItem::fromUserTitle( $user, $title )->addWatch(); - - $this->assertTrue( WatchedItem::fromUserTitle( $user, $title )->isWatched() ); - $user->mRights = []; - $this->assertFalse( WatchedItem::fromUserTitle( $user, $title )->isWatched() ); - } - - public function testGetNotificationTimestamp_falseOnNotAllowed() { - $user = $this->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - WatchedItem::fromUserTitle( $user, $title )->addWatch(); - MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp( - $user, $title - ); - - $this->assertEquals( - null, - WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() - ); - $user->mRights = []; - $this->assertFalse( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() ); - } - - public function testRemoveWatch_falseOnNotAllowed() { - $user = $this->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - WatchedItem::fromUserTitle( $user, $title )->addWatch(); - - $previousRights = $user->mRights; - $user->mRights = []; - $this->assertFalse( WatchedItem::fromUserTitle( $user, $title )->removeWatch() ); - $user->mRights = $previousRights; - $this->assertTrue( WatchedItem::fromUserTitle( $user, $title )->removeWatch() ); - } - - public function testGetNotificationTimestamp_falseOnNotWatched() { - $user = $this->getUser(); - $title = Title::newFromText( 'WatchedItemIntegrationTestPage' ); - - WatchedItem::fromUserTitle( $user, $title )->removeWatch(); - $this->assertFalse( WatchedItem::fromUserTitle( $user, $title )->isWatched() ); - - $this->assertFalse( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() ); - } - -} diff --git a/tests/phpunit/includes/watcheditem/WatchedItemUnitTest.php b/tests/phpunit/includes/watcheditem/WatchedItemUnitTest.php deleted file mode 100644 index 8897645479..0000000000 --- a/tests/phpunit/includes/watcheditem/WatchedItemUnitTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php -use MediaWiki\Linker\LinkTarget; - -/** - * @author Addshore - * - * @covers WatchedItem - */ -class WatchedItemUnitTest extends MediaWikiTestCase { - - /** - * @param int $id - * - * @return PHPUnit_Framework_MockObject_MockObject|User - */ - private function getMockUser( $id ) { - $user = $this->createMock( User::class ); - $user->expects( $this->any() ) - ->method( 'getId' ) - ->will( $this->returnValue( $id ) ); - $user->expects( $this->any() ) - ->method( 'isAllowed' ) - ->will( $this->returnValue( true ) ); - return $user; - } - - public function provideUserTitleTimestamp() { - $user = $this->getMockUser( 111 ); - return [ - [ $user, Title::newFromText( 'SomeTitle' ), null ], - [ $user, Title::newFromText( 'SomeTitle' ), '20150101010101' ], - [ $user, new TitleValue( 0, 'TVTitle', 'frag' ), '20150101010101' ], - ]; - } - - /** - * @return PHPUnit_Framework_MockObject_MockObject|WatchedItemStore - */ - private function getMockWatchedItemStore() { - return $this->getMockBuilder( WatchedItemStore::class ) - ->disableOriginalConstructor() - ->getMock(); - } - - /** - * @dataProvider provideUserTitleTimestamp - */ - public function testConstruction( $user, LinkTarget $linkTarget, $notifTimestamp ) { - $item = new WatchedItem( $user, $linkTarget, $notifTimestamp ); - - $this->assertSame( $user, $item->getUser() ); - $this->assertSame( $linkTarget, $item->getLinkTarget() ); - $this->assertSame( $notifTimestamp, $item->getNotificationTimestamp() ); - - // The below tests the internal WatchedItem::getTitle method - $this->assertInstanceOf( 'Title', $item->getTitle() ); - $this->assertSame( $linkTarget->getDBkey(), $item->getTitle()->getDBkey() ); - $this->assertSame( $linkTarget->getFragment(), $item->getTitle()->getFragment() ); - $this->assertSame( $linkTarget->getNamespace(), $item->getTitle()->getNamespace() ); - $this->assertSame( $linkTarget->getText(), $item->getTitle()->getText() ); - } - - /** - * @dataProvider provideUserTitleTimestamp - */ - public function testFromUserTitle( $user, $linkTarget, $timestamp ) { - $store = $this->getMockWatchedItemStore(); - $store->expects( $this->once() ) - ->method( 'loadWatchedItem' ) - ->with( $user, $linkTarget ) - ->will( $this->returnValue( new WatchedItem( $user, $linkTarget, $timestamp ) ) ); - $this->setService( 'WatchedItemStore', $store ); - - $item = WatchedItem::fromUserTitle( $user, $linkTarget, User::IGNORE_USER_RIGHTS ); - - $this->assertEquals( $user, $item->getUser() ); - $this->assertEquals( $linkTarget, $item->getLinkTarget() ); - $this->assertEquals( $timestamp, $item->getNotificationTimestamp() ); - } - - public function testAddWatch() { - $title = Title::newFromText( 'SomeTitle' ); - $timestamp = null; - $checkRights = 0; - - /** @var User|PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock( User::class ); - $user->expects( $this->once() ) - ->method( 'addWatch' ) - ->with( $title, $checkRights ); - - $item = new WatchedItem( $user, $title, $timestamp, $checkRights ); - $this->assertTrue( $item->addWatch() ); - } - - public function testRemoveWatch() { - $title = Title::newFromText( 'SomeTitle' ); - $timestamp = null; - $checkRights = 0; - - /** @var User|PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock( User::class ); - $user->expects( $this->once() ) - ->method( 'removeWatch' ) - ->with( $title, $checkRights ); - - $item = new WatchedItem( $user, $title, $timestamp, $checkRights ); - $this->assertTrue( $item->removeWatch() ); - } - - public function provideBooleans() { - return [ - [ true ], - [ false ], - ]; - } - - /** - * @dataProvider provideBooleans - */ - public function testIsWatched( $returnValue ) { - $title = Title::newFromText( 'SomeTitle' ); - $timestamp = null; - $checkRights = 0; - - /** @var User|PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock( User::class ); - $user->expects( $this->once() ) - ->method( 'isWatched' ) - ->with( $title, $checkRights ) - ->will( $this->returnValue( $returnValue ) ); - - $item = new WatchedItem( $user, $title, $timestamp, $checkRights ); - $this->assertEquals( $returnValue, $item->isWatched() ); - } - - public function testDuplicateEntries() { - $oldTitle = Title::newFromText( 'OldTitle' ); - $newTitle = Title::newFromText( 'NewTitle' ); - - $store = $this->getMockWatchedItemStore(); - $store->expects( $this->once() ) - ->method( 'duplicateAllAssociatedEntries' ) - ->with( $oldTitle, $newTitle ); - $this->setService( 'WatchedItemStore', $store ); - - WatchedItem::duplicateEntries( $oldTitle, $newTitle ); - } - -} diff --git a/tests/phpunit/maintenance/BenchmarkerTest.php b/tests/phpunit/maintenance/BenchmarkerTest.php new file mode 100644 index 0000000000..69f98bdd00 --- /dev/null +++ b/tests/phpunit/maintenance/BenchmarkerTest.php @@ -0,0 +1,135 @@ +<?php + +use Wikimedia\TestingAccessWrapper; + +/** + * @covers Benchmarker + */ +class BenchmarkerTest extends PHPUnit_Framework_TestCase { + public function testBenchSimple() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 3; + + $count = 0; + $bench->bench( [ + 'test' => function () use ( &$count ) { + $count++; + } + ] ); + + $this->assertSame( 3, $count ); + } + + public function testBenchSetup() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 2; + + $buffer = []; + $bench->bench( [ + 'test' => [ + 'setup' => function () use ( &$buffer ) { + $buffer[] = 'setup'; + }, + 'function' => function () use ( &$buffer ) { + $buffer[] = 'run'; + } + ] + ] ); + + $this->assertSame( [ 'setup', 'run', 'run' ], $buffer ); + } + + public function testBenchVerbose() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output', 'hasOption', 'verboseRun' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 1; + + $bench->expects( $this->exactly( 2 ) )->method( 'hasOption' ) + ->will( $this->returnValueMap( [ + [ 'verbose', true ], + [ 'count', false ], + ] ) ); + + $bench->expects( $this->once() )->method( 'verboseRun' ) + ->with( 0 ) + ->willReturn( null ); + + $bench->bench( [ + 'test' => function () { + } + ] ); + } + + public function noop() { + } + + public function testBenchName_method() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output', 'addResult' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 1; + + $bench->expects( $this->once() )->method( 'addResult' ) + ->with( $this->callback( function ( $res ) { + return isset( $res['name'] ) && $res['name'] === __CLASS__ . '::noop()'; + } ) ); + + $bench->bench( [ + [ 'function' => [ $this, 'noop' ] ] + ] ); + } + + public function testBenchName_string() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output', 'addResult' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 1; + + $bench->expects( $this->once() )->method( 'addResult' ) + ->with( $this->callback( function ( $res ) { + return 'strtolower(A)'; + } ) ); + + $bench->bench( [ [ + 'function' => 'strtolower', + 'args' => [ 'A' ], + ] ] ); + } + + /** + * @covers Benchmarker::verboseRun + */ + public function testVerboseRun() { + $bench = $this->getMockBuilder( Benchmarker::class ) + ->setMethods( [ 'execute', 'output', 'hasOption', 'startBench', 'addResult' ] ) + ->getMock(); + $benchProxy = TestingAccessWrapper::newFromObject( $bench ); + $benchProxy->defaultCount = 1; + + $bench->expects( $this->exactly( 2 ) )->method( 'hasOption' ) + ->will( $this->returnValueMap( [ + [ 'verbose', true ], + [ 'count', false ], + ] ) ); + + $bench->expects( $this->once() )->method( 'output' ) + ->with( $this->callback( function ( $out ) { + return preg_match( '/memory.+ peak/', $out ) === 1; + } ) ); + + $bench->bench( [ + 'test' => function () { + } + ] ); + } +} diff --git a/tests/phpunit/maintenance/categoriesRdfTest.php b/tests/phpunit/maintenance/categoriesRdfTest.php index ec2746e8ee..b51c14c495 100644 --- a/tests/phpunit/maintenance/categoriesRdfTest.php +++ b/tests/phpunit/maintenance/categoriesRdfTest.php @@ -1,5 +1,9 @@ <?php +/** + * @covers CategoriesRdf + * @covers DumpCategoriesAsRdf + */ class CategoriesRdfTest extends MediaWikiLangTestCase { public function getCategoryIterator() { return [ diff --git a/tests/phpunit/structure/AutoLoaderTest.php b/tests/phpunit/structure/AutoLoaderTest.php index d81e8c663d..d45a58c5d3 100644 --- a/tests/phpunit/structure/AutoLoaderTest.php +++ b/tests/phpunit/structure/AutoLoaderTest.php @@ -161,6 +161,7 @@ class AutoLoaderTest extends MediaWikiTestCase { $path = realpath( __DIR__ . '/../../..' ); $oldAutoload = file_get_contents( $path . '/autoload.php' ); $generator = new AutoloadGenerator( $path, 'local' ); + $generator->setExcludePaths( array_values( AutoLoader::getAutoloadNamespaces() ) ); $generator->initMediaWikiDefault(); $newAutoload = $generator->getAutoload( 'maintenance/generateLocalAutoload.php' ); diff --git a/tests/phpunit/structure/ResourcesTest.php b/tests/phpunit/structure/ResourcesTest.php index d31779d0ba..62ddacebfe 100644 --- a/tests/phpunit/structure/ResourcesTest.php +++ b/tests/phpunit/structure/ResourcesTest.php @@ -55,7 +55,7 @@ class ResourcesTest extends MediaWikiTestCase { public function testIllegalDependencies() { $data = self::getAllModules(); - $illegalDeps = ResourceLoaderStartupModule::getStartupModules(); + $illegalDeps = ResourceLoaderStartUpModule::getStartupModules(); foreach ( $data['modules'] as $moduleName => $module ) { if ( $module->isRaw() ) { $illegalDeps[] = $moduleName; diff --git a/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js b/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js index 674bf07295..fbd159cbb1 100644 --- a/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js +++ b/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js @@ -285,16 +285,41 @@ output: 'http://host/wiki/Special:RecentChangesLinked?target=Moai', message: 'Target as subpage in path' }, + { + input: 'http://host/wiki/Special:RecentChangesLinked/Château', + output: 'http://host/wiki/Special:RecentChangesLinked?target=Château', + message: 'Target as subpage in path with special characters' + }, + { + input: 'http://host/wiki/Special:RecentChangesLinked/Moai/Sub1', + output: 'http://host/wiki/Special:RecentChangesLinked?target=Moai/Sub1', + message: 'Target as subpage also has a subpage' + }, { input: 'http://host/wiki/Special:RecentChangesLinked/Category:Foo', output: 'http://host/wiki/Special:RecentChangesLinked?target=Category:Foo', message: 'Target as subpage in path (with namespace)' }, + { + input: 'http://host/wiki/Special:RecentChangesLinked/Category:Foo/Bar', + output: 'http://host/wiki/Special:RecentChangesLinked?target=Category:Foo/Bar', + message: 'Target as subpage in path also has a subpage (with namespace)' + }, { input: 'http://host/w/index.php?title=Special:RecentChangesLinked/Moai', output: 'http://host/w/index.php?title=Special:RecentChangesLinked&target=Moai', message: 'Target as subpage in title param' }, + { + input: 'http://host/w/index.php?title=Special:RecentChangesLinked/Moai/Sub1', + output: 'http://host/w/index.php?title=Special:RecentChangesLinked&target=Moai/Sub1', + message: 'Target as subpage in title param also has a subpage' + }, + { + input: 'http://host/w/index.php?title=Special:RecentChangesLinked/Category:Foo/Bar', + output: 'http://host/w/index.php?title=Special:RecentChangesLinked&target=Category:Foo/Bar', + message: 'Target as subpage in title param also has a subpage (with namespace)' + }, { input: 'http://host/wiki/Special:Watchlist', output: 'http://host/wiki/Special:Watchlist', diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js index b20b68f5d6..6a1b83cf12 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js @@ -12,7 +12,7 @@ assert.strictEqual( $( '.toc' ).length, 0, 'There is no table of contents on the page at the beginning' ); tocHtml = '<div id="toc" class="toc">' + - '<div class="toctitle">' + + '<div class="toctitle" lang="en" dir="ltr">' + '<h2>Contents</h2>' + '</div>' + '<ul><li></li></ul>' + diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js index bb27626575..1730575914 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js @@ -246,7 +246,7 @@ assert.equal( href, '/wiki/#Fragment', 'empty title with fragment' ); href = util.getUrl( '#Fragment', { action: 'edit' } ); - assert.equal( href, '/w/index.php?action=edit#Fragment', 'epmty title with query string and fragment' ); + assert.equal( href, '/w/index.php?action=edit#Fragment', 'empty title with query string and fragment' ); href = util.getUrl( 'Foo:Sandbox \xC4#Fragment \xC4', { action: 'edit' } ); assert.equal( href, '/w/index.php?title=Foo:Sandbox_%C3%84&action=edit#Fragment_.C3.84', 'title with query string, fragment, and special characters' ); diff --git a/tests/selenium/wdio.conf.jenkins.js b/tests/selenium/wdio.conf.jenkins.js index 59e1878a55..26881ebbbf 100644 --- a/tests/selenium/wdio.conf.jenkins.js +++ b/tests/selenium/wdio.conf.jenkins.js @@ -1,5 +1,3 @@ -/* eslint no-undef: "error" */ -/* eslint-env node */ 'use strict'; const merge = require( 'deepmerge' ), password = 'testpass', diff --git a/tests/selenium/wdio.conf.js b/tests/selenium/wdio.conf.js index cf9da0c1ce..5eba0e0b9c 100644 --- a/tests/selenium/wdio.conf.js +++ b/tests/selenium/wdio.conf.js @@ -1,6 +1,3 @@ -/* eslint-env node */ -/* eslint no-undef: "error" */ -/* eslint-disable no-console, comma-dangle */ 'use strict'; const password = 'vagrant', @@ -51,7 +48,7 @@ exports.config = { relPath( './tests/selenium/specs/**/*.js' ), relPath( './extensions/*/tests/selenium/specs/**/*.js' ), relPath( './extensions/VisualEditor/modules/ve-mw/tests/selenium/specs/**/*.js' ), - relPath( './skins/*/tests/selenium/specs/**/*.js' ), + relPath( './skins/*/tests/selenium/specs/**/*.js' ) ], // Patterns to exclude. exclude: [ @@ -233,7 +230,7 @@ exports.config = { // save screenshot browser.saveScreenshot( filePath ); console.log( '\n\tScreenshot location:', filePath, '\n' ); - }, + } // // Hook that gets executed after the suite has ended // afterSuite: function (suite) {