From: jenkins-bot Date: Fri, 2 Feb 2018 04:00:27 +0000 (+0000) Subject: Merge "rdbms: Bump TransactionProfiler log entries to WARNING" X-Git-Tag: 1.31.0-rc.0~731 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=b6af56e2cce9fcd42dda71ade7a77b173d110865;hp=f6c9b7a7b74dfe3582a1b39777b85b68b9a9ab8d Merge "rdbms: Bump TransactionProfiler log entries to WARNING" --- diff --git a/RELEASE-NOTES-1.31 b/RELEASE-NOTES-1.31 index 264113e18a..ad24852e73 100644 --- a/RELEASE-NOTES-1.31 +++ b/RELEASE-NOTES-1.31 @@ -50,7 +50,7 @@ production. ==== Upgraded external libraries ==== * Updated jquery.chosen from v0.9.14 to v1.8.2. * Updated composer/spdx-licenses from 1.1.4 to - 1.2.0 (development dependency). + 1.3.0 (development dependency). * Updated nikic/php-parser from 2.1.0 to 3.1.3 (development dependency). * Updated wikimedia/ip-set from 1.1.0 to 1.2.0. @@ -190,6 +190,12 @@ changes to languages because of Phabricator reports. * PreparedEdit->newText * PreparedEdit->oldText * PreparedEdit->pst +* QuickTemplate::setRef() was deprecated in favour of QuickTemplate::set(). + Setting template variables by reference allowed violating the principle of data being + immutable once added to the skin template. In practice, this method was not being + used for that. Rather, setRef() existed as memory optimisation for PHP 4. +* Passing false to ParserOptions::setWrapOutputClass() is deprecated. Use the + 'unwrap' transform to ParserOutput::getText() instead. == Compatibility == MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported, diff --git a/composer.json b/composer.json index 4596c4c05b..1730942f49 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "zordius/lightncandy": "0.23" }, "require-dev": { - "composer/spdx-licenses": "1.2.0", + "composer/spdx-licenses": "1.3.0", "hamcrest/hamcrest-php": "^2.0", "jakub-onderka/php-parallel-lint": "0.9.2", "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a", diff --git a/docs/extension.schema.v2.json b/docs/extension.schema.v2.json index 51f9417d30..e13129bb56 100644 --- a/docs/extension.schema.v2.json +++ b/docs/extension.schema.v2.json @@ -669,7 +669,7 @@ }, "SkinOOUIThemes": { "type": "object", - "description": "Map of skin names to OOjs UI themes to use. Same format as ResourceLoaderOOUIModule::$builtinSkinThemeMap." + "description": "Map of skin names to OOUI themes to use. Same format as ResourceLoaderOOUIModule::$builtinSkinThemeMap." }, "PasswordPolicy": { "type": "object", diff --git a/includes/Message.php b/includes/Message.php index e55eaaf646..fac9a59893 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -1245,7 +1245,14 @@ class Message implements MessageSpecifier, Serializable { ); return $out instanceof ParserOutput - ? $out->getText( [ 'enableSectionEditLinks' => false ] ) + ? $out->getText( [ + 'enableSectionEditLinks' => false, + // Wrapping messages in an extra
is probably not expected. If + // they're outside the content area they probably shouldn't be + // targeted by CSS that's targeting the parser output, and if + // they're inside they already are from the outer div. + 'unwrap' => true, + ] ) : $out; } diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index cf1fd1ede9..2839ab9453 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -344,6 +344,7 @@ class ApiParse extends ApiBase { $result_array['text'] = $p_result->getText( [ 'allowTOC' => !$params['disabletoc'], 'enableSectionEditLinks' => !$params['disableeditsection'], + 'unwrap' => $params['wrapoutputclass'] === '', ] ); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text'; } @@ -538,9 +539,9 @@ class ApiParse extends ApiBase { if ( $params['disabletidy'] ) { $popts->setTidy( false ); } - $popts->setWrapOutputClass( - $params['wrapoutputclass'] === '' ? false : $params['wrapoutputclass'] - ); + if ( $params['wrapoutputclass'] !== '' ) { + $popts->setWrapOutputClass( $params['wrapoutputclass'] ); + } $reset = null; $suppressCache = false; diff --git a/includes/api/i18n/hu.json b/includes/api/i18n/hu.json index 02915a743a..7c03097460 100644 --- a/includes/api/i18n/hu.json +++ b/includes/api/i18n/hu.json @@ -10,7 +10,7 @@ "Dj" ] }, - "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentáció]]\n* [[mw:Special:MyLanguage/API:FAQ|GYIK]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Levelezőlista]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-bejelentések]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Hibabejelentések és kérések]\n
\nStátusz: Minden ezen a lapon látható funkciónak működnie kell, de az API jelenleg is aktív fejlesztés alatt áll, és bármikor változhat. Iratkozz fel a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce levelezőlistára] a frissítések követéséhez.\n\nHibás kérések: Ha az API hibás kérést kap, egy HTTP-fejlécet küld vissza „MediaWiki-API-Error” kulccsal, és a fejléc értéke és a visszaküldött hibakód ugyanarra az értékre lesz állítva. További információért lásd: [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Hibák és figyelmeztetések]].\n\nTesztelés: Az API-kérések könnyebb teszteléséhez használható az [[Special:ApiSandbox|API-homokozó]].", + "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentáció]]\n* [[mw:Special:MyLanguage/API:FAQ|GYIK]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Levelezőlista]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-bejelentések]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Hibabejelentések és kérések]\n
\nStátusz: Minden ezen a lapon látható funkciónak működnie kell, de az API jelenleg is aktív fejlesztés alatt áll, és bármikor változhat. Iratkozz fel a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce levelezőlistára] a frissítések követéséhez.\n\nHibás kérések: Ha az API hibás kérést kap, egy HTTP-fejlécet küld vissza „MediaWiki-API-Error” kulccsal, és a fejléc értéke és a visszaküldött hibakód ugyanarra az értékre lesz állítva. További információért lásd: [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Hibák és figyelmeztetések]].\n\n

Tesztelés: Az API-kérések könnyebb teszteléséhez használható az [[Special:ApiSandbox|API-homokozó]].

", "apihelp-main-param-action": "Milyen műveletet hajtson végre.", "apihelp-main-param-format": "A kimenet formátuma.", "apihelp-main-param-smaxage": "Az s-maxage gyorsítótár-vezérlő HTTP-fejléc beállítása ennyi másodpercre. A hibák soha nincsenek gyorsítótárazva.", diff --git a/includes/api/i18n/ja.json b/includes/api/i18n/ja.json index 094c406164..f51b03fe0c 100644 --- a/includes/api/i18n/ja.json +++ b/includes/api/i18n/ja.json @@ -15,7 +15,7 @@ "Omotecho" ] }, - "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|説明文書]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n
\n状態: このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n誤ったリクエスト: 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\nテスト: API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。", + "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|説明文書]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n
\n状態: このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n誤ったリクエスト: 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n

テスト: API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。

", "apihelp-main-param-action": "実行する操作です。", "apihelp-main-param-format": "出力する形式です。", "apihelp-main-param-smaxage": "s-maxage HTTP キャッシュ コントロール ヘッダー に、この秒数を設定します。エラーがキャッシュされることはありません。", diff --git a/includes/cache/MessageCache.php b/includes/cache/MessageCache.php index c9615b1335..63c03af25b 100644 --- a/includes/cache/MessageCache.php +++ b/includes/cache/MessageCache.php @@ -193,7 +193,6 @@ class MessageCache { $po = ParserOptions::newFromAnon(); $po->setEditSection( false ); $po->setAllowUnsafeRawHtml( false ); - $po->setWrapOutputClass( false ); return $po; } @@ -203,11 +202,6 @@ class MessageCache { // from malicious sources. As a precaution, disable // the parser tag when parsing messages. $this->mParserOptions->setAllowUnsafeRawHtml( false ); - // Wrapping messages in an extra
is probably not expected. If - // they're outside the content area they probably shouldn't be - // targeted by CSS that's targeting the parser output, and if - // they're inside they already are from the outer div. - $this->mParserOptions->setWrapOutputClass( false ); } return $this->mParserOptions; diff --git a/includes/deferred/MWCallableUpdate.php b/includes/deferred/MWCallableUpdate.php index 5b822af492..9803b7a491 100644 --- a/includes/deferred/MWCallableUpdate.php +++ b/includes/deferred/MWCallableUpdate.php @@ -14,14 +14,18 @@ class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback { /** * @param callable $callback * @param string $fname Calling method - * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28) + * @param IDatabase|IDatabase[]|null $dbws Abort if any of the specified DB handles have + * a currently pending transaction which later gets rolled back [optional] (since 1.28) */ - public function __construct( callable $callback, $fname = 'unknown', IDatabase $dbw = null ) { + public function __construct( callable $callback, $fname = 'unknown', $dbws = [] ) { $this->callback = $callback; $this->fname = $fname; - if ( $dbw && $dbw->trxLevel() ) { - $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname ); + $dbws = is_array( $dbws ) ? $dbws : [ $dbws ]; + foreach ( $dbws as $dbw ) { + if ( $dbw && $dbw->trxLevel() ) { + $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname ); + } } } diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index 7e05be6675..e76bffcace 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -650,6 +650,12 @@ class DifferenceEngine extends ContextSource { } } + /** + * @param WikiPage $page + * @param Revision $rev + * + * @return ParserOutput|bool False if the revision was not found + */ protected function getParserOutput( WikiPage $page, Revision $rev ) { $parserOptions = $page->makeParserOptions( $this->getContext() ); diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index b4df68a4c9..6b32953449 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -132,6 +132,13 @@ class FileRepo { /** @var array callable|bool Override these in the base class */ protected $oldFileFactoryKey = false; + /** @var string URL of where to proxy thumb.php requests to. + * Example: http://127.0.0.1:8888/wiki/dev/thumb/ + */ + protected $thumbProxyUrl; + /** @var string Secret key to pass as an X-Swift-Secret header to the proxied thumb service */ + protected $thumbProxySecret; + /** * @param array|null $info * @throws MWException @@ -159,7 +166,7 @@ class FileRepo { $optionalSettings = [ 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription', 'thumbScriptUrl', 'pathDisclosureProtection', 'descriptionCacheExpiry', - 'scriptExtension', 'favicon' + 'scriptExtension', 'favicon', 'thumbProxyUrl', 'thumbProxySecret' ]; foreach ( $optionalSettings as $var ) { if ( isset( $info[$var] ) ) { @@ -611,6 +618,24 @@ class FileRepo { return $this->thumbScriptUrl; } + /** + * Get the URL thumb.php requests are being proxied to + * + * @return string + */ + public function getThumbProxyUrl() { + return $this->thumbProxyUrl; + } + + /** + * Get the secret key for the proxied thumb service + * + * @return string + */ + public function getThumbProxySecret() { + return $this->thumbProxySecret; + } + /** * Returns true if the repository can transform files via a 404 handler * diff --git a/includes/htmlform/HTMLFormElement.php b/includes/htmlform/HTMLFormElement.php index 66d6143329..2830b9c258 100644 --- a/includes/htmlform/HTMLFormElement.php +++ b/includes/htmlform/HTMLFormElement.php @@ -1,7 +1,7 @@ addClasses( [ 'mw-htmlform-hide-if' ] ); } if ( $this->modules ) { - // JS code must be able to read this before infusing (before OOjs UI is even loaded), + // JS code must be able to read this before infusing (before OOUI is even loaded), // so we put this in a separate attribute (not with the rest of the config). // And it's not needed anymore after infusing, so we don't put it in JS config at all. $this->setAttributes( [ 'data-mw-modules' => implode( ',', $this->modules ) ] ); diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index 9c301e6aa7..aab881129b 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -673,7 +673,7 @@ abstract class HTMLFormField { } /** - * Whether the field should be automatically infused. Note that all OOjs UI HTMLForm fields are + * Whether the field should be automatically infused. Note that all OOUI HTMLForm fields are * infusable (you can call OO.ui.infuse() on them), but not all are infused by default, since * there is no benefit in doing it e.g. for buttons and it's a small performance hit on page load. * @@ -686,7 +686,7 @@ abstract class HTMLFormField { /** * Get the list of extra ResourceLoader modules which must be loaded client-side before it's - * possible to infuse this field's OOjs UI widget. + * possible to infuse this field's OOUI widget. * * @return string[] */ diff --git a/includes/http/MWHttpRequest.php b/includes/http/MWHttpRequest.php index fff72ec09f..ac16032ca3 100644 --- a/includes/http/MWHttpRequest.php +++ b/includes/http/MWHttpRequest.php @@ -181,7 +181,7 @@ abstract class MWHttpRequest implements LoggerAwareInterface { * @return MWHttpRequest * @see MWHttpRequest::__construct */ - public static function factory( $url, $options = null, $caller = __METHOD__ ) { + public static function factory( $url, array $options = [], $caller = __METHOD__ ) { return \MediaWiki\MediaWikiServices::getInstance() ->getHttpRequestFactory() ->create( $url, $options, $caller ); diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php index 5e018e0559..e42146d51b 100644 --- a/includes/installer/Installer.php +++ b/includes/installer/Installer.php @@ -447,7 +447,6 @@ abstract class Installer { $this->parserTitle = Title::newFromText( 'Installer' ); $this->parserOptions = new ParserOptions( $wgUser ); // language will be wrong :( $this->parserOptions->setEditSection( false ); - $this->parserOptions->setWrapOutputClass( false ); // Don't try to access DB before user language is initialised $this->setParserLanguage( Language::factory( 'en' ) ); } @@ -689,6 +688,7 @@ abstract class Installer { $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart ); $html = $out->getText( [ 'enableSectionEditLinks' => false, + 'unwrap' => true, ] ); } catch ( MediaWiki\Services\ServiceDisabledException $e ) { $html = ' ' . htmlspecialchars( $text ); diff --git a/includes/installer/i18n/uk.json b/includes/installer/i18n/uk.json index 54339c3b3d..a5b630a3fd 100644 --- a/includes/installer/i18n/uk.json +++ b/includes/installer/i18n/uk.json @@ -12,7 +12,8 @@ "아라", "Amire80", "Piramidion", - "Macofe" + "Macofe", + "Movses" ] }, "config-desc": "Інсталятор MediaWiki", @@ -314,6 +315,7 @@ "config-install-mainpage-failed": "Не вдається вставити головну сторінку: $1", "config-install-done": "Вітаємо!\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл LocalSettings.php, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\nПримітка: Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете [$2 увійти у свою вікі].", "config-install-done-path": "Вітаємо!\nВи встановили Медіавікі.\n\nІнсталятор створив файл LocalSettings.php.\nУ ньому містяться всі Ваші налаштування.\n\nВам потрібно завантажити його й помістити в $4. Завантаження повинно було автоматично розпочатись.\n\nЯкщо завантаження не було запропоновано, або Ви його скасували, Ви можете перезапустити завантаження натиснувши на посилання нижче:\n\n$3\n\nЗверніть увагу: Якщо Ви не зробите це зараз, цей згенерований файл налаштувань не буде доступним для Вас пізніше якщо Ви вийдете зі встановлення не завантаживши його.\n\nКоли це було зроблено Ви можете [$2 зайти до своєї вікі].", + "config-install-success": "Mediawiki успішно встановлено. Зараз ви можете перейти до <$1$2>, щоб переглянути свою вікі. Якщо у вас є питання, ознайомтеся з нашим FAQ: або використовуйте один з форумів підтримки, які вказано на цій сторінці.", "config-download-localsettings": "Завантажити LocalSettings.php", "config-help": "допомога", "config-help-tooltip": "натисніть, щоб розгорнути", diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php index 08424c9c8a..d9398196f5 100644 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -47,17 +47,23 @@ use Psr\Log\NullLogger; * There are three supported ways to handle broadcasted operations: * - a) Configure the 'purge' EventRelayer to point to a valid PubSub endpoint * that has subscribed listeners on the cache servers applying the cache updates. - * - b) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer) - * and set up mcrouter as the underlying cache backend, using one of the memcached - * BagOStuff classes as 'cache'. Use OperationSelectorRoute in the mcrouter settings - * to configure 'set' and 'delete' operations to go to all DCs via AllAsyncRoute and - * configure other operations to go to the local DC via PoolRoute (for reference, - * see https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles). - * - c) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer) - * and set up dynomite as cache middleware between the web servers and either - * memcached or redis. This will also broadcast all key setting operations, not just purges, - * which can be useful for cache warming. Writes are eventually consistent via the - * Dynamo replication model (see https://github.com/Netflix/dynomite). + * - b) Ommit the 'purge' EventRelayer parameter and set up mcrouter as the underlying cache + * backend, using a memcached BagOStuff class for the 'cache' parameter. The 'region' + * and 'cluster' parameters must be provided and 'mcrouterAware' must be set to 'true'. + * Configure mcrouter as follows: + * - 1) Use Route Prefixing based on region (datacenter) and cache cluster. + * See https://github.com/facebook/mcrouter/wiki/Routing-Prefix and + * https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup + * - 2) To increase the consistency of delete() and touchCheckKey() during cache + * server membership changes, you can use the OperationSelectorRoute to + * configure 'set' and 'delete' operations to go to all servers in the cache + * cluster, instead of just one server determined by hashing. + * See https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles + * - c) Ommit the 'purge' EventRelayer parameter and set up dynomite as cache middleware + * between the web servers and either memcached or redis. This will also broadcast all + * key setting operations, not just purges, which can be useful for cache warming. + * Writes are eventually consistent via the Dynamo replication model. + * See https://github.com/Netflix/dynomite * * Broadcasted operations like delete() and touchCheckKey() are done asynchronously * in all datacenters this way, though the local one should likely be near immediate. @@ -87,6 +93,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { protected $purgeChannel; /** @var EventRelayer Bus that handles purge broadcasts */ protected $purgeRelayer; + /** @bar bool Whether to use mcrouter key prefixing for routing */ + protected $mcrouterAware; + /** @var string Physical region for mcrouter use */ + protected $region; + /** @var string Cache cluster name for mcrouter use */ + protected $cluster; /** @var LoggerInterface */ protected $logger; /** @var StatsdDataFactoryInterface */ @@ -200,6 +212,16 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * callback supplied by the getWithSetCallback() caller. The result will be saved * as normal. The handler is expected to call the WAN cache callback at an opportune * time (e.g. HTTP post-send), though generally within a few 100ms. [optional] + * - region: the current physical region. This is required when using mcrouter as the + * backing store proxy. [optional] + * - cluster: name of the cache cluster used by this WAN cache. The name must be the + * same in all datacenters; the ("region","cluster") tuple is what distinguishes + * the counterpart cache clusters among all the datacenter. The contents of + * https://github.com/facebook/mcrouter/wiki/Config-Files give background on this. + * This is required when using mcrouter as the backing store proxy. [optional] + * - mcrouterAware: set as true if mcrouter is the backing store proxy and mcrouter + * is configured to interpret /// key prefixes as routes. This + * requires that "region" and "cluster" are both set above. [optional] */ public function __construct( array $params ) { $this->cache = $params['cache']; @@ -209,6 +231,10 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { $this->purgeRelayer = isset( $params['relayers']['purge'] ) ? $params['relayers']['purge'] : new EventRelayerNull( [] ); + $this->region = isset( $params['region'] ) ? $params['region'] : 'main'; + $this->cluster = isset( $params['cluster'] ) ? $params['cluster'] : 'wan-main'; + $this->mcrouterAware = !empty( $params['mcrouterAware'] ); + $this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() ); $this->stats = isset( $params['stats'] ) ? $params['stats'] : new NullStatsdDataFactory(); $this->asyncHandler = isset( $params['asyncHandler'] ) ? $params['asyncHandler'] : null; @@ -1779,9 +1805,18 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * @return bool Success */ protected function relayPurge( $key, $ttl, $holdoff ) { - if ( $this->purgeRelayer instanceof EventRelayerNull ) { + if ( $this->mcrouterAware ) { + // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup + // Wildcards select all matching routes, e.g. the WAN cluster on all DCs + $ok = $this->cache->set( + "/*/{$this->cluster}/{$key}", + $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ), + $ttl + ); + } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) { // This handles the mcrouter and the single-DC case - $ok = $this->cache->set( $key, + $ok = $this->cache->set( + $key, $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ), $ttl ); @@ -1810,8 +1845,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * @return bool Success */ protected function relayDelete( $key ) { - if ( $this->purgeRelayer instanceof EventRelayerNull ) { - // This handles the mcrouter and the single-DC case + if ( $this->mcrouterAware ) { + // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup + // Wildcards select all matching routes, e.g. the WAN cluster on all DCs + $ok = $this->cache->delete( "/*/{$this->cluster}/{$key}" ); + } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) { + // Some other proxy handles broadcasting or there is only one datacenter $ok = $this->cache->delete( $key ); } else { $event = $this->cache->modifySimpleRelayEvent( [ diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php index 2f284afbec..1405c451e3 100644 --- a/includes/parser/ParserOptions.php +++ b/includes/parser/ParserOptions.php @@ -781,6 +781,7 @@ class ParserOptions { * CSS class to use to wrap output from Parser::parse() * @since 1.30 * @param string|bool $className Set false to disable wrapping. + * Passing false is deprecated since MediaWiki 1.31 * @return string|bool Current value */ public function setWrapOutputClass( $className ) { diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index 153a7708f4..e2efaff40f 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -23,10 +23,11 @@ */ class ParserOutput extends CacheTime { /** - * Feature flag to indicate to extensions that MediaWiki core supports and + * Feature flags to indicate to extensions that MediaWiki core supports and * uses getText() stateless transforms. */ const SUPPORTS_STATELESS_TRANSFORMS = 1; + const SUPPORTS_UNWRAP_TRANSFORM = 1; /** * @var string $mText The output text @@ -266,29 +267,47 @@ class ParserOutput extends CacheTime { * to generate one and `__NOTOC__` wasn't used. Default is true, * but might be statefully overridden. * - enableSectionEditLinks: (bool) Include section edit links, assuming - * section edit link tokens are present in the HTML. Default is true, + * section edit link tokens are present in the HTML. Default is true, * but might be statefully overridden. + * - unwrap: (bool) Remove a wrapping mw-parser-output div. Default is false. * @return string HTML */ public function getText( $options = [] ) { - // @todo Warn if !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled ) + if ( !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled ) ) { + wfDeprecated( 'ParserOutput stateful allowTOC', '1.31' ); + } - // @todo Warn if !array_key_exists( 'enableSectionEditLinks', $options ) - // && !$this->mEditSectionTokens // Note that while $this->mEditSectionTokens formerly defaulted to false, // ParserOptions->getEditSection() defaults to true and Parser copies // that to us so true makes more sense as the stateless default. + if ( !array_key_exists( 'enableSectionEditLinks', $options ) && !$this->mEditSectionTokens ) { + wfDeprecated( 'ParserOutput stateful enableSectionEditLinks', '1.31' ); + } $options += [ // empty() here because old cached versions might lack the field somehow. // In that situation, the historical behavior (possibly buggy) is to remove the TOC. 'allowTOC' => !empty( $this->mTOCEnabled ), 'enableSectionEditLinks' => $this->mEditSectionTokens, + 'unwrap' => false, ]; $text = $this->mText; Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] ); + if ( $options['unwrap'] !== false ) { + $start = Html::openElement( 'div', [ + 'class' => 'mw-parser-output' + ] ); + $startLen = strlen( $start ); + $end = Html::closeElement( 'div' ); + $endLen = strlen( $end ); + + if ( substr( $text, 0, $startLen ) === $start && substr( $text, -$endLen ) === $end ) { + $text = substr( $text, $startLen, -$endLen ); + } + } + if ( $options['enableSectionEditLinks'] ) { $text = preg_replace_callback( self::EDITSECTION_REGEX, diff --git a/includes/skins/QuickTemplate.php b/includes/skins/QuickTemplate.php index d1be4bb0df..19b41ba969 100644 --- a/includes/skins/QuickTemplate.php +++ b/includes/skins/QuickTemplate.php @@ -91,10 +91,14 @@ abstract class QuickTemplate { } /** + * @deprecated since 1.31 This function is a now-redundant optimisation intended + * for very old versions of PHP. The use of references here makes the code + * more fragile and is incompatible with plans like T140664. Use set() instead. * @param string $name * @param mixed &$value */ public function setRef( $name, &$value ) { + wfDeprecated( __METHOD__, '1.31' ); $this->data[$name] =& $value; } diff --git a/languages/i18n/be-tarask.json b/languages/i18n/be-tarask.json index fbc1dff330..6f0bd53c31 100644 --- a/languages/i18n/be-tarask.json +++ b/languages/i18n/be-tarask.json @@ -1123,7 +1123,7 @@ "right-autocreateaccount": "Аўтаматычны ўваход з вонкавага рахунку ўдзельніка", "right-minoredit": "Пазначэньне рэдагаваньняў як дробных", "right-move": "Перанос старонак", - "right-move-subpages": "перанос старонак разам зь іх падстаронкамі", + "right-move-subpages": "Перанос старонак разам зь іх падстаронкамі", "right-move-rootuserpages": "перанос карэнных старонак удзельнікаў", "right-move-categorypages": "перанос старонак катэгорыяў", "right-movefile": "перайменаваньне файлаў", diff --git a/languages/i18n/bs.json b/languages/i18n/bs.json index 1780f90411..27151181f4 100644 --- a/languages/i18n/bs.json +++ b/languages/i18n/bs.json @@ -151,9 +151,9 @@ "category-empty": "Ova kategorija trenutno ne sadrži članke ni medije.", "hidden-categories": "{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}}", "hidden-category-category": "Skrivene kategorije", - "category-subcat-count": "{{PLURAL:$2|1=Ova kategorija samo ima sljedeću potkategoriju.|Ova kategorija ima {{PLURAL:$1|sljedeću potkategoriju|sljedeće $1 potkategorije|sljedećih $1 potkategorija}}, od $2 ukupno.}}", + "category-subcat-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću potkategoriju.|{{PLURAL:$1|Prikazana je $1 potkategorija|Prikazane su $1 potkategorije|Prikazano je $1 potkategorija}}, od ukupno $2.}}", "category-subcat-count-limited": "Ova kategorija sadrži {{PLURAL:$1|sljedeću $1 potkategoriju|sljedeće $1 potkategorije|sljedećih $1 potkategorija}}.", - "category-article-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću stranicu.|{{PLURAL:$1|Sljedeća stranica je|Sljedeće $1 stranice su|Sljedećih $1 stranica je}} u ovoj kategoriji, od ukupno $2.}}", + "category-article-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću stranicu.|{{PLURAL:$1|Prikazana je $1 stranica|Prikazane su $1 stranice|Prikazano je $1 stranica}}, od ukupno $2.}}", "category-article-count-limited": "{{PLURAL:$1|Slijedeća $1 stranica je|Slijedeće $1 stranice su|Slijedećih $1 stranica je}} u ovoj kategoriji.", "category-file-count": "{{PLURAL:$2|Ova kategorija ima slijedeću $1 datoteku.|{{PLURAL:$1|Prikazana je $1 datoteka|Prikazane su $1 datoteke|Prikazano je $1 datoteka}} u ovoj kategoriji, od ukupno $2.}}", "category-file-count-limited": "{{PLURAL:$1|Slijedeća $1 datoteka je|Slijedeće $1 datoteke su|Slijedećih $1 datoteka je}} u ovoj kategoriji.", diff --git a/languages/i18n/hu.json b/languages/i18n/hu.json index 58d3d17abe..082760f9f0 100644 --- a/languages/i18n/hu.json +++ b/languages/i18n/hu.json @@ -747,6 +747,7 @@ "postedit-confirmation-created": "Az oldal létrehozva.", "postedit-confirmation-restored": "Az oldal helyre lett állítva.", "postedit-confirmation-saved": "A szerkesztésedet elmentettük.", + "postedit-confirmation-published": "A szerkesztésed közzé lett téve.", "edit-already-exists": "Az új lap nem készíthető el.\nMár létezik.", "defaultmessagetext": "Alapértelmezett szöveg", "content-failed-to-parse": "Hiba történt a $2 tartalom $1 modellre történő konvertálása során: $3", @@ -1675,7 +1676,7 @@ "lockmanager-fail-closelock": "Nem sikerült a „$1” zárolási fájljának bezárása.", "lockmanager-fail-deletelock": "Nem sikerült a(z) „$1” zárolási fájljának törlése.", "lockmanager-fail-acquirelock": "Nem sikerült zárolást igényelni a „$1” fájlhoz.", - "lockmanager-fail-openlock": "Nem sikerült a „$1” zárolási fájljának megnyitása.", + "lockmanager-fail-openlock": "Nem sikerült a „$1” zárolási fájljának megnyitása. Győződj meg róla, hogy a feltöltési könyvtárad jól van konfigurálva és a webszerverednek van engedélye írni a könyvtárat. Lásd még további információért: https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory", "lockmanager-fail-releaselock": "Nem sikerült a(z) „$1” fájl zárolásának feloldása.", "lockmanager-fail-db-bucket": "Nem sikerült kapcsolatot létesíteni elég adatbázis zároláshoz a $1 vödörben.", "lockmanager-fail-db-release": "Nem lehet a $1 adatbázis zárolását feloldani.", diff --git a/languages/i18n/is.json b/languages/i18n/is.json index 340ab40168..8aeac3c540 100644 --- a/languages/i18n/is.json +++ b/languages/i18n/is.json @@ -958,6 +958,7 @@ "timezoneregion-indian": "Indlandshaf", "timezoneregion-pacific": "Kyrrahaf", "allowemail": "Leyfa öðrum notendum að senda mér tölvupóst", + "email-allow-new-users-label": "Leyfa tölvupóst frá nýskráðum notendum", "prefs-searchoptions": "Leit", "prefs-namespaces": "Nafnrými", "default": "sjálfgefið", @@ -1533,7 +1534,7 @@ "lockmanager-fail-closelock": "Gat ekki lokað lásaskrá vegna „$1“.", "lockmanager-fail-deletelock": "Gat ekki eytt lásaskrá vegna „$1“.", "lockmanager-fail-acquirelock": "Gat ekki nálgast lás vegna „$1“.", - "lockmanager-fail-openlock": "Gat ekki opnað lásaskrá vegna „$1“.", + "lockmanager-fail-openlock": "Gat ekki opnað lásaskrá vegna „$1“. Gakkt úr skugga um að innsendingamappan þín sé rétt uppsett, og að vefþjónninn þinn hafi heimildir til að skrifa í þá möppu. Skoðaðu https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory til að sjá nánari upplýsingar.", "lockmanager-fail-releaselock": "Gat ekki opnað lás vegna „$1“.", "lockmanager-fail-db-bucket": "Náði ekki sambandi við nógu marga lása í fötunni $1.", "lockmanager-fail-db-release": "Gat ekki opnað lása á gagnagrunninum $1.", @@ -1554,6 +1555,8 @@ "uploadstash-bad-path": "Slóðin er ekki til.", "uploadstash-bad-path-invalid": "Slóðin er ógild.", "uploadstash-bad-path-unknown-type": "Óþekkt gerð \"$1\".", + "uploadstash-bad-path-unrecognized-thumb-name": "Óþekkt heiti á smámynd.", + "uploadstash-file-not-found-no-thumb": "Gat ekki náð í smámynd.", "invalid-chunk-offset": "Ógild raðbreyting bunka", "img-auth-accessdenied": "Aðgangur óheimill", "img-auth-nopathinfo": "PATH_INFO vantar.\nBiðlarinn þínn er ekki stilltur til að gefa upp þessar upplýsingar.\nÞær mega vera CGI-byggðar og mega ekki styðja img_auth.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization", @@ -1705,7 +1708,7 @@ "doubleredirects": "Tvöfaldar tilvísanir", "doubleredirectstext": "Þessi síða er listi yfir skrár sem eru tilvísanir á aðrar tilvísanir.\nHver lína inniheldur tengla á fyrstu og aðra tilvísun auk þeirrar síðu sem seinni tilvísunin beinist að, sem er oftast sú síða sem allar tilvísanirnar eiga að benda á.\nYfirstrikaðar færslur hafa verið leiðréttar.", "double-redirect-fixed-move": "[[$1]] hefur verið færð.\nHún var uppfærð sjálfkrafa og tilvísar núna á [[$2]].", - "double-redirect-fixed-maintenance": "Laga sjálfvirkt tvöfalda tilvísun frá [[$1]] til [[$2]] í viðhalds aðgerð.", + "double-redirect-fixed-maintenance": "Laga sjálfvirkt tvöfalda endurbeiningu frá [[$1]] til [[$2]] í viðhaldsaðgerð", "double-redirect-fixer": "Laga tilvísun", "brokenredirects": "Rofnar endurbeiningar", "brokenredirectstext": "Eftirfarandi tilvísanir vísa á síður sem ekki eru til:", @@ -2679,6 +2682,7 @@ "newimages-hidepatrolled": "Fela yfirfarnar innsendingar", "newimages-mediatype": "Skrátegund:", "noimages": "Ekkert að sjá.", + "gallery-slideshow-toggle": "Víxla smámyndum af/á", "ilsubmit": "Leita", "bydate": "eftir dagsetningu", "sp-newimages-showfrom": "Leita af nýjum skráum frá $2, $1", diff --git a/languages/i18n/it.json b/languages/i18n/it.json index 26da7749c4..88a605bda8 100644 --- a/languages/i18n/it.json +++ b/languages/i18n/it.json @@ -140,7 +140,7 @@ "tog-enotifwatchlistpages": "Inviami una email quando viene modificata una pagina o un file presente tra gli osservati speciali", "tog-enotifusertalkpages": "Inviami una email quando viene modificata la mia pagina di discussione", "tog-enotifminoredits": "Inviami una email anche per le modifiche minori di pagine e file", - "tog-enotifrevealaddr": "Mostra il mio indirizzo nelle e-mail di notifica", + "tog-enotifrevealaddr": "Mostra il mio indirizzo nelle email di notifica", "tog-shownumberswatching": "Mostra il numero di utenti che hanno la pagina in osservazione", "tog-oldsig": "La tua firma attuale:", "tog-fancysig": "Gestisci la firma come wikitesto (senza collegamento automatico)", @@ -561,8 +561,8 @@ "mailmypassword": "Reimposta password", "passwordremindertitle": "Servizio Password Reminder di {{SITENAME}}", "passwordremindertext": "Qualcuno (probabilmente tu, con indirizzo IP $1) ha richiesto l'invio di una nuova password di accesso a {{SITENAME}} ($4).\nUna password temporanea per l'utente \"$2\" è stata impostata a \"$3\".\nÈ opportuno eseguire un accesso quanto prima e cambiare la password immediatamente. La password temporanea scadrà dopo {{PLURAL:$5|un giorno|$5 giorni}}.\n\nSe non sei stato tu a fare la richiesta, oppure hai ritrovato la password e non desideri più cambiarla, puoi ignorare questo messaggio e continuare a usare la vecchia password.", - "noemail": "Nessun indirizzo e-mail registrato per l'utente \"$1\".", - "noemailcreate": "È necessario fornire un indirizzo e-mail valido", + "noemail": "Nessun indirizzo email registrato per l'utente \"$1\".", + "noemailcreate": "È necessario fornire un indirizzo email valido", "passwordsent": "Una nuova password è stata inviata all'indirizzo e-mail registrato per l'utente \"$1\".\nPer favore, effettua un accesso non appena la ricevi.", "blocked-mailpassword": "Il tuo indirizzo IP è bloccato alla modifica. Per prevenire abusi, non è consentito usare la funzione di recupero password da questo indirizzo IP.", "eauthentsent": "Un messaggio email di conferma è stato spedito all'indirizzo indicato.\nPer abilitare l'invio di messaggi email per questo utente è necessario seguire le istruzioni che vi sono indicate, in modo da confermare che si è i legittimi proprietari dell'indirizzo.", @@ -571,7 +571,7 @@ "acct_creation_throttle_hit": "{{PLURAL:$1|1 registrazione è già stata effettuata|$1 registrazioni sono già state effettuate}} da qualcuno con il tuo stesso indirizzo IP negli ultimi $2, che è il massimo consentito in questo periodo di tempo.\nPerciò, gli utenti che usano questo indirizzo IP non possono più registrarsi per il momento.", "emailauthenticated": "L'indirizzo email è stato confermato il $2 alle $3.", "emailnotauthenticated": "L'indirizzo di posta elettronica non è stato ancora confermato.\nNon verranno inviati messaggi email per le funzioni elencate di seguito.", - "noemailprefs": "Indicare un indirizzo e-mail per attivare queste funzioni.", + "noemailprefs": "Indicare un indirizzo email per attivare queste funzioni.", "emailconfirmlink": "Conferma il tuo indirizzo email", "invalidemailaddress": "L'indirizzo e-mail indicato ha un formato non valido. Inserire un indirizzo valido o svuotare la casella.", "cannotchangeemail": "Gli indirizzi e-mail non possono essere modificati in questo wiki.", @@ -592,7 +592,7 @@ "pt-createaccount": "registrati", "pt-userlogout": "esci", "php-mail-error-unknown": "Errore sconosciuto nella funzione PHP mail()", - "user-mail-no-addy": "Hai cercato di inviare una e-mail senza un indirizzo.", + "user-mail-no-addy": "Hai cercato di inviare una email senza un indirizzo.", "user-mail-no-body": "Tentato di inviare una e-mail con un testo vuoto o estremamente breve.", "changepassword": "Cambia password", "resetpass_announce": "Per completare l'accesso, è necessario impostare una nuova password.", @@ -676,7 +676,7 @@ "changeemail-password": "La password su {{SITENAME}}:", "changeemail-submit": "Modifica email", "changeemail-throttled": "Sono stati effettuati troppi tentativi di accesso.\nAttendi $1 e riprova in seguito.", - "changeemail-nochange": "Per favore inserisci un nuovo indirizzo e-mail.", + "changeemail-nochange": "Per favore inserisci un nuovo indirizzo email.", "resettokens": "Reimposta token", "resettokens-text": "Qui puoi reimpostare le chiavi che permettono l'accesso a determinati dati privati associati alla tua utenza.\n\nDovresti farlo se le hai accidentalmente condivise con qualcuno o se la tua utenza è stato compromessa.", "resettokens-no-tokens": "Non ci sono token da reimpostare.", @@ -2162,8 +2162,8 @@ "defemailsubject": "Messaggio da {{SITENAME}} dall'utente \"$1\"", "usermaildisabled": "e-mail utente disabilitata", "usermaildisabledtext": "Non è possibile inviare e-mail ad altri utenti su questo wiki", - "noemailtitle": "Nessun indirizzo e-mail", - "noemailtext": "Questo utente non ha indicato un indirizzo e-mail valido.", + "noemailtitle": "Nessun indirizzo email", + "noemailtext": "Questo utente non ha indicato un indirizzo email valido.", "nowikiemailtext": "Questo utente ha scelto di non ricevere messaggi di posta elettronica dagli altri utenti.", "emailnotarget": "Nome utente del destinatario inesistente o non valido.", "emailtarget": "Inserisci il nome utente del destinatario", @@ -3347,14 +3347,14 @@ "confirmemail_oncreate": "Un codice di conferma è stato spedito all'indirizzo\ndi posta elettronica indicato. Il codice non è necessario per accedere al sito,\nma è necessario fornirlo per poter abilitare tutte le funzioni del sito che fanno\nuso della posta elettronica.", "confirmemail_sendfailed": "{{SITENAME}} non può inviare il messaggio e-mail di conferma. Verificare che il proprio indirizzo e-mail non contenga caratteri non validi.\n\nMessaggio di errore del mailer: $1", "confirmemail_invalid": "Codice di conferma non valido. Il codice potrebbe essere scaduto.", - "confirmemail_needlogin": "È necessario $1 per confermare il proprio indirizzo e-mail.", + "confirmemail_needlogin": "È necessario $1 per confermare il proprio indirizzo email.", "confirmemail_success": "L'indirizzo e-mail è confermato. Ora è possibile [[Special:UserLogin|eseguire l'accesso]] e fare pieno uso del sito.", "confirmemail_loggedin": "L'indirizzo email è stato confermato.", "confirmemail_subject": "{{SITENAME}}: richiesta di conferma dell'indirizzo", "confirmemail_body": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1, ha registrato l'account \"$2\" su {{SITENAME}} indicando questo indirizzo e-mail.\n\nPer confermare che l'account ti appartiene veramente e attivare le funzioni relative all'invio di e-mail su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe *non* hai registrato tu l'account, segui questo collegamento per annullare la conferma dell'indirizzo e-mail:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.", "confirmemail_body_changed": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1,\nha modificato l'indirizzo e-mail dell'account \"$2\" su {{SITENAME}} indicando questo indirizzo e-mail.\n\nPer confermare che l'account ti appartiene veramente e riattivare le funzioni relative all'invio\ndi e-mail su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe l'account *non* ti appartiene, segui questo collegamento\nper annullare la conferma dell'indirizzo e-mail:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.", "confirmemail_body_set": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1,\nha impostato l'indirizzo email dell'account \"$2\" su {{SITENAME}} indicando questo indirizzo email.\n\nPer confermare che l'account ti appartiene veramente e attivare le funzioni relative all'invio\ndi email su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe l'account *non* ti appartiene, segui questo collegamento\nper annullare la conferma dell'indirizzo email:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.", - "confirmemail_invalidated": "Richiesta di conferma indirizzo e-mail annullata", + "confirmemail_invalidated": "Richiesta di conferma indirizzo email annullata", "invalidateemail": "Annulla richiesta di conferma e-mail", "notificationemail_subject_changed": "L'indirizzo di posta elettronica registrato su {{SITENAME}} è stato modificato", "notificationemail_subject_removed": "L'indirizzo di posta elettronica registrato su {{SITENAME}} è stato rimosso", diff --git a/languages/i18n/ja.json b/languages/i18n/ja.json index 367115c24d..6be5b5db88 100644 --- a/languages/i18n/ja.json +++ b/languages/i18n/ja.json @@ -1636,7 +1636,7 @@ "uploaded-script-svg": "アップロードされたSVGファイルにスクリプト可能な要素「$1」が見つかりました。", "uploaded-hostile-svg": "アップロードされたSVGファイルのスタイル要素に安全ではないCSSが見つかりました。", "uploaded-event-handler-on-svg": "イベントハンドラをセットする属性 $1=\"$2\" は、SVGファイルを許可されていません。", - "uploaded-href-attribute-svg": "SVG ファイルの href 属性が http:// または https:// のターゲットのみにリンクする <$1 $2=\"$3\"> が見つかりました。", + "uploaded-href-attribute-svg": " 要素では、データ (埋め込みファイル)、http://、https://、フラグメント (#、ID 属性など) のターゲットにのみリンクできます。 などの他の要素では、データとフラグメントのみが使用できます。SVG をエクスポートする際は画像を埋め込むようにしてください。アップロードされたファイルには <$1 $2=\"$3\"> が含まれています。", "uploaded-href-unsafe-target-svg": "アップロードされた SVG ファイルの、安全ではないデータ URI にターゲット <$1 $2=\"$3\"> の href が見つかりました。", "uploaded-animate-svg": "アップロードされたSVGファイルに、「from」属性 <$1 $2=\"$3\"> を使用した、href を変更させる可能性がある「animate」タグが見つかりました。", "uploaded-setting-event-handler-svg": "アップロードされたSVGファイルに、ブロックされているイベントハンドラ属性が設定された <$1 $2=\"$3\"> が見つかりました。", @@ -1726,7 +1726,7 @@ "lockmanager-fail-closelock": "「$1」用のロックファイルを閉じることができませんでした。", "lockmanager-fail-deletelock": "「$1」用のロックファイルを削除できませんでした。", "lockmanager-fail-acquirelock": "「$1」用のロックを取得できませんでした。", - "lockmanager-fail-openlock": "「$1」用のロックファイルを開くことができませんでした。", + "lockmanager-fail-openlock": "「$1」用のロックファイルを開くことができませんでした。アップロードディレクトリが正しく設定されており、ウェブサーバーによる書き込みの権限が許可されていることを確認してください。詳しくは https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory をご覧ください。", "lockmanager-fail-releaselock": "「$1」用のロックを解放できませんでした。", "lockmanager-fail-db-bucket": "バケツ $1 で十分な数のロックデータベースに接触できませんでした。", "lockmanager-fail-db-release": "データベース $1 上のロックを解放できませんでした。", @@ -1745,6 +1745,12 @@ "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 の MIME $1 に対するハンドラが見つかりません。", + "uploadstash-bad-path-bad-format": "キー「$1」は適切な形式ではありません。", "uploadstash-zero-length": "ファイルのサイズがゼロです。", "invalid-chunk-offset": "無効なチャンクオフセット", "img-auth-accessdenied": "アクセスが拒否されました", @@ -2693,6 +2699,7 @@ "import-mapping-namespace": "名前空間へインポート:", "import-mapping-subpage": "次のページの下位ページとしてインポート:", "import-upload-filename": "ファイル名:", + "import-upload-username-prefix": "インターウィキ接頭辞:", "import-comment": "コメント:", "importtext": "元のウィキで[[Special:Export|書き出し機能]]を使用してファイルに書き出してください。\nそれをコンピューターに保存した後、こちらへアップロードしてください。", "importstart": "ページを取り込み中...", @@ -2889,6 +2896,7 @@ "pageinfo-category-subcats": "下位カテゴリ数", "pageinfo-category-files": "ファイル数", "pageinfo-user-id": "利用者 ID", + "pageinfo-file-hash": "ハッシュ値", "markaspatrolleddiff": "巡回済みにする", "markaspatrolledtext": "このページを巡回済みにする", "markaspatrolledtext-file": "このファイルの版を巡回済みにする", @@ -3970,9 +3978,9 @@ "limitreport-expensivefunctioncount": "高負荷パーサー関数の数", "limitreport-expensivefunctioncount-value": "$1/$2", "expandtemplates": "テンプレートを展開", - "expand_templates_intro": "この特別ページは、入力したテキストに含まれるすべてのテンプレートを再帰的に展開します。\n{{#language:…}} のようなパーサー関数や、\n{{CURRENTDAY}} のような変数も展開します。\nつまり、二重中括弧で囲まれたものほぼすべてを展開します。", + "expand_templates_intro": "この特別ページは、入力したウィキテキストに含まれるすべてのテンプレートを再帰的に展開します。\n{{#language:…}} のようなパーサー関数や、\n{{CURRENTDAY}} のような変数も展開します。\nつまり、二重中括弧で囲まれたものほぼすべてを展開します。", "expand_templates_title": "{{FULLPAGENAME}} などで使用するページ名:", - "expand_templates_input": "展開するテキスト:", + "expand_templates_input": "展開するウィキテキスト:", "expand_templates_output": "展開結果", "expand_templates_xml_output": "XML 出力", "expand_templates_html_output": "出力される HTML ソース", @@ -3984,7 +3992,7 @@ "expand_templates_preview": "プレビュー", "expand_templates_preview_fail_html": "{{SITENAME}} ではHTMLソースが有効になっており、セッションデータの損失が生じているので、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。\n\nこれが合法的なプレビューの試みである場合には、もう一度試してください。\nそれでも動作しない場合は、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。", "expand_templates_preview_fail_html_anon": "{{SITENAME}} ではHTMLソースが有効になっており、ログインしていないため、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。\n\nこれが合法的なプレビューの試みである場合には、[[Special:UserLogin|ログイン]]してもう一度試してください。", - "expand_templates_input_missing": "文章を入力してください。", + "expand_templates_input_missing": "ウィキテキストを入力してください。", "pagelanguage": "ページ言語の変更", "pagelang-name": "ページ", "pagelang-language": "言語", @@ -4208,7 +4216,7 @@ "restrictionsfield-label": "許可する IP の範囲:", "restrictionsfield-help": "一行につき、単一の IP アドレス、もしくは CIDR による範囲。全帯域からの接続を許可する場合:
0.0.0.0/0\n::/0
", "edit-error-short": "エラー: $1", - "edit-error-long": "エラー:\n\n\n\n$1", + "edit-error-long": "エラー:\n\n\n\n$1", "revid": "版 $1", "pageid": "ページID $1", "rawhtml-notallowed": "<html>タグは通常ページ以外では使用できません。", diff --git a/languages/i18n/lij.json b/languages/i18n/lij.json index 87fd05cb13..171b989877 100644 --- a/languages/i18n/lij.json +++ b/languages/i18n/lij.json @@ -207,7 +207,7 @@ "mediawikipage": "Vizualizza o messaggio", "templatepage": "Vizualizza o modello", "viewhelppage": "Móstra a pagina d'agiutto", - "categorypage": "Veddi a pagina da categorîa", + "categorypage": "Amia a pagina da categoria", "viewtalkpage": "Amîa a pagina de discusción", "otherlanguages": "In âtre léngoe", "redirectedfrom": "(Rendirissou da $1)", @@ -346,7 +346,7 @@ "perfcached": "I dæti chì apreuvo son estræti da 'na coppia ''cache'' do database, e porrieivan no ese agiornæ. Un mascimo de {{PLURAL:$1|un risultou o l'è disponibbile|$1 risultæ son disponibbili}} into cache.", "perfcachedts": "I dæti chì apreuvo son estræti da una coppia ''cache'' do database, o quæ urtimo agiornamento o remonta a-o $1. Un mascimo de {{PLURAL:$4|un risultou o l'è disponibbile|$4 risultæ son disponibili}} into cache.", "querypage-no-updates": "I agiornamenti da pagina son temporaniamente sospeixi. I dæti in lé contegnui no saian aggiornæ.", - "viewsource": "Veddi a fonte", + "viewsource": "Vixualizza wikitesto", "viewsource-title": "Visualizza sorgente de $1", "actionthrottled": "Assion ritardâ", "actionthrottledtext": "Comme mesua de segueça contra o spam, l'esecuçion de çerte açioin a l'è limitâ a un nummero mascimo de votte inte 'n determinòu periodo de tempo, limmite che ti t'hæ superòu. Se prega de riprovâ tra quarche menuto.", @@ -611,8 +611,8 @@ "publishpage": "Pubbrica a paggina", "publishchanges": "Pubbrica e modiffiche", "preview": "Anteprìmma", - "showpreview": "Veddi l'anteprimma", - "showdiff": "Veddi i cangiamenti", + "showpreview": "Amia l'anteprimma", + "showdiff": "Mostra modiffiche", "blankarticle": "Atençión: a pàgina che ti çerchi a l'é vêua.\nClicando tórna in sce \"$1\", a pàgina a saiâ creâ sénsa contegnûi.", "anoneditwarning": "Attension: No t'ê introu. Se ti fæ di cangi o teu adresso IP o saiâ vixibile pubbricamente. Se [$1 ti intri] ò [$2 ti crei un'utensa], e teu modifiche saian attribuie a-o teu nomme utente, insemme a di atri benefiççi.", "anonpreviewwarning": "No t'hæ fæto l'accesso. Se ti sarvi inta stoia da paggina ghe saiâ solo o to adresso IP", @@ -756,7 +756,7 @@ "undo-summary-username-hidden": "Anullou a modiffica $1 de un utente ascoso", "cantcreateaccount-text": "A registrassion da questo addresso IP ($1) a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta a l'è ''$2''", "cantcreateaccount-range-text": "A registraçion da di addressi IP inte l'intervallo $1, ch'o l'includde o teu ($4), a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta da $3 a l'è $2", - "viewpagelogs": "Veddi i log relativi a 'sta paggina.", + "viewpagelogs": "Amia i log relativi a 'sta paggina.", "nohistory": "A stoia de verscioin de sta paggina a no gh'è.", "currentrev": "Verscion attuâle", "currentrev-asof": "Ùrtima revixón do $1", @@ -1282,7 +1282,7 @@ "recentchanges-label-unpatrolled": "Sto cangiaménto o no l'é stæto ancón verificòu", "recentchanges-label-plusminus": "Variassion da paggina in nummero de byte", "recentchanges-legend-heading": "Legenda:", - "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (veddi e [[Special:NewPages|neuve paggine]])", + "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (amia e [[Special:NewPages|noeuve paggine]])", "recentchanges-submit": "Fanni vedde", "rcfilters-activefilters": "Filtri attivi", "rcfilters-advancedfilters": "Filtri avançæ", @@ -1424,7 +1424,7 @@ "recentchanges-page-removed-from-category-bundled": "[[:$1]] rimossa da-a categoria, [[Special:WhatLinksHere/$1|questa pagina a l'è inclusa a l'interno di atre pagine]]", "autochange-username": "Modiffica aotomattica MediaWiki", "upload": "Carrega 'n file", - "uploadbtn": "Carrega 'n file", + "uploadbtn": "Carreghilo", "reuploaddesc": "Torna a-o moddulo pe-o caregamento.", "upload-tryagain": "Invia a descrission do file modificou", "uploadnologin": "No t'ê introu", @@ -1627,8 +1627,8 @@ "upload-curl-error6-text": "Imposcibile razonze a URL specificâ. Verifica che a URL a sæ scrita correttamente e che o scito in question o sæ attivo.", "upload-curl-error28": "Tempo descheito pe l'upload", "upload-curl-error28-text": "O scito remoto o l'ha impiegou troppo tempo a risponde. Verifica che o sito o sæ attivo, attendi quarche menuto e proeuva torna, se mai inte 'n momento con meno traffego.", - "license": "Licénsa:", - "license-header": "Licénsa", + "license": "Liçençia:", + "license-header": "Liçençia", "nolicense": "Nisciûnn-a liçensa indicâa", "licenses-edit": "Modiffica opçioin de liçença", "license-nopreview": "(Anteprimma non disponibbile)", @@ -2152,7 +2152,7 @@ "modifiedarticleprotection-comment": "{{GENDER:$2|Cangiou o livello de proteçion}} pe \"[[$1]]\"", "unprotectedarticle-comment": "{{GENDER:$2|Rimosso a proteçion}} da \"[[$1]]\"", "protect-title": "Cangio do livello de proteçion pe \"$1\"", - "protect-title-notallowed": "Veddi o livello de proteçion de \" $1 \"", + "protect-title-notallowed": "Amia o livello de proteçion de \" $1 \"", "prot_1movedto2": "[[$1]] mesciòu a [[$2]]", "protect-badnamespace-title": "Namespace non protezibbile", "protect-badnamespace-text": "E pagine de questo namespace no poeuan ese protezue.", @@ -2201,9 +2201,9 @@ "restriction-level-autoconfirmed": "semi-protezua", "restriction-level-all": "Tutti i livelli", "undelete": "Amîa e paggine scassæ", - "undeletepage": "Veddi e recuppera e pagine scançellæ", + "undeletepage": "Amia e recuppera e paggine scassæ", "undeletepagetitle": "'''Quanto segue o l'è composto da de revixoin scassæ de [[:$1|$1]]'''.", - "viewdeletedpage": "Veddi e paggine scassæ", + "viewdeletedpage": "Amia e paggine scassæ", "undeletepagetext": "{{PLURAL:$1|A seguente pagina a l'è stæta scassâ, ma a l'è ancon in archivio e pertanto a poeu ese recuperâ|E seguente pagine son stæte scassæ, ma son ancon in archivio e pertanto poeuan ese recuperæ}}. L'archivio o poeu ese vuou periodicamente.", "undelete-fieldset-title": "Ripristina revixoin", "undeleteextrahelp": "Pe recuperâ l'intrega cronologia da pagina, lascia tutte e caselle deseleçionæ e fanni clic insce '''''{{int:undeletebtn}}'''''.\nPe effettuâ un ripristino selettivo, seleçion-a e caselle corrispondente a-e verscioin da ripristinâ e fanni clic insce '''''{{int:undeletebtn}}'''''.", @@ -2625,13 +2625,13 @@ "tooltip-t-permalink": "Colegaménto fisso a sta revixión da pàgina", "tooltip-ca-nstab-main": "Véddi a vôxe", "tooltip-ca-nstab-user": "Amîa a paggina utente", - "tooltip-ca-nstab-media": "Veddi a paggina do file murtimediâ", + "tooltip-ca-nstab-media": "Amia a paggina do file murtimediâ", "tooltip-ca-nstab-special": "Sta chi l'è 'na pàgina speciâle e a no peu êse cangiâ", "tooltip-ca-nstab-project": "Véddi a pàgina de servìçio", - "tooltip-ca-nstab-image": "Veddi a paggina do file", - "tooltip-ca-nstab-mediawiki": "Veddi o messaggio de scistema", - "tooltip-ca-nstab-template": "Veddi o template", - "tooltip-ca-nstab-help": "Veddi a paggina d'agiûtto", + "tooltip-ca-nstab-image": "Amia a paggina do file", + "tooltip-ca-nstab-mediawiki": "Amia o messaggio de scistema", + "tooltip-ca-nstab-template": "Amia o template", + "tooltip-ca-nstab-help": "Amia a paggina d'agiutto", "tooltip-ca-nstab-category": "Véddi a pàgina da categorîa", "tooltip-minoredit": "Marchilo comme cangiaménto minô", "tooltip-save": "Sarva i cangiaménti", @@ -3235,8 +3235,8 @@ "watchlistedit-clear-removed": "L'è stæto eliminou {{PLURAL:$1|una paggina|$1 paggine}}:", "watchlistedit-too-many": "Gh'è troppe paggine da visualizzâ chì.", "watchlisttools-clear": "Svoeua a lista sott'öservaçion", - "watchlisttools-view": "Veddi e modiffiche pertinenti", - "watchlisttools-edit": "Veddi e modiffica a lista", + "watchlisttools-view": "amia e modiffiche pertinenti", + "watchlisttools-edit": "Amia e modiffica a lista", "watchlisttools-raw": "Modiffica a lista in formato testo", "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discuscioin]])", "timezone-local": "Locale", diff --git a/languages/i18n/pt-br.json b/languages/i18n/pt-br.json index a3f3ce6fae..3820aa88d6 100644 --- a/languages/i18n/pt-br.json +++ b/languages/i18n/pt-br.json @@ -813,6 +813,7 @@ "postedit-confirmation-created": "A página foi criada.", "postedit-confirmation-restored": "Esta página foi restaurada.", "postedit-confirmation-saved": "Sua edição foi salva", + "postedit-confirmation-published": "A sua edição foi publicada.", "edit-already-exists": "Não foi possível criar uma nova página.\nEla já existia.", "defaultmessagetext": "Texto da mensagem padrão", "content-failed-to-parse": "Falha ao analisar o conteúdo $2 para o modelo $1: $3", diff --git a/languages/i18n/pt.json b/languages/i18n/pt.json index 88ac09e34d..15d950f91a 100644 --- a/languages/i18n/pt.json +++ b/languages/i18n/pt.json @@ -1717,7 +1717,7 @@ "lockmanager-fail-closelock": "Não foi possível encerrar a referência de bloqueio para \"$1\".", "lockmanager-fail-deletelock": "Não foi possível eliminar a referência de bloqueio para \"$1\".", "lockmanager-fail-acquirelock": "Não foi possível adquirir bloqueio para \"$1\".", - "lockmanager-fail-openlock": "Não foi possível abrir o ficheiro de bloqueio de \"$1\".", + "lockmanager-fail-openlock": "Não foi possível abrir o ficheiro de bloqueio de \"$1\". Verifique que o seu diretório de carregamento está devidamente configurado e que o seu servidor de Internet tem permissão para escrever nesse diretório. Para mais informações, consulte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.", "lockmanager-fail-releaselock": "Não foi possível libertar o bloqueio de \"$1\".", "lockmanager-fail-db-bucket": "Não foi possível contactar bases de dados de bloqueio suficientes no \"bucket\" $1.", "lockmanager-fail-db-release": "Não foi possível libertar bloqueios na base de dados $1.", diff --git a/languages/i18n/ru.json b/languages/i18n/ru.json index 4bff6f5c2c..d7c8990957 100644 --- a/languages/i18n/ru.json +++ b/languages/i18n/ru.json @@ -822,6 +822,7 @@ "postedit-confirmation-created": "Страница создана.", "postedit-confirmation-restored": "Страница была восстановлена.", "postedit-confirmation-saved": "Ваша правка сохранена.", + "postedit-confirmation-published": "Ваша правка была опубликована.", "edit-already-exists": "Невозможно создать новую страницу.\nОна уже существует.", "defaultmessagetext": "Текст по умолчанию", "content-failed-to-parse": "Содержимое $2 не соответствует типу $1: $3.", @@ -1753,7 +1754,7 @@ "lockmanager-fail-closelock": "Не удалось закрыть файл блокировки для «$1».", "lockmanager-fail-deletelock": "Не удалось удалить файл блокировки для «$1».", "lockmanager-fail-acquirelock": "Не удалось добиться блокировки «$1».", - "lockmanager-fail-openlock": "Не удалось открыть файл блокировки для «$1».", + "lockmanager-fail-openlock": "Не удалось открыть файл блокировки для «$1». Убедитесь, что ваш каталог загрузки настроен правильно, а ваш веб-сервер имеет разрешение на запись в этот каталог. Дополнительную информацию см. на https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.", "lockmanager-fail-releaselock": "Не удалось разблокировать «$1».", "lockmanager-fail-db-bucket": "Не удалось связаться с достаточным количеством баз блокировок в сегменте $1.", "lockmanager-fail-db-release": "Не удалось снять блокировку базы данных $1 .", diff --git a/languages/i18n/sah.json b/languages/i18n/sah.json index 676b3e3c25..55494e24b8 100644 --- a/languages/i18n/sah.json +++ b/languages/i18n/sah.json @@ -711,6 +711,7 @@ "postedit-confirmation-created": "Сирэй оҥоһулунна.", "postedit-confirmation-restored": "Сирэй сөргүтүлүннэ.", "postedit-confirmation-saved": "Көннөрүүҥ бигэргэннэ.", + "postedit-confirmation-published": "Уларытыыҥ олоххо киирдэ.", "edit-already-exists": "Саҥа сирэйи оҥорор табыллыбат.\nМаннык сирэй баар эбит.", "defaultmessagetext": "Туспа этиллибэтэҕинэ суруллар тиэкис", "content-failed-to-parse": "$2 иһинээҕитэ $1 көрүҥэр сөп түбэспэт: $3.", diff --git a/languages/i18n/sr-ec.json b/languages/i18n/sr-ec.json index 6e57b62c6a..f1e57fa2e7 100644 --- a/languages/i18n/sr-ec.json +++ b/languages/i18n/sr-ec.json @@ -3527,6 +3527,7 @@ "compare-title-not-exists": "Наведени наслов не постоји.", "compare-revision-not-exists": "Наведена измена не постоји.", "diff-form": "Разлике", + "diff-form-submit": "Прикажи разлике", "permanentlink": "Стална веза", "dberr-problems": "Дошло је до техничких проблема.", "dberr-again": "Сачекајте неколико минута и поново учитајте страницу.", @@ -3740,6 +3741,7 @@ "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": "До датума:", @@ -3783,9 +3785,13 @@ "log-action-filter-rights-autopromote": "аутоматски", "log-action-filter-upload-upload": "Ново отпремање", "log-action-filter-upload-overwrite": "промена постојећег", + "authmanager-authplugin-setpass-failed-title": "Неуспешна промена лозинке", "authmanager-email-label": "Имејл", "authmanager-email-help": "Имејл адреса", + "authmanager-realname-label": "Право име", + "authmanager-realname-help": "Право име корисника", "authprovider-resetpass-skip-label": "Прескочи", + "cannotauth-not-allowed-title": "Приступ је одбијен", "changecredentials": "Промјена акредитива", "changecredentials-submit": "Промени", "removecredentials": "Уклањање акредитива", @@ -3793,6 +3799,9 @@ "credentialsform-account": "Назив налога:", "userjsispublic": "Напомена: JavaScript подстранице не би требале садржавати поверљиве информације будући да су видљиве другим корисницима.", "usercssispublic": "Напомена: CSS подстранице не би требале садржавати поверљиве информације будући да су видљиве другим корисницима.", + "edit-error-short": "Грешка: $1", + "edit-error-long": "Грешке:\n\n$1", + "revid": "измена $1", "rawhtml-notallowed": "<html> тагови не могу да се користе ван нормалних страница.", "gotointerwiki": "Напуштам пројекат {{SITENAME}}", "gotointerwiki-invalid": "Одабрани наслов је неисправан.", diff --git a/languages/i18n/sty.json b/languages/i18n/sty.json new file mode 100644 index 0000000000..9b40b5cdc2 --- /dev/null +++ b/languages/i18n/sty.json @@ -0,0 +1,630 @@ +{ + "@metadata": { + "authors": [ + "Khanmarat", + "Sorbat", + "Stephanecbisson", + "Рустам Нурыев" + ] + }, + "sunday": "Йәкшәмпе", + "monday": "Түшәмпе", + "tuesday": "Сишәмпе", + "wednesday": "Царшампы", + "thursday": "Пәйшәмпе", + "friday": "Йома", + "saturday": "Шәмпе", + "sun": "Йәк", + "mon": "Түш", + "tue": "Сиш", + "wed": "Цар", + "thu": "Пәй", + "fri": "Йом", + "sat": "Шәм", + "january": "январь", + "february": "февраль", + "march": "март", + "april": "апрель", + "may_long": "май", + "june": "июнь", + "july": "июль", + "august": "август", + "september": "сентябрь", + "october": "октябрь", + "november": "ноябрь", + "december": "декабрь", + "january-gen": "январь", + "february-gen": "февраль", + "march-gen": "март", + "april-gen": "апрель", + "may-gen": "май", + "june-gen": "июнь", + "july-gen": "июль", + "august-gen": "август", + "september-gen": "сентябрь", + "october-gen": "октябрь", + "november-gen": "ноябрь", + "december-gen": "декабрь", + "jan": "янв", + "feb": "фев", + "mar": "мар", + "apr": "апр", + "may": "май", + "jun": "июн", + "jul": "июл", + "aug": "авг", + "sep": "сен", + "oct": "окт", + "nov": "ноя", + "dec": "дек", + "pagecategories": "{{PLURAL:$1|1=Категория|Категориялар}}", + "category_header": "«$1» категориятағы питләр", + "subcategories": "Эцке категориялар", + "category-media-header": "«$1» категориясынтағы файллар", + "category-empty": "Пы категория ҡәсерге уаҡытта пуш.", + "hidden-categories": "{{PLURAL:$1|Йәшерен категория|Йәшерен категориялар}}", + "category-subcat-count": "{{PLURAL:$2|1=Пы төргөнтә түмәнтәге астөргөн кенә пар. |Пы төргөнтә пулған $2-тән {{PLURAL:$1|астөргөн|астөргөннәр}} $1 ҡына күрсәтеләте.}}", + "category-article-count": "{{PLURAL:$2|1=Пы төргөнтә пер генә пит пар. Пы категориятан пулған {{PLURAL:$2|пулған|пулғаннар}}}} $2 -тән {{PLURAL:$1|$1 пит күрсәтелгән|$1 питләр күрсәтелгәннәр}}.", + "category-file-count": "{{PLURAL:$2|Пы категорията пер генә файл пар.|Категориятағы $2 файлныңҡы {{PLURAL:$1|$1 файлы күргәселгән}}.}}", + "listingcontinuesabbrev": "ҡушлау", + "noindex-category": "Индексланмайтығын питләр", + "broken-file-category": "Файлға эшләмәйтеген ссылкалары пулған питләр", + "about": "Сүрәтләмә", + "newwindow": "(йаңа тәрәсәтә)", + "cancel": "Пулмайын иткәле", + "mytalk": "Уйлашыу", + "navigation": "Навигация", + "and": " әм", + "namespaces": "Исемнәрнең киңнеге", + "variants": "Вариантлар", + "navigation-heading": "Навигация", + "returnto": "$1 питкә ҡайтыу.", + "tagline": "{{SITENAME}} проекттан", + "help": "Пелешмә", + "search": "Эстәү", + "searchbutton": "Тапҡалы", + "searcharticle": "‎Күцкәле", + "history": "Тариҡ", + "history_short": "Тариҡ", + "printableversion": "Пастырыр өцөн версия", + "permalink": "Келәң ссылка", + "view": "Ҡарау", + "view-foreign": "$1 сайтта ҡарағалы", + "edit": "Төсәткәле", + "create": "Пултырғалы", + "create-local": "Локаль сүрәтләмәне өстәгәле", + "delete": "Йуҡ иткәле", + "newpage": "Йаңа пит", + "talkpagelinktext": "уйлашыу", + "personaltools": "Персональ ҡораллар", + "talk": "Уйлап ҡарау", + "views": "Ҡараулар", + "toolbox": "‎Ҡораллар", + "otherlanguages": "Пашҡа телләртә", + "redirectedfrom": "($1 питтән йебәрелгән)", + "redirectpagesub": "Йусыҡлау пит", + "redirectto": "Йусыҡлағалы", + "lastmodifiedat": "Пы пит $1гә, $2 пуйта суңҡа төсәйтелгән.", + "jumpto": "Анта күцкәле:", + "jumptonavigation": "навигация", + "jumptosearch": "эстәү", + "aboutsite": "‎{{SITENAME}} турлы", + "aboutpage": "Project:Сүрәтләмә", + "copyright": "Эцтәлеге $1 лицензия пелән кенә ацыҡ (пашҡа исем күргәселмәсә).", + "copyrightpage": "{{ns:project}}:Авторның ҡаҡы", + "currentevents": "Ҡәсерге пулған ҡәлләр", + "currentevents-url": "Project:Ҡәсерге пулған ҡәлләр", + "disclaimers": "Пелеклелектән паш тартыу", + "disclaimerpage": "Project:Пелеклелектән паш тартыу", + "edithelp": "Төсәтеүгә йәртәм", + "mainpage": "Паш пит", + "mainpage-description": "Паш пит", + "portal": "Перләшмә", + "portal-url": "Project:Порталның перләшмәсе", + "privacy": "Серне саҡлау сәйәсәт", + "privacypage": "Project:Серне саҡлайтығын сәйәсәт", + "retrievedfrom": "Аҡма — «$1»", + "youhavenewmessages": "{{PLURAL:$3|Сестә}} $1 ($2) пар.", + "youhavenewmessagesfromusers": "{{PLURAL:$4|Сескә}} {{PLURAL:$3|$3 ҡатнашыуцытан}} $1 килте ($2).", + "newmessageslinkplural": "{{PLURAL:$1|1=йаңа ҡәбәр|999=йаңа ҡәбәрләр}}", + "newmessagesdifflinkplural": "{{PLURAL:$1|1=суңҡы пашҡартыу|999=суңҡы пашҡартыулар}}", + "editsection": "төсәткәле", + "editold": "төсәткәле", + "viewsourceold": "пашлапҡы кодны ҡарағалы", + "editlink": "төсәткәле", + "viewsourcelink": "паш кодны ҡарағалы", + "editsectionhint": "$1 ‎пүлекне төсәткәле", + "toc": "Эцтәлек", + "site-atom-feed": "$1 — Atom тасма", + "page-atom-feed": "«$1» — Atom-тасма", + "red-link-title": "$1 (пы пит йуҡ)", + "nstab-main": "Мәҡәлә", + "nstab-user": "Ҡатнашыуцы", + "nstab-special": "Ҡесмәт пит", + "nstab-project": "Проетның пите", + "nstab-image": "Файл", + "nstab-mediawiki": "Пелтереү", + "nstab-template": "Ҡалып", + "nstab-category": "Категория", + "mainpage-nstab": "Паш пит", + "nosuchspecialpage": "Мынтайын ҡесмәт пит йуҡ", + "nospecialpagetext": "Сес сураған ҡесмәт пит йуҡ.\n* Ҡесмәт питләр күцермәлекне ҡараң: [[Special:SpecialPages|{{int:specialpages}}]].", + "badtitle": "Йарамаған исем", + "badtitletext": "Питнең суралған исеме төрөс түгел, пуш йә интервикиныңҡы исеме төрөс күрсәтелмәгән. Пәйәкпәр, исемтә йарамаған символлар пар ты.", + "viewsource": "Вики-текстны ҡарау", + "viewsource-title": "$1 питнең паш текстын ҡарау", + "viewsourcetext": "Сес пы питнеңке паш текстын ҡарап күцерә аласыс.", + "userlogin-yourname": "Ҡулланыуцыныңҡы исеме", + "userlogin-yourname-ph": "Исәп йасмағысныңҡы исемен кергесең", + "userlogin-yourpassword": "Пароль", + "userlogin-yourpassword-ph": "Үсегеснең парольны йасың", + "createacct-yourpassword-ph": "Парольны йасың", + "createacct-yourpasswordagain": "Парольны төрөс тигәле", + "createacct-yourpasswordagain-ph": "Парольны тағын та йасың", + "userlogin-remembermypassword": "Системата ҡалғалы", + "login": "Кергәле", + "userlogin-noaccount": "Исәп йасмағыс йуҡ ма?", + "userlogin-joinproject": "{{SITENAME}} ‎проектҡа ҡушылғалы", + "createaccount": "Йаңа ҡатнашыуцыны теркәү", + "userlogin-resetpassword-link": "Паролеғысны оноттоғос ма?", + "userlogin-helplink2": "Керер өцөн йәртәм", + "createacct-emailoptional": "Электрон почтағысның адресы (тейешлецә түгел)", + "createacct-email-ph": "Электрон почтағысның адресын йасың", + "createacct-submit": "Исәп йасманы пултырғалы", + "createacct-benefit-heading": "{{SITENAME}} — сеснең шигелле кешеләрнең эше.", + "createacct-benefit-body1": "{{PLURAL:$1|төсәтеү}}", + "createacct-benefit-body2": "{{PLURAL:$1|мәҡәлә|мәҡәләнең}}", + "createacct-benefit-body3": "Суңҡы уаҡыттағы {{PLURAL:$1|ҡатнашыуцы}}", + "loginlanguagelabel": "Тел: $1", + "pt-login": "Кергәле", + "pt-login-button": "Кергәле", + "pt-createaccount": "Йаңа аккаунт пултырғалы", + "pt-userlogout": "Цыҡҡалы", + "passwordreset": "Парольны пөтөрөү", + "bold_sample": "Ҡалын пелән йасыу", + "bold_tip": "Ҡалын пелән йасыу", + "italic_sample": "Курсив пелән йасыу", + "italic_tip": "Курсив пелән йасыу", + "link_sample": "Ссылканыңҡы төп исеме", + "link_tip": "Эцке ссылка", + "extlink_sample": "http://www.example.com ссылканың төп исеме", + "extlink_tip": "Тышҡы ссылка (http:// префиксны онотмаң)", + "headline_sample": "Төп исем", + "headline_tip": "2-нце ҡат төп исем", + "nowiki_sample": "Мынта форматлау кәрәкмәйтеген текстны өстәң", + "nowiki_tip": "Вики-форматлауға ҡолаҡ салмау", + "image_tip": "Кергеселгән файл", + "media_tip": "Файлға ссылка", + "sig_tip": "Ҡул ҡуйыуығыс пелән уаҡыт", + "hr_tip": "Горизонталь цыйыҡ (кел ҡулланмаң)", + "summary": "Пашҡартыуларны аңнатыу:", + "minoredit": "Кецкенә пашҡартыу", + "watchthis": "Пы питне көсәткәле", + "savearticle": "Питне йасып ҡуйғалы", + "preview": "Алттан ҡарау", + "showpreview": "Алттан ҡарап цығыу", + "showdiff": "Кергеселгән пашҡартыулар", + "anoneditwarning": "Саҡлыҡ! Сес сайтта теркәлмәтегес. Әгәр тә сес төрлө төсәтеүләр ҡылсағыс, сеснеңке IP-адресығыс пашҡаларға күренеп торор. Әгәр тә Сес [$1 керсәгез] йә [$2 ҡулланыуцы йасманы пултырсағыс], сеснең төсәтеүләр ҡулланыуцы йасмағысҡа пәйле пулыр, шалай уҡ пашҡа өстөннөкләр тыуыр.", + "blockedtext": "Сеснең исәп йасмағыс йә IP адресығыс тыйылған.\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Ҡатларта пы ҡәбәр-пелемнәрне күргәскәле онотмаң.", + "loginreqlink": "кергәле", + "newarticletext": "Сес ссылка пелән әле йасылмаған питкә күцтегес. Йаңа пит ҡылыр өцөн астытағы тәрәсәгә текст йасың (тулыраҡ өцөн [$1 пелешмәлек питне] ҡараң). Әгәр мынта йалғыш кереп киткән пулсағыс, браузерығысныңҡы \"сыртҡа\" кнопкаға пасың.", + "anontalkpagetext": "----\nПы уйлап ҡарау пит исәп йасыуны ҡылтырмаған йә аны ҡулланмайтығын аноним ҡатнашыуцыныңҡы пите. Ҡулланыуцыны таныу өцөн аныңҡы IP-адресы ҡулланылаты. Әгәр сес аноним ҡулланыуцы пулсағыс, сескә йебәрелмәгән ҡатлар алтым тисәгес (пер IP-адрес кән ҡулланыуцыларта пулғалы мөмкин), пашҡа мынтайын аңнашылмауцылыҡлар килеп цыҡмасын өцөн, системаға керең йә теркәлең.", + "noarticletext": "Ҡәсерге уаҡытта пы питтә текст йуҡ. Сес [[Special:Search/{{PAGENAME}}|пы исем кергән пашҡа мәҡәләләрне]], [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} туры килгән йасмаларны] таба йә '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} шантайын уҡ исемле йаңа питне ҡыла]''' аласыс.", + "noarticletext-nopermission": "Ҡәсерге уаҡытта пы питтә текст йуҡ. Сес пашҡа питләртә [[Special:Search/{{PAGENAME}}|пы исемне]] йә [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} журналтағы йасмаларны] эстәй аласыс. Сескә пит йасағалы рөҡсәт пирелмәгән.", + "userpage-userdoesnotexist-view": "«$1» исемле исәп йасма йуҡ.", + "clearyourcache": "Искәрмә: Питне саҡлағаннан суң пашҡартыуларығыс күренер өцөн, браузерығысныңҡы кэшын тасартҡалы кәрәк пулыр.\n* Firefox / Safari: Shift төймәгә пасып, йебәрмәйен, Йаңартҡалы төймәгә пасың, йә Ctrl-F5 йә Ctrl-R (Mac-та ⌘-R) төймәләргә пасың\n* Google Chrome: Ctrl-Shift-R төймәгә пасың (Mac-та ⌘-Shift-R)\n* Internet Explorer: Ctrl төймәгә пасып, йебәрмәйен, Йаңартҡалытөймәгә пасың, йә Ctrl-F5 төймәгә пасың\n* Opera: Ҡораллар → Ҡоролошлар (Mac-та Opera → Ҡоролошлар) пүлеккә күцең, суңынан Ҡауапсыслыҡ → Кереүләр тариҡны тасартыу → Рәсемнәр кэшлау менюта кэш тасартыуны сайлаң", + "previewnote": "Онотмаң, пы алттан ҡарап цығыу ғына.\nПашҡартыуларығыс әле саҡланмаған!", + "continue-editing": "Төсәтеүне ҡушлағалы", + "editing": "«$1» битне төсәтеү", + "creating": "«$1» питне пултырыу", + "editingsection": "«$1» питтә төсәтеү", + "templatesused": "Пы питтә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:", + "templatesusedpreview": "Алттан ҡарау режимта ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:", + "template-protected": "(саҡланҡан)", + "template-semiprotected": "(өлөшө пелән саҡланҡан)", + "hiddencategories": "Пы пит $1 {{PLURAL:$1|йәшерен төргөнкә|$1 йәшерен төргөннәргә}} керәте:", + "permissionserrors": "Кереүнең ҡаҡ ҡатасы", + "permissionserrorstext-withaction": "$2 әмәлен ҡыла алмайсыс. {{PLURAL:$1|1=сәбәбе пуйынца|сәбәпләре пуйынца}}:", + "recreate-moveddeleted-warn": "Саҡлыҡ: Сес элек йуҡ ителгән питне йаңатан ҡылмаҡцы пуласыс.\n\n* Сескә пы питне йаңатан ҡылыу кәрәклекне йаңатан уйлап ҡараң.\nТүмәнтә питнең йуҡ итеү пелән исемен пашҡартыу журнал пар.", + "moveddeleted-notice": "Пы пит уйылған. Пелешмә өцөн астыта пөтөрөү пелән исем алмаштырыу журналларның йасылғаннары күргәселгән.", + "content-model-wikitext": "вики-текст", + "undo-failure": "Аралыҡ пашҡартыулар туры килмәгәнкә, төсәтеүне кире алып пулмайты.", + "viewpagelogs": "Пы питнең журналын ҡарағалы", + "currentrev-asof": "$1, ҡәсерге версия", + "revisionasof": "$1 версия", + "revision-info": "$1 аңышы; {{GENDER:$6|$2}}$7", + "previousrevision": "← Алттағы", + "nextrevision": "Мынан суңҡысы →", + "currentrevisionlink": "Ҡәсерге версия", + "cur": "ҡәсерге", + "last": "алттағы", + "histlegend": "Аңышларны сайлау: сес туры килтергәле теләгән питләрнең аңышларын сайлап алың та  {{int:compare-submit}}.
-ға пасың. Аңнатмалар: ({{int:cur}}) — ҡәсерге аңыштан айырмалар; ({{int:last}}) — алтта пулған аңыштан айырмалар; {{int:minoreditletter}} — сур пулмаған айырмалар.", + "history-fieldset-title": "Төсәтеүләрне эстәү", + "histfirst": "иң элекеләре", + "histlast": "иң суңҡылары", + "history-feed-title": "Пашҡартыулар тариғы", + "history-feed-description": "Пы питнең викитағы пашҡартыулар тариғы", + "history-feed-item-nocomment": "$2-та $1", + "rev-delundel": "күргәскәле/йәшергәле", + "mergelog": "Перләштереүләр журнал", + "history-title": "$1 питенең пашҡартыу тариғы", + "difference-title": "$1 — версиялар арасынтағы айырмалар", + "lineno": "$1 йул:‎", + "compareselectedversions": "Сайланҡан аңышларны туры килтергәле", + "editundo": "пулмайын иткәле", + "diff-empty": "(айырмалар йуҡ)", + "diff-multi-sameuser": "(пы ҡулланыуцының {{PLURAL:$1|аралыҡ версия $1|аралыҡ версиялары $1}} күргәселмәгән)", + "diff-multi-otherusers": "({{PLURAL:$2|ҡатнашыуцының|$2 ҡатнашыуцыларның}} {{PLURAL:$1|ара аңышы|$1 арадаш аңышлары}} күргәселмәгән)", + "searchresults": "Эстәүнең йомҡаҡлары", + "searchresults-title": "«$1»ны эстәгәле", + "prevn": "алттағы {{PLURAL:$1|$1}}", + "nextn": "киләсе {{PLURAL:$1|$1}}", + "prevn-title": "Алттағы $1 {{PLURAL:$1|1=йасма|йасмалар}}", + "nextn-title": "{{PLURAL:$1|Киләсе $1 йасма}}", + "shown-title": "$1 пер питтә {{PLURAL:$1|йасыу|йасыулар}} күргәскәле", + "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) ҡарап паҡҡалы", + "searchmenu-exists": "Пы викита [[:$1]] тигән пит пар", + "searchmenu-new": "«[[:$1]]» Вики-проектта пит йасағалы! {{PLURAL:$2|0=|Шалай уҡ, эстәгән арағыстағы табылған питне караң.|Шалай уҡ, эстәгәнегестә табылған питләрне караң.}}", + "searchprofile-articles": "Төп питләр", + "searchprofile-images": "Мультимедиа", + "searchprofile-everything": "Пөтөн йертә", + "searchprofile-advanced": "Киңәйтелгән", + "searchprofile-articles-tooltip": "$1 тә эстәү", + "searchprofile-images-tooltip": "Файлларны эстәү", + "searchprofile-everything-tooltip": "Пөтөн питләртә (уйлашыу питләртә тә) эстәү", + "searchprofile-advanced-tooltip": "Пирелгән исемнәр киңнектә эстәгәгәле", + "search-result-size": "$1 ({{PLURAL:$2|1=$2 сүс|$2 сүс}})", + "search-result-category-size": "$1 {{PLURAL:$1|аса}} ($2 {{PLURAL:$2|астөргөн}}, $3 {{PLURAL:$3|файл}})", + "search-redirect": "($1 питтән йусыҡлау)", + "search-section": "($1 пүлек)", + "search-file-match": "(файлның эцтәлеге пелән туры киләте)", + "search-suggest": "Пәйәкпәр, ошоно эстәйсес пулыр: $1", + "searchall": "пөтөннәйе", + "search-showingresults": "{{PLURAL:$4|$3 нәтижәтән $1| $3 нәтижәләртән $1 — $2}}", + "search-nonefound": "Сурауға туры килгән йауаплар табылматы.", + "mypreferences": "Ҡороулар", + "group-bot": "Ботлар", + "group-sysop": "Администраторлар", + "grouppage-bot": "{{ns:project}}:Ботлар", + "grouppage-sysop": "{{ns:project}}:Администраторлар", + "right-writeapi": "Йасыр өцөн API-ны ҡулланыу", + "newuserlogpage": "Йаңа ҡулланыуцыларны теркәү журналы", + "rightslog": "Ҡатнашыуцының ҡаҡлары журналы", + "action-edit": "пы питне төсәткәле", + "action-createaccount": "Пы исәп йасманы ҡылыу", + "enhancedrc-history": "тариҡ", + "recentchanges": "Йаңа төсәтеүләр", + "recentchanges-legend": "Суңҡы төсәтеүлернең ҡоролошо", + "recentchanges-summary": "Төрлө питләртә эшләнкән суңҡы пашҡартыуларның күцермәлеге", + "recentchanges-noresult": "Күрсәтелгән уаҡытта тейешле шартларға туры килгән пашҡартыулар йуҡ.", + "recentchanges-feed-description": "Викита суңҡы пашҡартыуларны көсәтеү.", + "recentchanges-label-newpage": "Пы төсәтеү йаңа пит ҡылтырты", + "recentchanges-label-minor": "Пы сур пулмаған төсәтеү", + "recentchanges-label-bot": "Пы төсәтеүне бот ҡылған", + "recentchanges-label-unpatrolled": "Пы төсәтеүне әле иц кем тикшермәте", + "recentchanges-label-plusminus": "Питнең сурлығы ошо ҡәтәр байтҡа алмашынты", + "recentchanges-legend-heading": "Аңнатма:", + "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шалай уҡ [[Special:NewPages|йаңа питләр күцермәлеген]] ҡараң)", + "rcfilters-filterlist-title": "Фильтрлар", + "rcfilters-filterlist-noresults": "Иц фильтр табылматы", + "rcnotefrom": "Астта $3, $4 -ға туры килгән {{PLURAL:$5|пашҡартыулар күргәселгән}} ($1 артыҡ түгел).", + "rclistfrom": "$3 $2 алып йаңа пашҡартыуларны күргәскәле", + "rcshowhideminor": "кецкенә төсәтеүләр $1", + "rcshowhideminor-show": "Күргәскәле", + "rcshowhideminor-hide": "Йәшергәле", + "rcshowhidebots": "ботларны $1", + "rcshowhidebots-show": "Күргәскәле", + "rcshowhidebots-hide": "Йәшергәле", + "rcshowhideliu": "$1 танылған ҡулланыуцылар", + "rcshowhideliu-show": "Күргәскәле", + "rcshowhideliu-hide": "Йәшергәле", + "rcshowhideanons": "танылмаған ҡулланыуцылар $1", + "rcshowhideanons-show": "Күргәскәле", + "rcshowhideanons-hide": "Йәшергәле", + "rcshowhidepatr": "$1 ҡараған төсәтеүләр", + "rcshowhidemine": "үс төсәтеүләр $1", + "rcshowhidemine-show": "Күргәскәле", + "rcshowhidemine-hide": "Йәшергәле", + "rclinks": "Суңҡы $2 көн арата йасалған $1 пашҡартыуларны күргәскәле", + "diff": "Төрлө", + "hist": "тариҡ", + "hide": "Йәшергәле", + "show": "Күргәскәле", + "minoreditletter": "м", + "newpageletter": "Й", + "boteditletter": "б", + "rc-change-size-new": "Төсәткәннән суңҡы сурлыҡ: $1 {{PLURAL:$1|1=байт|байт}}", + "rc-old-title": "\"$1\" итеп пашта ҡылынҡан", + "recentchangeslinked": "Пәйләнкән төсәтеүләр", + "recentchangeslinked-feed": "Пәйле пулған пашҡартыулар", + "recentchangeslinked-toolbox": "Пәйле төсәтеүләр", + "recentchangeslinked-title": "\"$1\" өцөн пәйләнкән төсәтеүләр", + "recentchangeslinked-summary": "Пы, күрсәтелгән пит йебәрәтеген (йә күрсәтелгән төргөнкә керәтегән), питләртә суңҡы пашҡартыуларның күцермәлеге. [[Special:Watchlist|көсәтеү күцермәлегегескә]] керәтеген питләр '''ҡалын''' итеп күрсәтелгән.", + "recentchangeslinked-page": "Питнең төп исеме:", + "recentchangeslinked-to": "Кирецә, пы питкә пәйләнкән питләртәге пашҡартыуларны күргәскәле", + "upload": "Файлны төйәгәлә", + "uploadlogpage": "Төйәүләр журнал", + "filedesc": "Ҡысҡа аңнатыу", + "license": "Лицензиясы:", + "license-header": "Лицензирование", + "imgfile": "файл", + "listfiles": "Файлларның күцермәлеге", + "file-anchor-link": "Файл", + "filehist": "Файлның тарығы", + "filehist-help": "Үткән уаҡытта файлныңҡы нинтәйен пулғанын күргегес килсә, көнө/уаҡытына пасың.", + "filehist-revert": "ҡайтарғалы", + "filehist-current": "ҡәсерге", + "filehist-datetime": "Көнө/уаҡыты", + "filehist-thumb": "Уаҡ рәсем", + "filehist-thumbtext": "$1 версиятан пулған сүрәт", + "filehist-nothumb": "Миниатюрасы йуҡ", + "filehist-user": "Ҡатнашыуцы", + "filehist-dimensions": "Сурлығы", + "filehist-comment": "Искәрмә", + "imagelinks": "Файлны ҡулланыу", + "linkstoimage": "Пы файлға {{PLURAL:$1|1=пит|$1 пит}} йебәрәте:", + "linkstoimage-more": "Пы файлға $1-на пашҡа та {{PLURAL:$1|пит}} ссылка ҡылатылар.\nТүмәнтәге күцермәлектә пы файлға $1 {{PLURAL:$1|ссылка}} ғына күргәселгән.\nШалай уҡ тулы күцермәлекне ҡарап пулаты.", + "nolinkstoimage": "Пы файлға йебәргән питләр йуҡ.", + "linkstoimage-redirect": "$1 (файл йусыҡлау) $2", + "sharedupload-desc-here": "Пы файл $1-нан, ул пашҡа проектларта ҡулланыла алаты. Файл турлы [$2 сүрәтләү пите] тулыраҡ пелешмә түмәнтәрәк пирелгән.", + "filepage-nofile": "Мынтайын исемле файл йуҡ.", + "upload-disallowed-here": "Сес пы файлны йаңатан йастыра алмайсыс.", + "randompage": "Уйланмаған мәҡәлә", + "statistics": "Статистика", + "double-redirect-fixer": "Йаңа йусыҡлауның төсәтеүцесе", + "nbytes": "$1 {{PLURAL:$1|байт}}", + "nmembers": "$1 {{PLURAL:$1|объект}}", + "prefixindex": "Питләрнең исемнәренең пашы пуйынца күстәргец", + "listusers": "Ҡатнашыуцыларның күцермәлеге", + "newpages": "Йаңа питләр", + "move": "Йаңа исем ататҡалы", + "pager-newer-n": "{{PLURAL:$1|$1 йаңараҡ}}", + "pager-older-n": "$1 {{PLURAL:$1|искерәк}}", + "booksources": "Китапларның аҡмалары", + "booksources-search-legend": "Китап турлы информация эстәү", + "booksources-search": "Эстәгәле", + "specialloguserlabel": "Ҡылыуцы:", + "speciallogtitlelabel": "Морат (исеме йә {{ns:user}}:ҡулланыуцының исеме):", + "log": "Журналлар", + "all-logs-page": "Пөтөн ацыҡ журналлар", + "alllogstext": "{{SITENAME}} сайтыныңҡы журналларының тула күцермәлеге.\nСес йомҡаҡларны журнал төрөнә, ҡатаншыуцының исеменә ҡарап (сур йә кецкенә ҡәрептән йасылыуына ҡарап ) йә күргәселгән питнең исеменә ҡарап (шулай уҡ сур йә кецкенә ҡәрептән йасылыуына ҡарап) сайлап ала аласыс.", + "logempty": "Журналта йарығытайын асмалар йуҡ.", + "allpages": "Пөтөн питләр", + "allarticles": "Пөтөн питләр", + "allpagessubmit": "Ҡылғалы", + "allpages-hide-redirects": "Йусыҡлауларны йәшергәле", + "categories": "Төргөннәр", + "listgrouprights-members": "(ҡатнашыуцыларның күцермәлеге)", + "emailuser": "Ҡатнашыуцыға ҡат", + "usermessage-editor": "Системалыҡ тапшырыу", + "watchlist": "Көсәтеү күцермәлек", + "mywatchlist": "Көсәтеү күцермәлек", + "watchlistfor2": "$1 $2-ға тип", + "watch": "Көсәткәле", + "unwatch": "Көсәтмәгәле", + "watchlist-details": "Көсәтеү күцермәлегегестә, уйлап ҡарау питләрне исәпләмәйен, {{PLURAL:$1|$1 пит}} пар.", + "wlheader-showupdated": "Сеснең суңҡы пашҡартыулартан суң пашҡарған питләр ҡалын шрифт пелән күргәселгән.", + "wlnote": "Түмәнтә $3 $4 уаҡытта суңҡы {{PLURAL:$2|1=сәғәт|$2 сәғәт}} эцтә эшләнкән {{PLURAL:$1|1=пашҡартыу|$1пашҡартыулар}} күргәселгән.", + "wlshowlast": "Суңҡы $1 сәғәт $2 көн эцентәгесен күргәскәле", + "watchlist-options": "Көсәтеү күцермәлекнең ҡоролошлары", + "enotif_reset": "Пөтөн питләрне ҡаралған тип пилгеләгәле", + "dellogpage": "Уйыуларның журналы", + "rollbacklink": "кире ҡайтарғалы", + "rollbacklinkcount": "$1 {{PLURAL:$1|1=төсәтеүне|төсәтеүне}} кире алғалы", + "protectlogpage": "Саҡлау журнал", + "protectedarticle": "[[$1]] питен йаҡлаған", + "modifiedarticleprotection": "[[$1]]-ныңҡы питенең йаҡлау ҡаты пашҡартылты", + "protect-default": "Пөтөн ҡулланыуцыларға ацыҡ", + "restriction-edit": "Төсәткәле", + "restriction-move": "Йаңа исем ҡушыу", + "namespace": "Исемнәрнең киңнеге:", + "invert": "Сайланҡанны әйләнтергәле", + "tooltip-invert": "Сайланҡан исемнәр арасынта (күрсәтелгән пулса, исемнәрнең пәйле киңнегентә тә) питләртәге пашҡартыуларны йәшерер өцөн пы пилгене ҡороң", + "namespace_association": "Пәйле киңнек", + "tooltip-namespace_association": "Сайланҡан исемнәр киңнек пелән пәйле пулған уйлап ҡаралған исемнәрнең киңнеген кергесер өцөн пы пилгене ҡороң", + "blanknamespace": "(Төп)", + "contributions": "{{GENDER:$1|ҡатнашыуцының}} ҡылған эше", + "contributions-title": "$1 исемле ҡатнашыуцының ҡылған эше", + "mycontris": "Кергескән эшләр", + "anoncontribs": "Кергескән эшләр", + "contribsub2": "{{GENDER:$3|$1}}-ның ҡылған эше ($2)", + "nocontribs": "Күргәселгән шартларға туры килгән пашҡартыулар табылматы.", + "uctop": "(ҡәсерге)", + "month": "Айтан пашлап (анан алттараҡ та):", + "year": "Йылтан пашлап (анан алттараҡ та):", + "sp-contributions-newbies": "Йаңа исәп йасмалартан ҡылынҡан эшне генә күргәскәле", + "sp-contributions-blocklog": "тыйыулар", + "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": "Пит:", + "linkshere": "''[[:$1]]''' питкә киләсе питләр тайанатылар:", + "nolinkshere": "[[:$1]] питкә пер питтән тә ссылка йуҡ.", + "isredirect": "йусыҡлау пит", + "istemplate": "ҡушылыу", + "isimage": "файллы ссылка", + "whatlinkshere-prev": "{{PLURAL:$1|1=алттағы}} $1", + "whatlinkshere-next": "{{PLURAL:$1|1=киләсе}} $1", + "whatlinkshere-links": "← ссылкалар", + "whatlinkshere-hideredirs": "$1 йусыҡлаулар", + "whatlinkshere-hidetrans": "Кергесеүләр $1", + "whatlinkshere-hidelinks": "$1 ссылкалар", + "whatlinkshere-hideimages": "$1 файлның ссылкалары", + "whatlinkshere-filters": "Фильтрлар", + "ipboptions": "2 сәғәт:2 hours,1 көн:1 day,3 көн:3 days,1 атна:1 week,2 атна:2 weeks,1 ай:1 month,3ай:3 months,6 ай:6 months,1 йыл:1 year,уаҡытсыс:infinite", + "infiniteblock": "пилгеле пер уаҡытсыс", + "blocklink": "тыйғалы", + "contribslink": "өлөш", + "blocklogpage": "Тыйыулар журнал", + "blocklogentry": "[[$1]] $2 $3-ҡа пикләте", + "reblock-logentry": "[[$1]] ҡатнашыуцының тыйыуын пашҡартҡан, пөтөү уаҡыты — $2 $3", + "block-log-flags-nocreate": "исәп йасманың теркелеүе тыйылған", + "proxyblocker": "Проксины пикләү", + "movelogpage": "Исем алмаштырыуның журналы", + "export": "Питләрне цығарыу", + "thumbnail-more": "Сурайтҡалы", + "importlogpage": "Кергесеү журнал", + "tooltip-pt-userpage": "{{GENDER:|Сеснең}} ҡатнашыуцының пите", + "tooltip-pt-mytalk": "{{GENDER:|Сеснең}} уйлашыу питегес", + "tooltip-pt-preferences": "{{GENDER:|Сеснең}} ҡоролошларығыс", + "tooltip-pt-watchlist": "Сес көсәтеп парған питләрнең күцермәлеге", + "tooltip-pt-mycontris": "{{GENDER:|Сеснең}} төсәтеүләрнең күцермәлеге", + "tooltip-pt-login": "Мынта регистрация үткәле пулаты, әммә ул тейешлецә түгел", + "tooltip-pt-logout": "Цыҡҡалы", + "tooltip-pt-createaccount": "Тейешлецә пулмаса та, аккаунт ҡылып системаға кергәле киңәш итәбес", + "tooltip-ca-talk": "Төп пит турлы уйлап ҡарашыу", + "tooltip-ca-edit": "Пы питне төсәткәле", + "tooltip-ca-addsection": "Йаңа пүлек ҡылғалы", + "tooltip-ca-viewsource": "Пы пит пашҡартыулартан йаҡланҡан, әммә сес аның төп текстын ҡарай аласыс, копиясын ала аласыс", + "tooltip-ca-history": "Питне пашҡартыу күцермәлеге", + "tooltip-ca-protect": "Питне пашҡартыулартан саҡлағалы", + "tooltip-ca-delete": "Пы питне йуҡ иткәле", + "tooltip-ca-move": "Питкә йаңа исем пиргәле", + "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-currentevents": "Ҡәсер пулып йатҡан пулған ҡәлләр турлы пелешмә", + "tooltip-n-recentchanges": "Суңҡы төсәтеүләрнең күцермәлеге", + "tooltip-n-randompage": "Көтөлмәгән питне ҡарап паҡҡалы", + "tooltip-n-help": "Пелешмәне алырлыҡ урын", + "tooltip-t-whatlinkshere": "Пы питкә тапшырған пөтөн питләрнең күцермәлеге", + "tooltip-t-recentchangeslinked": "Пы питтән тапшырылған питләрнең суңҡы пашҡартыулары", + "tooltip-feed-atom": "Пы пит өцөн Atom-ҡа трансляция", + "tooltip-t-contributions": "{{GENDER:$1|Пы ҡулланыуцы ҡылған}} пашҡартыуларның күцермәлеге", + "tooltip-t-emailuser": "{{GENDER:$1|пы ҡулланыуцыға}} ҡат йебәргәле", + "tooltip-t-upload": "Файлларны төйәгәле", + "tooltip-t-specialpages": "Ҡесмәт питләрнең күцермәлеге", + "tooltip-t-print": "Пы питнең пасма аңышы", + "tooltip-t-permalink": "Пы питнең версиясына тапшырған келәң ссылка", + "tooltip-ca-nstab-main": "Мәҡәләнеңке эцтәлеге", + "tooltip-ca-nstab-user": "Ҡулланыуцыныңҡы үсенең пите", + "tooltip-ca-nstab-special": "Пы ҡесмәт пит, аны төсәткәле пулмайты", + "tooltip-ca-nstab-project": "Проектның пите", + "tooltip-ca-nstab-image": "Файлның пите", + "tooltip-ca-nstab-mediawiki": "MediaWiki-ның ҡәбәрләре пите", + "tooltip-ca-nstab-template": "Ҡалыпның пите", + "tooltip-ca-nstab-category": "Категорияның пите", + "tooltip-minoredit": "Пы пашҡартыуны пайтаҡ түгел итеп пилгеләгәле", + "tooltip-save": "Пашҡартыуларығысны ҡалтырғалы", + "tooltip-preview": "Питне алттан ҡарап цығыу; төсәтеүләрегесне ҡалтырыр алттан ҡулланың!", + "tooltip-diff": "Текстта эшләнкән үсегеснең пашҡартыуларығысны генә күргәскәле", + "tooltip-compareselectedversions": "Пы питнең ике сайланҡан аңышылары арасынтағы айырманы ҡарағалы.", + "tooltip-watch": "Пы питне көсәтеү күцермәлегемә өстәгәле", + "tooltip-rollback": "Пер пасыу пелән суңҡы пашҡартыуларны алып уйғалы", + "tooltip-undo": "Пулмайын итеүнең сәбәбен күргәсә алыу мөмкинцелеге пелән кергеселгән төсәтеүне уйып ташлағалы.", + "tooltip-summary": "Ҡысҡаца сүрәтләмә кергесең", + "simpleantispam-label": "Спамҡа ҡаршы тикшереү. Мыны ТУЛТЫРМАҢ!", + "pageinfo-title": "«$1» турлы ҡәбәр-пелем", + "pageinfo-header-basic": "Төп ҡәбәр-пелемнәр", + "pageinfo-header-edits": "Төсәтеүләр тариғы", + "pageinfo-header-restrictions": "Питне саҡлау", + "pageinfo-header-properties": "Питнең үслекләре", + "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-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": "Суңҡы төсәткер", + "pageinfo-lasttime": "Суңҡы төсәтеүнең датасы", + "pageinfo-edits": "Пөттөрә төсәтеүләр исәбе", + "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": "\tПит турлы ҡәбәр-пелемнәр", + "pageinfo-contentpage": "Эцтәлекле пит тип исәпләнәте", + "pageinfo-contentpage-yes": "Әйә", + "patrol-log-page": "Тикшереү журнал", + "previousdiff": "← Алттағы төсәтеү", + "nextdiff": "Мынан суңҡы төсәтеү →", + "widthheightpage": "$1 × $2, $3 {{PLURAL:$3 пит}}", + "file-info-size": "$1 × $2 {{PLURAL:$2|пиксель}}, файлның сурлығы: $3, MIME төр: $4", + "file-info-size-pages": "$1 × $2 пиксель, файл үлцәме: $3, MIME-тибы: $4, $5 {{PLURAL:$5|пит|питләр}}", + "file-nohires": "Йуғары ацыҡлыҡлы аңыш йуҡ", + "svg-long-desc": "SVG-файл, номиналь $1 × $2 {{PLURAL:$2|пиксель|}}, файлның сурлығы: $3", + "show-big-image": "Пашлапҡы файл", + "show-big-image-preview": "Алттан ҡарау уаҡыттағы сурлыҡ: $1.", + "show-big-image-other": "{{PLURAL:$2|1=Пашҡа сурлыҡ|Пашҡа сурлыҡлар}}: $1.", + "show-big-image-size": "$1 × $2 пиксель", + "metadata": "Метапиремнәр", + "metadata-help": "Пы файлта тик цифралы камера йә сканер пелән өстәлгән мәғлүмәтләр пар. Әгәр пы файл ҡороу уаҡытынан суң алмашынҡан пулса, аныңҡы ҡәйбер параметрлары төрөс пулмасҡа мөмкин.", + "metadata-fields": "Пы күцермәлеккә кергән метапиремнәрнең ҡырлары сүрәт питтә күрсәтелер. Ҡалғаннары килешеү пуйынца йәшерелер. * make * model * datetimeoriginal * exposuretime * fnumber * isospeedratings * focallength * artist * copyright * imagedescription * gpslatitude * gpslongitude * gpsaltitude", + "exif-orientation": "Кадрныңҡы утысылыуы", + "exif-xresolution": "Горизонталь сурлыҡ", + "exif-yresolution": "Вертикаль сурлыҡ", + "exif-datetime": "Файлны пашҡартыу көнө пелән уаҡыты", + "exif-make": "Камераны йасауцы", + "exif-model": "Камераның төрө", + "exif-software": "Ҡулланҡан программа", + "exif-exifversion": "Exif-ныңҡы версиясы", + "exif-colorspace": "Төсләрнең киңнеге", + "exif-datetimeoriginal": "Цын көнө пелән уаҡыты", + "exif-datetimedigitized": "Саннаштырыуның көнө пелән уаҡыты", + "exif-orientation-1": "Нормаль", + "namespacesall": "парысы", + "monthsall": "пөттөрә", + "imgmultipagenext": "алттағы пит →", + "imgmultigo": "Күцкәле!", + "imgmultigoto": "$1 питкә күцкәле", + "watchlisttools-clear": "Төсәтеү күцермәлекне тасартҡалы", + "watchlisttools-view": "Күцермәлектәге питләртә пашҡарыулар", + "watchlisttools-edit": "Күцермәлекне ҡарағалы та төсәткәле", + "watchlisttools-raw": "Тик текст итеп төсәтеү", + "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|уйлап ҡарау]])", + "redirect": "Файлның аңныштырғыцынан, ҡатнашыуцытан, питтән, версиятан йә журналтан йусыҡлау", + "redirect-summary": "Пы ҡесмәт пит файлға (файлның исеменән), питкә (версияның аңныштырғыцынан йә питенән), ҡатнашыуцының питен (ҡатнашыуцының исәп аңныштырғыцынан) йә ҡатнашыуцының питенә (аңныштырғыцның журналынан) йебәрәте.", + "redirect-submit": "Күцкәле", + "redirect-lookup": "Эстәү:", + "redirect-value": "Мәғнә:", + "redirect-user": "Ҡатнашыуцының аңыштырғыцы", + "redirect-page": "Питнең аңныштырғыцы", + "redirect-revision": "Питнең аңышы", + "redirect-file": "Файлның исеме", + "specialpages": "Атайы питләр", + "tag-filter": "[[Special:Tags|Пилгеләр]] фильтры:", + "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Тамғалар}}]]: $2)", + "tags-active-yes": "Әйә", + "tags-active-no": "Йуҡ", + "tags-hitcount": "$1 {{PLURAL:$1|1=пашҡартыу|пашҡартыулар|пашҡартыуларны}}", + "logentry-delete-delete": "$1 $3 питне {{GENDER:$2|пөтөрөп ҡуйты}}", + "logentry-delete-restore": "$1 $3 ($4) питен {{GENDER:$2|йаңатан торғосто}}", + "logentry-delete-revision": "Ҡатнашыуцы $1 $3: $4 питенең {{PLURAL:$5|$5 версиялары|$5 версияларының|1=версиялар}} күренешен {{GENDER:$2|пашҡартты|пашҡартты}}.", + "revdelete-content-hid": "эцтәлеге йәшерелшән", + "logentry-move-move": "$1 $3 питнең исемен {{GENDER:$2| алмаштырты}}. Йаңа исеме: $4", + "logentry-move-move-noredirect": "$1 йусыҡ ҡалтырмайын $3 питне $4 итеп йаңа исем пирте", + "logentry-move-move_redir": "$1 йусыҡлап $3 питне $4 итеп күцерте", + "logentry-patrol-patrol-auto": "$1 $3 питнең $4 версиясын автоматиклап {{GENDER:$2|тикшерте}}", + "logentry-newusers-create": "{{GENDER:$2|ҡатнашыуцы}} $1 исәп йасманы ҡылты.", + "logentry-newusers-autocreate": "Автоматик килеш {{GENDER:$2| ҡатнашыуцының}} $1 исәп йасмасы ҡылынты", + "logentry-upload-upload": "$1 $3 {{GENDER:$2|төйәте}}", + "logentry-upload-overwrite": "$1 йаңа аңыш{{GENDER:$2||тейәте}} $3", + "searchsuggest-search": "{{SITENAME}}та эстәгәле‎", + "duration-days": "$1 {{PLURAL:$1|көн}}", + "randomrootpage": "Көтөлмәгән тамыр пит" +} diff --git a/languages/i18n/tr.json b/languages/i18n/tr.json index a838dbfd2e..d0d8ceb5d4 100644 --- a/languages/i18n/tr.json +++ b/languages/i18n/tr.json @@ -539,7 +539,7 @@ "nosuchusershort": "\"$1\" adında bir kullanıcı bulunmamaktadır. Yazılışı kontrol edin.", "nouserspecified": "Bir kullanıcı adı belirtmek zorundasınız.", "login-userblocked": "Bu kullanıcı engellenmiş. Giriş yapmaya izin verilmiyor.", - "wrongpassword": "Parolayı yanlış girdiniz. Lütfen tekrar deneyiniz.", + "wrongpassword": "Hatalı kullanıcı adı ya da parola girildi. Lütfen tekrar deneyin.", "wrongpasswordempty": "Boş parola girdiniz. Lütfen tekrar deneyiniz.", "passwordtooshort": "Parolalar en az {{PLURAL:$1|1 karakter|$1 karakter}} uzunluğunda olmalı.", "passwordtoolong": "Parolalar $1 karakterden uzun olamaz.", @@ -1261,19 +1261,23 @@ "grant-editmywatchlist": "İzleme listeni düzenle", "grant-editprotected": "Korumalı sayfaları Düzenle", "grant-patrol": "Sayfadaki değişiklikleri incele", + "grant-sendemail": "Diğer kullanıcılara e-posta gönder", + "grant-uploadeditmovefile": "Dosya yükle, yenisiyle değiştir ve taşı", "grant-uploadfile": "Dosya yükle", "grant-basic": "Basit haklar", "grant-viewdeleted": "Silinen dosya ve sayfaları görüntüle", "grant-viewmywatchlist": "İzleme listeni gör", + "grant-viewrestrictedlogs": "Kısıtlanmış günlük girdilerini görüntüle", "newuserlogpage": "Kullanıcı oluşturma günlüğü", "newuserlogpagetext": "Bu bir kullanıcı oluşturma günlüğüdür.", "rightslog": "Kullanıcı hakları günlüğü", "rightslogtext": "Bu, kullanıcı hakları değişiklikleri için bir günlüktür.", "action-read": "bu sayfayı okumaya", "action-edit": "bu sayfayı değiştirmeye", - "action-createpage": "sayfa oluşturmaya", - "action-createtalk": "tartışma sayfası oluşturmaya", + "action-createpage": "sayfayı oluştur", + "action-createtalk": "tartışma sayfasını oluştur", "action-createaccount": "bu kullanıcı hesabını oluşturmaya", + "action-autocreateaccount": "bu harici kullanıcı hesabını otomatik olarak oluştur", "action-history": "sayfa geçmişini görüntüle", "action-minoredit": "bu değişikliği küçük olarak işaretlemeye", "action-move": "bu sayfayı taşımaya", @@ -1287,8 +1291,10 @@ "action-upload_by_url": "bir URL adresinden bu dosyayı yüklemeye", "action-writeapi": "API yaz kullanmaya", "action-delete": "bu sayfayı silmeye", - "action-deleterevision": "bu revizyonu silmeye", - "action-deletedhistory": "bu sayfanın silinme geçmişini görmeye", + "action-deleterevision": "revizyonları sil", + "action-deletelogentry": "günlük girdilerini sil", + "action-deletedhistory": "sayfanın silme geçmişini görüntüle", + "action-deletedtext": "silinmiş revizyon metnini görüntüle", "action-browsearchive": "silinen sayfaları aramaya", "action-undelete": "sayfaları geri getir", "action-suppressrevision": "gizli sürümleri gözden geçir ve geri getir", @@ -1338,8 +1344,11 @@ "rcfilters-activefilters": "Etkin süzgeçler", "rcfilters-advancedfilters": "Gelişmiş süzgeçler", "rcfilters-limit-title": "Gösterilecek sonuçlar", + "rcfilters-limit-and-date-label": "$1 değişiklik, $2", "rcfilters-days-title": "Son günler", "rcfilters-hours-title": "Son saatler", + "rcfilters-days-show-days": "$1 gün", + "rcfilters-days-show-hours": "$1 saat", "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.", @@ -1374,8 +1383,8 @@ "rcfilters-filtergroup-userExpLevel": "Deneyim düzeyi (yalnızca kayıtlı kullanıcılar için)", "rcfilters-filter-user-experience-level-registered-label": "Kayıtlı", "rcfilters-filter-user-experience-level-registered-description": "Oturum açmış editörler.", - "rcfilters-filter-user-experience-level-unregistered-label": "Kayıtsız", - "rcfilters-filter-user-experience-level-unregistered-description": "Oturum açmamış editörler.", + "rcfilters-filter-user-experience-level-unregistered-label": "Kayıtlı olmayan", + "rcfilters-filter-user-experience-level-unregistered-description": "Oturum açmamış kullanıcılar.", "rcfilters-filter-user-experience-level-newcomer-label": "Yeni gelenler", "rcfilters-filter-user-experience-level-newcomer-description": "10'dan az düzenlemesi veya 4 günden az etkinliği olan kayıtlı kullanıcılar.", "rcfilters-filter-user-experience-level-learner-label": "Öğreniciler", @@ -1397,6 +1406,9 @@ "rcfilters-filter-minor-description": "Yazarın küçük olarak etiketlediği düzenlemeler.", "rcfilters-filter-major-label": "Küçük olmayan düzenlemeler", "rcfilters-filter-major-description": "Küçük olarak etiketlenmemiş düzenlemeler.", + "rcfilters-filtergroup-watchlistactivity": "İzleme listesi faaliyetleri", + "rcfilters-filter-watchlistactivity-unseen-label": "Görülmemiş değişiklikler", + "rcfilters-filter-watchlistactivity-seen-label": "Görülmüş değişiklikler", "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...", diff --git a/maintenance/benchmarks/Benchmarker.php b/maintenance/benchmarks/Benchmarker.php index f1e7dbbdee..e1eef07e07 100644 --- a/maintenance/benchmarks/Benchmarker.php +++ b/maintenance/benchmarks/Benchmarker.php @@ -100,7 +100,7 @@ abstract class Benchmarker extends Maintenance { 'name' => $name, 'count' => $stat->getCount(), // Get rate per second from mean (in ms) - 'rate' => 1.0 / ( $stat->getMean() / 1000.0 ), + 'rate' => $stat->getMean() == 0 ? INF : ( 1.0 / ( $stat->getMean() / 1000.0 ) ), 'total' => $stat->getMean() * $stat->getCount(), 'mean' => $stat->getMean(), 'max' => $stat->max, diff --git a/maintenance/resources/update-oojs-ui.sh b/maintenance/resources/update-oojs-ui.sh index 799af4ca47..d1e6496dce 100755 --- a/maintenance/resources/update-oojs-ui.sh +++ b/maintenance/resources/update-oojs-ui.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu -# This script generates a commit that updates our copy of OOjs UI +# This script generates a commit that updates our copy of OOUI if [ -n "${2:-}" ] then @@ -20,7 +20,7 @@ git checkout composer.json git reset -- $TARGET_DIR git checkout -- $TARGET_DIR git fetch origin -git checkout -B upstream-oojs-ui origin/master +git checkout -B upstream-ooui origin/master # Fetch upstream version cd $NPM_DIR @@ -31,10 +31,10 @@ else npm install oojs-ui fi -OOJSUI_VERSION=$(node -e 'console.log(require("./node_modules/oojs-ui/package.json").version);') -if [ "$OOJSUI_VERSION" == "" ] +OOUI_VERSION=$(node -e 'console.log(require("./node_modules/oojs-ui/package.json").version);') +if [ "$OOUI_VERSION" == "" ] then - echo 'Could not find OOjs UI version' + echo 'Could not find OOUI version' exit 1 fi @@ -68,15 +68,15 @@ rm -rf "$NPM_DIR" cd $REPO_DIR COMMITMSG=$(cat <assertSame( 401, $request->getStatus() ); } + public function testFactoryDefaults() { + $request = MWHttpRequest::factory( 'http://acme.test' ); + $this->assertInstanceOf( MWHttpRequest::class, $request ); + } + // -------------------- /** @@ -242,4 +247,5 @@ abstract class MWHttpRequestTestCase extends PHPUnit_Framework_TestCase { $this->assertArrayNotHasKey( strtolower( $name ), array_change_key_case( $cookieJar->cookie, CASE_LOWER ) ); } + } diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php index 9b5897c89e..4dd4bc67d2 100644 --- a/tests/parser/ParserTestRunner.php +++ b/tests/parser/ParserTestRunner.php @@ -811,10 +811,6 @@ class ParserTestRunner { $options = ParserOptions::newFromContext( $context ); $options->setTimestamp( $this->getFakeTimestamp() ); - if ( !isset( $opts['wrap'] ) ) { - $options->setWrapOutputClass( false ); - } - if ( isset( $opts['tidy'] ) ) { if ( !$this->tidySupport->isEnabled() ) { $this->recorder->skipped( $test, 'tidy extension is not installed' ); @@ -854,7 +850,8 @@ class ParserTestRunner { } else { $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 ); $out = $output->getText( [ - 'allowTOC' => !isset( $opts['notoc'] ) + 'allowTOC' => !isset( $opts['notoc'] ), + 'unwrap' => !isset( $opts['wrap'] ), ] ); if ( isset( $opts['tidy'] ) ) { $out = preg_replace( '/\s+$/', '', $out ); diff --git a/tests/phpunit/includes/ExtraParserTest.php b/tests/phpunit/includes/ExtraParserTest.php index aaa135d8a0..75ebd31a21 100644 --- a/tests/phpunit/includes/ExtraParserTest.php +++ b/tests/phpunit/includes/ExtraParserTest.php @@ -26,7 +26,6 @@ class ExtraParserTest extends MediaWikiTestCase { // FIXME: This test should pass without setting global content language $this->options = ParserOptions::newFromUserAndLang( new User, $contLang ); $this->options->setTemplateCallback( [ __CLASS__, 'statelessFetchTemplate' ] ); - $this->options->setWrapOutputClass( false ); $this->parser = new Parser; MagicWord::clearCache(); @@ -41,9 +40,8 @@ class ExtraParserTest extends MediaWikiTestCase { $title = Title::newFromText( 'Unit test' ); $options = ParserOptions::newFromUser( new User() ); - $options->setWrapOutputClass( false ); $this->assertEquals( "

$longLine

", - $this->parser->parse( $longLine, $title, $options )->getText() ); + $this->parser->parse( $longLine, $title, $options )->getText( [ 'unwrap' => true ] ) ); } /** @@ -55,7 +53,7 @@ class ExtraParserTest extends MediaWikiTestCase { $parserOutput = $this->parser->parse( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options ); $this->assertEquals( "

Test\nContent of Template:Foo\nContent of Template:Bar\n

", - $parserOutput->getText() + $parserOutput->getText( [ 'unwrap' => true ] ) ); } diff --git a/tests/phpunit/includes/deferred/MWCallableUpdateTest.php b/tests/phpunit/includes/deferred/MWCallableUpdateTest.php index 088ab4f544..6977aef1a2 100644 --- a/tests/phpunit/includes/deferred/MWCallableUpdateTest.php +++ b/tests/phpunit/includes/deferred/MWCallableUpdateTest.php @@ -29,8 +29,54 @@ class MWCallableUpdateTest extends PHPUnit_Framework_TestCase { // Emulate rollback $db->rollback( __METHOD__ ); + $update->doUpdate(); + + // Ensure it was cancelled + $this->assertSame( 0, $ran ); + } + + public function testCancelSome() { + // Prepare update and DB + $db1 = new DatabaseTestHelper( __METHOD__ ); + $db1->begin( __METHOD__ ); + $db2 = new DatabaseTestHelper( __METHOD__ ); + $db2->begin( __METHOD__ ); + $ran = 0; + $update = new MWCallableUpdate( function () use ( &$ran ) { + $ran++; + }, __METHOD__, [ $db1, $db2 ] ); + + // Emulate rollback + $db1->rollback( __METHOD__ ); + + $update->doUpdate(); + + // Prevents: "Notice: DB transaction writes or callbacks still pending" + $db2->rollback( __METHOD__ ); + // Ensure it was cancelled + $this->assertSame( 0, $ran ); + } + + public function testCancelAll() { + // Prepare update and DB + $db1 = new DatabaseTestHelper( __METHOD__ ); + $db1->begin( __METHOD__ ); + $db2 = new DatabaseTestHelper( __METHOD__ ); + $db2->begin( __METHOD__ ); + $ran = 0; + $update = new MWCallableUpdate( function () use ( &$ran ) { + $ran++; + }, __METHOD__, [ $db1, $db2 ] ); + + // Emulate rollbacks + $db1->rollback( __METHOD__ ); + $db2->rollback( __METHOD__ ); + $update->doUpdate(); + + // Ensure it was cancelled $this->assertSame( 0, $ran ); } + } diff --git a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php index a2c3bdcdbb..ca78f6500c 100644 --- a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php +++ b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php @@ -1481,7 +1481,10 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase { $wanCache = new WANObjectCache( [ 'cache' => $localBag, 'pool' => 'testcache-hash', - 'relayer' => new EventRelayerNull( [] ) + 'relayer' => new EventRelayerNull( [] ), + 'mcrouterAware' => true, + 'region' => 'pmtpa', + 'cluster' => 'mw-wan' ] ); $valFunc = function () { return 1; @@ -1498,6 +1501,60 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase { $wanCache->reap( 'zzz', time() - 300 ); } + public function testMcRouterSupportBroadcastDelete() { + $localBag = $this->getMockBuilder( EmptyBagOStuff::class ) + ->setMethods( [ 'set' ] )->getMock(); + $wanCache = new WANObjectCache( [ + 'cache' => $localBag, + 'pool' => 'testcache-hash', + 'relayer' => new EventRelayerNull( [] ), + 'mcrouterAware' => true, + 'region' => 'pmtpa', + 'cluster' => 'mw-wan' + ] ); + + $localBag->expects( $this->once() )->method( 'set' ) + ->with( "/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX . "test" ); + + $wanCache->delete( 'test' ); + } + + public function testMcRouterSupportBroadcastTouchCK() { + $localBag = $this->getMockBuilder( EmptyBagOStuff::class ) + ->setMethods( [ 'set' ] )->getMock(); + $wanCache = new WANObjectCache( [ + 'cache' => $localBag, + 'pool' => 'testcache-hash', + 'relayer' => new EventRelayerNull( [] ), + 'mcrouterAware' => true, + 'region' => 'pmtpa', + 'cluster' => 'mw-wan' + ] ); + + $localBag->expects( $this->once() )->method( 'set' ) + ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" ); + + $wanCache->touchCheckKey( 'test' ); + } + + public function testMcRouterSupportBroadcastResetCK() { + $localBag = $this->getMockBuilder( EmptyBagOStuff::class ) + ->setMethods( [ 'delete' ] )->getMock(); + $wanCache = new WANObjectCache( [ + 'cache' => $localBag, + 'pool' => 'testcache-hash', + 'relayer' => new EventRelayerNull( [] ), + 'mcrouterAware' => true, + 'region' => 'pmtpa', + 'cluster' => 'mw-wan' + ] ); + + $localBag->expects( $this->once() )->method( 'delete' ) + ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" ); + + $wanCache->resetCheckKey( 'test' ); + } + /** * @dataProvider provideAdaptiveTTL * @covers WANObjectCache::adaptiveTTL() diff --git a/tests/phpunit/includes/parser/ParserOptionsTest.php b/tests/phpunit/includes/parser/ParserOptionsTest.php index 93ab35c424..232b0bb141 100644 --- a/tests/phpunit/includes/parser/ParserOptionsTest.php +++ b/tests/phpunit/includes/parser/ParserOptionsTest.php @@ -62,7 +62,7 @@ class ParserOptionsTest extends MediaWikiTestCase { 'No overrides' => [ true, [] ], 'In-key options are ok' => [ true, [ 'thumbsize' => 1e100, - 'wrapclass' => false, + 'printable' => false, ] ], 'Non-in-key options are not ok' => [ false, [ 'removeComments' => false, @@ -102,7 +102,7 @@ class ParserOptionsTest extends MediaWikiTestCase { } public static function provideOptionsHash() { - $used = [ 'wrapclass', 'printable' ]; + $used = [ 'thumbsize', 'printable' ]; $classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class ); $classWrapper->getDefaults(); @@ -116,9 +116,9 @@ class ParserOptionsTest extends MediaWikiTestCase { 'Canonical options, used some options' => [ $used, 'canonical', [] ], 'Used some options, non-default values' => [ $used, - 'printable=1!wrapclass=foobar', + 'printable=1!thumbsize=200', [ - 'wrapclass' => 'foobar', + 'thumbsize' => 200, 'printable' => true, ] ], diff --git a/tests/phpunit/includes/parser/ParserOutputTest.php b/tests/phpunit/includes/parser/ParserOutputTest.php index 9642bbc0fb..efcc4e079e 100644 --- a/tests/phpunit/includes/parser/ParserOutputTest.php +++ b/tests/phpunit/includes/parser/ParserOutputTest.php @@ -105,6 +105,8 @@ class ParserOutputTest extends MediaWikiTestCase { 'wgScriptPath' => '/w', 'wgScript' => '/w/index.php', ] ); + $this->hideDeprecated( 'ParserOutput stateful allowTOC' ); + $this->hideDeprecated( 'ParserOutput stateful enableSectionEditLinks' ); $po = new ParserOutput( $text ); @@ -125,7 +127,7 @@ class ParserOutputTest extends MediaWikiTestCase { public static function provideGetText() { // phpcs:disable Generic.Files.LineLength $text = <<Test document. +

Test document.

Contents

    @@ -150,13 +152,13 @@ class ParserOutputTest extends MediaWikiTestCase {

    Section 3Section 3

    Three -

    +

EOF; return [ 'No stateless options, default state' => [ [], [], $text, <<Test document. +

Test document.

EOF ], 'No stateless options, TOC statefully disabled' => [ [], [ 'mTOCEnabled' => false ], $text, <<Test document. +

Test document.

Section 1[edit]

@@ -200,12 +202,12 @@ EOF

Section 3[edit]

Three -

+

EOF ], 'No stateless options, section edits statefully disabled' => [ [], [ 'mEditSectionTokens' => false ], $text, <<Test document. +

Test document.

Contents

    @@ -230,14 +232,14 @@ EOF

    Section 3

    Three -

    +

EOF ], 'Stateless options override stateful settings' => [ [ 'allowTOC' => true, 'enableSectionEditLinks' => true ], [ 'mTOCEnabled' => false, 'mEditSectionTokens' => false ], $text, <<Test document. +

Test document.

Contents

    @@ -262,12 +264,12 @@ EOF

    Section 3[edit]

    Three -

    +

EOF ], 'Statelessly disable section edit links' => [ [ 'enableSectionEditLinks' => false ], [], $text, <<Test document. +

Test document.

Contents

    @@ -292,13 +294,43 @@ EOF

    Section 3

    Three -

    +

EOF ], 'Statelessly disable TOC' => [ [ 'allowTOC' => false ], [], $text, <<

Test document. +

+ +

Section 1[edit]

+

One +

+

Section 2[edit]

+

Two +

+

Section 2.1[edit]

+

Two point one +

+

Section 3[edit]

+

Three +

+EOF + ], + 'Statelessly unwrap text' => [ + [ 'unwrap' => true ], [], $text, <<Test document.

+

Section 1[edit]

One @@ -314,6 +346,9 @@ EOF

EOF ], + 'Unwrap without a mw-parser-output wrapper' => [ + [ 'unwrap' => true ], [], '
Content
', '
Content
' + ], ]; // phpcs:enable } diff --git a/tests/phpunit/includes/parser/TagHooksTest.php b/tests/phpunit/includes/parser/TagHooksTest.php index 7e31cba60f..2fdaa1892f 100644 --- a/tests/phpunit/includes/parser/TagHooksTest.php +++ b/tests/phpunit/includes/parser/TagHooksTest.php @@ -46,7 +46,6 @@ class TagHooksTest extends MediaWikiTestCase { private function getParserOptions() { global $wgContLang; $popt = ParserOptions::newFromUserAndLang( new User, $wgContLang ); - $popt->setWrapOutputClass( false ); return $popt; } @@ -63,7 +62,7 @@ class TagHooksTest extends MediaWikiTestCase { Title::newFromText( 'Test' ), $this->getParserOptions() ); - $this->assertEquals( "

FooOneBaz\n

", $parserOutput->getText() ); + $this->assertEquals( "

FooOneBaz\n

", $parserOutput->getText( [ 'unwrap' => true ] ) ); $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle } @@ -98,7 +97,7 @@ class TagHooksTest extends MediaWikiTestCase { Title::newFromText( 'Test' ), $this->getParserOptions() ); - $this->assertEquals( "

FooOneBaz\n

", $parserOutput->getText() ); + $this->assertEquals( "

FooOneBaz\n

", $parserOutput->getText( [ 'unwrap' => true ] ) ); $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle } diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js index 59432945c2..06c146c298 100644 --- a/tests/qunit/data/testrunner.js +++ b/tests/qunit/data/testrunner.js @@ -6,18 +6,21 @@ /** * Make a safe copy of localEnv: - * - Creates a copy so that when the same object reference to module hooks is - * used by multipe test hooks, our QUnit.module extension will not wrap the - * callbacks multiple times. Instead, they wrap using a new object. - * - Normalise setup/teardown to avoid having to repeat this in each extension + * - Creates a new object that inherits, instead of modifying the original. + * This prevents recursion in the event that a test suite stores inherits + * hooks object statically and passes it to multiple QUnit.module() calls. + * - Supporting QUnit 1.x 'setup' and 'teardown' hooks * (deprecated in QUnit 1.16, removed in QUnit 2). - * - Strip any other properties. */ function makeSafeEnv( localEnv ) { - return { - beforeEach: localEnv.setup || localEnv.beforeEach, - afterEach: localEnv.teardown || localEnv.afterEach - }; + var wrap = localEnv ? Object.create( localEnv ) : {}; + if ( wrap.setup ) { + wrap.beforeEach = wrap.beforeEach || wrap.setup; + } + if ( wrap.teardown ) { + wrap.afterEach = wrap.afterEach || wrap.teardown; + } + return wrap; } /** @@ -73,13 +76,16 @@ useFakeTimers: false, useFakeServer: false }; - // Extend QUnit.module to provide a Sinon sandbox. + // Extend QUnit.module with: + // - Add support for QUnit 1.x 'setup' and 'teardown' hooks + // - Add a Sinon sandbox to the test context. + // - Add a test fixture to the test context. ( function () { var orgModule = QUnit.module; QUnit.module = function ( name, localEnv, executeNow ) { - var orgBeforeEach, orgAfterEach, orgExecute; + var orgExecute, orgBeforeEach, orgAfterEach; if ( nested ) { - // In a nested module, don't re-run our handlers. + // In a nested module, don't re-add our hooks, QUnit does that already. return orgModule.apply( this, arguments ); } if ( arguments.length === 2 && typeof localEnv === 'function' ) { @@ -98,49 +104,17 @@ }; } - localEnv = localEnv || {}; + localEnv = makeSafeEnv( localEnv ); orgBeforeEach = localEnv.beforeEach; orgAfterEach = localEnv.afterEach; + localEnv.beforeEach = function () { + // Sinon sandbox var config = sinon.getConfig( sinon.config ); config.injectInto = this; sinon.sandbox.create( config ); - if ( orgBeforeEach ) { - return orgBeforeEach.apply( this, arguments ); - } - }; - localEnv.afterEach = function () { - var ret; - if ( orgAfterEach ) { - ret = orgAfterEach.apply( this, arguments ); - } - - this.sandbox.verifyAndRestore(); - return ret; - }; - return orgModule( name, localEnv, executeNow ); - }; - }() ); - - // Extend QUnit.module to provide a fixture element. - ( function () { - var orgModule = QUnit.module; - QUnit.module = function ( name, localEnv, executeNow ) { - var orgBeforeEach, orgAfterEach; - if ( nested ) { - // In a nested module, don't re-run our handlers. - return orgModule.apply( this, arguments ); - } - if ( arguments.length === 2 && typeof localEnv === 'function' ) { - executeNow = localEnv; - localEnv = undefined; - } - - localEnv = localEnv || {}; - orgBeforeEach = localEnv.beforeEach; - orgAfterEach = localEnv.afterEach; - localEnv.beforeEach = function () { + // Fixture element this.fixture = document.createElement( 'div' ); this.fixture.id = 'qunit-fixture'; document.body.appendChild( this.fixture ); @@ -154,23 +128,11 @@ if ( orgAfterEach ) { ret = orgAfterEach.apply( this, arguments ); } - + this.sandbox.verifyAndRestore(); this.fixture.parentNode.removeChild( this.fixture ); return ret; }; - return orgModule( name, localEnv, executeNow ); - }; - }() ); - // Extend QUnit.module to normalise localEnv. - // NOTE: This MUST be the last QUnit.module extension so that the above extensions - // may safely modify the object and assume beforeEach/afterEach. - ( function () { - var orgModule = QUnit.module; - QUnit.module = function ( name, localEnv, executeNow ) { - if ( typeof localEnv === 'object' ) { - localEnv = makeSafeEnv( localEnv ); - } return orgModule( name, localEnv, executeNow ); }; }() ); @@ -239,98 +201,101 @@ } return function ( orgEnv ) { - var localEnv = orgEnv ? makeSafeEnv( orgEnv ) : {}; - // MediaWiki env testing - localEnv.config = orgEnv && orgEnv.config || {}; - localEnv.messages = orgEnv && orgEnv.messages || {}; - - return { - beforeEach: function () { - // Greetings, mock environment! - mw.config = new MwMap(); - mw.config.set( freshConfigCopy( localEnv.config ) ); - mw.messages = new MwMap(); - mw.messages.set( freshMessagesCopy( localEnv.messages ) ); - // Update reference to mw.messages - mw.jqueryMsg.setParserDefaults( { - messages: mw.messages - } ); - - this.suppressWarnings = suppressWarnings; - this.restoreWarnings = restoreWarnings; + var localEnv, orgBeforeEach, orgAfterEach; - // Start tracking ajax requests - $( document ).on( 'ajaxSend', trackAjax ); - - if ( localEnv.beforeEach ) { - return localEnv.beforeEach.apply( this, arguments ); - } - }, + localEnv = makeSafeEnv( orgEnv ); + // MediaWiki env testing + localEnv.config = localEnv.config || {}; + localEnv.messages = localEnv.messages || {}; - afterEach: function () { - var timers, pending, $activeLen, ret; + orgBeforeEach = localEnv.beforeEach; + orgAfterEach = localEnv.afterEach; - if ( localEnv.afterEach ) { - ret = localEnv.afterEach.apply( this, arguments ); - } + localEnv.beforeEach = function () { + // Greetings, mock environment! + mw.config = new MwMap(); + mw.config.set( freshConfigCopy( localEnv.config ) ); + mw.messages = new MwMap(); + mw.messages.set( freshMessagesCopy( localEnv.messages ) ); + // Update reference to mw.messages + mw.jqueryMsg.setParserDefaults( { + messages: mw.messages + } ); + + this.suppressWarnings = suppressWarnings; + this.restoreWarnings = restoreWarnings; + + // Start tracking ajax requests + $( document ).on( 'ajaxSend', trackAjax ); - // Stop tracking ajax requests - $( document ).off( 'ajaxSend', trackAjax ); + if ( orgBeforeEach ) { + return orgBeforeEach.apply( this, arguments ); + } + }; + localEnv.afterEach = function () { + var timers, pending, $activeLen, ret; - // As a convenience feature, automatically restore warnings if they're - // still suppressed by the end of the test. - restoreWarnings(); + if ( orgAfterEach ) { + ret = orgAfterEach.apply( this, arguments ); + } - // Farewell, mock environment! - mw.config = liveConfig; - mw.messages = liveMessages; - // Restore reference to mw.messages - mw.jqueryMsg.setParserDefaults( { - messages: liveMessages + // Stop tracking ajax requests + $( document ).off( 'ajaxSend', trackAjax ); + + // As a convenience feature, automatically restore warnings if they're + // still suppressed by the end of the test. + restoreWarnings(); + + // Farewell, mock environment! + mw.config = liveConfig; + mw.messages = liveMessages; + // Restore reference to mw.messages + mw.jqueryMsg.setParserDefaults( { + messages: liveMessages + } ); + + // Tests should use fake timers or wait for animations to complete + // Check for incomplete animations/requests/etc and throw if there are any. + if ( $.timers && $.timers.length !== 0 ) { + timers = $.timers.length; + $.each( $.timers, function ( i, timer ) { + var node = timer.elem; + mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' + + mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() ) + ); } ); + // Force animations to stop to give the next test a clean start + $.timers = []; + $.fx.stop(); - // Tests should use fake timers or wait for animations to complete - // Check for incomplete animations/requests/etc and throw if there are any. - if ( $.timers && $.timers.length !== 0 ) { - timers = $.timers.length; - $.each( $.timers, function ( i, timer ) { - var node = timer.elem; - mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' + - mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() ) - ); - } ); - // Force animations to stop to give the next test a clean start - $.timers = []; - $.fx.stop(); - - throw new Error( 'Unfinished animations: ' + timers ); - } + throw new Error( 'Unfinished animations: ' + timers ); + } - // Test should use fake XHR, wait for requests, or call abort() - $activeLen = $.active; - if ( $activeLen !== undefined && $activeLen !== 0 ) { - pending = ajaxRequests.filter( function ( ajax ) { - return ajax.xhr.state() === 'pending'; - } ); - if ( pending.length !== $activeLen ) { - mw.log.warn( 'Pending requests does not match jQuery.active count' ); - } - // Force requests to stop to give the next test a clean start - ajaxRequests.forEach( function ( ajax, i ) { - mw.log.warn( - 'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')', - ajax.options - ); - ajax.xhr.abort(); - } ); - ajaxRequests = []; - - throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' ); + // Test should use fake XHR, wait for requests, or call abort() + $activeLen = $.active; + if ( $activeLen !== undefined && $activeLen !== 0 ) { + pending = ajaxRequests.filter( function ( ajax ) { + return ajax.xhr.state() === 'pending'; + } ); + if ( pending.length !== $activeLen ) { + mw.log.warn( 'Pending requests does not match jQuery.active count' ); } + // Force requests to stop to give the next test a clean start + ajaxRequests.forEach( function ( ajax, i ) { + mw.log.warn( + 'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')', + ajax.options + ); + ajax.xhr.abort(); + } ); + ajaxRequests = []; - return ret; + throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' ); } + + return ret; }; + return localEnv; }; }() ); @@ -657,4 +622,31 @@ } ); } ); + QUnit.module( 'testrunner-hooks-outer', function () { + var beforeHookWasExecuted = false, + afterHookWasExecuted = false; + QUnit.module( 'testrunner-hooks', { + before: function () { + beforeHookWasExecuted = true; + + // This way we can be sure that module `testrunner-hook-after` will always + // be executed after module `testrunner-hooks` + QUnit.module( 'testrunner-hooks-after' ); + QUnit.test( + '`after` hook for module `testrunner-hooks` was executed', + function ( assert ) { + assert.ok( afterHookWasExecuted ); + } + ); + }, + after: function () { + afterHookWasExecuted = true; + } + } ); + + QUnit.test( '`before` hook was executed', function ( assert ) { + assert.ok( beforeHookWasExecuted ); + } ); + } ); + }( jQuery, mediaWiki, QUnit ) ); diff --git a/thumb.php b/thumb.php index 02ac0b024d..c4b40dc76f 100644 --- a/thumb.php +++ b/thumb.php @@ -337,7 +337,16 @@ function wfStreamThumb( array $params ) { return; } - list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath ); + $thumbProxyUrl = $img->getRepo()->getThumbProxyUrl(); + + if ( strlen( $thumbProxyUrl ) ) { + wfProxyThumbnailRequest( $img, $thumbName ); + // No local fallback when in proxy mode + return; + } else { + // Generate the thumbnail locally + list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath ); + } /** @var MediaTransformOutput|MediaTransformError|bool $thumb */ @@ -377,6 +386,43 @@ function wfStreamThumb( array $params ) { } } +/** + * Proxies thumbnail request to a service that handles thumbnailing + * + * @param File $img + * @param string $thumbName + */ +function wfProxyThumbnailRequest( $img, $thumbName ) { + $thumbProxyUrl = $img->getRepo()->getThumbProxyUrl(); + + // Instead of generating the thumbnail ourselves, we proxy the request to another service + $thumbProxiedUrl = $thumbProxyUrl . $img->getThumbRel( $thumbName ); + + $req = MWHttpRequest::factory( $thumbProxiedUrl ); + $secret = $img->getRepo()->getThumbProxySecret(); + + // Pass a secret key shared with the proxied service if any + if ( strlen( $secret ) ) { + $req->setHeader( 'X-Swift-Secret', $secret ); + } + + // Send request to proxied service + $status = $req->execute(); + + // Simply serve the response from the proxied service as-is + header( 'HTTP/1.1 ' . $req->getStatus() ); + + $headers = $req->getResponseHeaders(); + + foreach ( $headers as $key => $values ) { + foreach ( $values as $value ) { + header( $key . ': ' . $value, false ); + } + } + + echo $req->getContent(); +} + /** * Actually try to generate a new thumbnail *