Merge "Fix undefined $param"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 16 Dec 2016 00:20:45 +0000 (00:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 16 Dec 2016 00:20:45 +0000 (00:20 +0000)
27 files changed:
includes/api/i18n/cs.json
includes/api/i18n/de.json
includes/api/i18n/fr.json
includes/api/i18n/ko.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/zh-hans.json
includes/cache/MessageCache.php
includes/page/WikiPage.php
includes/parser/Preprocessor.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/search/DummySearchIndexFieldDefinition.php
languages/i18n/be-tarask.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/ka.json
languages/i18n/lt.json
languages/i18n/nah.json
languages/i18n/ro.json
languages/i18n/sl.json
languages/i18n/tt-cyrl.json
maintenance/backup.inc
tests/parser/parserTests.txt
tests/phan/config.php

index cf50f90..e8fd165 100644 (file)
@@ -26,7 +26,7 @@
        "apihelp-main-param-origin": "Pokud k API přistupujete pomocí mezidoménového AJAXového požadavku (CORS), nastavte tento parametr na doménu původu. Musí být součástí všech předběžných požadavků, takže musí být součástí URI požadavku (nikoli těla POSTu).\n\nU autentizovaných požadavků hodnota musí přesně odpovídat jednomu z původů v hlavičce <code>Origin</code>, takže musí být nastavena na něco jako <kbd>https://en.wikipedia.org</kbd> nebo <kbd>https://meta.wikimedia.org</kbd>. Pokud parametr neodpovídá hlavičce <code>Origin</code>, bude vrácena odpověď 403. Pokud parametr odpovídá hlavičce <code>Origin</code> a tento původ je na bílé listině, budou nastaveny hlavičky <code>Access-Control-Allow-Origin</code> a <code>Access-Control-Allow-Credentials</code>.\n\nU neautentizovaných požadavků uveďte hodnotu <kbd>*</kbd>. To způsobí nastavení hlavičky <code>Access-Control-Allow-Origin</code>, ale hlavička <code>Access-Control-Allow-Credentials</code> bude <code>false</code> a budou omezena všechna data specifická pro uživatele.",
        "apihelp-main-param-uselang": "Jazyk, který se má použít pro překlad hlášení. Pomocí <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> se <kbd>siprop=languages</kbd> získáte seznam jazykových kódů nebo zadejte „<kbd>user</kbd>“ pro použití předvoleného jazyka aktuálního uživatele či „<kbd>content</kbd>“ pro použití jazyka obsahu této wiki.",
        "apihelp-block-description": "Zablokovat uživatele.",
-       "apihelp-block-param-user": "Uživatelské jméno, IP adresa nebo rozsah IP adres, které chcete zablokovat.",
+       "apihelp-block-param-user": "Uživatelské jméno, IP adresa nebo rozsah IP adres, které chcete zablokovat. Nelze použít dohromady s <var>$1userid</var>.",
        "apihelp-block-param-reason": "Důvod bloku.",
        "apihelp-block-param-anononly": "Zablokovat pouze anonymní uživatele (tj. zakázat editovat anonymně z této IP).",
        "apihelp-block-param-nocreate": "Nedovolit registraci nových uživatelů.",
        "apihelp-query+watchlistraw-description": "Získat všechny stránky, které jsou aktuálním uživatelem sledovány.",
        "apihelp-query+watchlistraw-example-simple": "Seznam sledovaných stránek uživatele.",
        "apihelp-stashedit-param-summary": "Změnit shrnutí.",
-       "apihelp-unblock-param-user": "Uživatel, IP adresa nebo záběr IP adres k odblokování. Nelze použít dohromady s <var>$1id</var>.",
+       "apihelp-unblock-param-user": "Uživatel, IP adresa nebo rozsah IP adres k odblokování. Nelze použít dohromady s <var>$1id</var> nebo <var>$luserid</var>.",
        "apihelp-watch-example-watch": "Sledovat stránku <kbd>Main Page</kbd>.",
        "apihelp-watch-example-generator": "Zobrazit prvních několik stránek z hlavního jmenného prostoru.",
        "apihelp-format-example-generic": "Výsledek dotazu vrátit ve formátu $1.",
index 404c3b2..7e54617 100644 (file)
        "apihelp-query+imageinfo-paramvalue-prop-extmetadata": "Listet formatierte Metadaten kombiniert aus mehreren Quellen auf. Die Ergebnisse sind im HTML-Format.",
        "apihelp-query+imageinfo-paramvalue-prop-archivename": "Fügt den Dateinamen der Archivversion für die nicht-letzten Versionen hinzu.",
        "apihelp-query+imageinfo-paramvalue-prop-bitdepth": "Fügt die Bittiefe der Version hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-badfile": "Ergänzt, ob die Datei auf der [[MediaWiki:Bad image list]] ist.",
        "apihelp-query+imageinfo-param-limit": "Wie viele Dateiversionen pro Datei zurückgegeben werden sollen.",
        "apihelp-query+imageinfo-param-start": "Zeitstempel, von dem die Liste beginnen soll.",
        "apihelp-query+imageinfo-param-end": "Zeitstempel, an dem die Liste enden soll.",
        "apihelp-query+imageinfo-param-urlheight": "Ähnlich wie $1urlwidth.",
+       "apihelp-query+imageinfo-param-badfilecontexttitle": "Falls <kbd>$2prop=badfile</kbd> festgelegt ist, ist dies der verwendete Seitentitel beim Auswerten der [[MediaWiki:Bad image list]].",
        "apihelp-query+imageinfo-param-localonly": "Suche nur nach Dateien im lokalen Repositorium.",
        "apihelp-query+imageinfo-example-simple": "Rufe Informationen über die aktuelle Version von [[:File:Albert Einstein Head.jpg]] ab.",
        "apihelp-query+imageinfo-example-dated": "Rufe Informationen über Versionen von [[:File:Test.jpg]] von 2008 und später ab.",
index acc409e..09241fb 100644 (file)
        "apihelp-query+imageinfo-paramvalue-prop-archivename": "Ajoute le nom de fichier de la version d’archive pour les versions autres que la dernière.",
        "apihelp-query+imageinfo-paramvalue-prop-bitdepth": "Ajoute la profondeur de bits de la version.",
        "apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "Utilisé par la page Special:Upload pour obtenir de l’information sur un fichier existant. Non prévu pour être utilisé en dehors du cœur de MédiaWiki.",
+       "apihelp-query+imageinfo-paramvalue-prop-badfile": "Ajoute l'indication que le fichier est sur [[MediaWiki:Bad image list]]",
        "apihelp-query+imageinfo-param-limit": "Combien de révision de fichier renvoyer par fichier.",
        "apihelp-query+imageinfo-param-start": "Horodatage auquel démarrer la liste.",
        "apihelp-query+imageinfo-param-end": "Horodatage auquel arrêter la liste.",
        "apihelp-query+imageinfo-param-extmetadatamultilang": "Si des traductions pour la propriété extmetadata sont disponibles, les analyser toutes.",
        "apihelp-query+imageinfo-param-extmetadatafilter": "Si spécifié et non vide, seules ces clés seront renvoyées pour $1prop=extmetadata.",
        "apihelp-query+imageinfo-param-urlparam": "Une chaîne de paramètre spécifique à l’analyseur. Par exemple, les PDFs peuvent utiliser <kbd>page15-100px</kbd>. <var>$1urlwidth</var> doit être utilisé et être cohérent avec <var>$1urlparam</var>.",
+       "apihelp-query+imageinfo-param-badfilecontexttitle": "Si <kbd>$2prop=badfile</kbd> est positionné, il s'agit du titre de la page utilisé pour évaluer la [[MediaWiki:Bad image list]]",
        "apihelp-query+imageinfo-param-localonly": "Rechercher les fichiers uniquement dans le référentiel local.",
        "apihelp-query+imageinfo-example-simple": "Analyser les informations sur la version actuelle de [[:File:Albert Einstein Head.jpg]]",
        "apihelp-query+imageinfo-example-dated": "Analyser les informations sur les versions de [[:File:Test.jpg]] depuis 2008",
        "apiwarn-tokens-origin": "Les jetons ne peuvent pas être obtenus quand la politique de même origine n’est pas appliquée.",
        "apiwarn-toomanyvalues": "Trop de valeurs fournies pour le paramètre <var>$1</var>: la limite est $2.",
        "apiwarn-truncatedresult": "Ce résultat a été tronqué parce que sinon, il dépasserait la limite de $1 octets.",
-       "apiwarn-unclearnowtimestamp": "Passer « $2 » comme paramètre d’horodatage <var>$1</var> a été rendu obsolète. Si, pour une raison quelconque, vous avez besoin de spécifier explicitement l’heure courante sans calculer son côté client, utilisez <kbd>now<kbd>.",
+       "apiwarn-unclearnowtimestamp": "Passer « $2 » comme paramètre d’horodatage <var>$1</var> a été rendu obsolète. Si, pour une raison quelconque, vous avez besoin de spécifier explicitement l’heure courante sans la recalculer du côté client, utilisez <kbd>now<kbd>.",
        "apiwarn-unrecognizedvalues": "{{PLURAL:$3|Valeur non reconnue|Valeurs non reconnues}} pour le paramètre <var>$1</var> : $2.",
        "apiwarn-unsupportedarray": "Le paramètre <var>$1</var> utilise une syntaxe PHP de tableau non prise en charge.",
        "apiwarn-urlparamwidth": "Valeur de la largeur définie dans <var>$1urlparam</var> ($2) ignorée en faveur de la largeur calculée à partir de <var>$1urlwidth</var>/<var>$1urlheight</var> ($3).",
index e5baedc..a1b8b31 100644 (file)
@@ -30,7 +30,8 @@
        "apihelp-main-param-curtimestamp": "결과의 타임스탬프를 포함합니다.",
        "apihelp-main-param-uselang": "메시지 번역을 위한 언어입니다. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>에 <kbd>siprop=languages</kbd>를 함께 사용하면 언어 코드의 목록을 반환하고, <kbd>user</kbd>를 지정하면 현재 사용자의 언어 환경 설정을 사용하며, <kbd>content</kbd>를 지정하면 이 위키의 콘텐츠 언어를 사용합니다.",
        "apihelp-block-description": "사용자를 차단합니다.",
-       "apihelp-block-param-user": "차단하고자 하는 계정 이름, IP 주소 또는 대역",
+       "apihelp-block-param-user": "차단할 사용자 이름, IP 주소, 또는 IP 주소 대역입니다. <var>$1userid</var>와(과) 함께 사용할 수 없습니다.",
+       "apihelp-block-param-userid": "차단할 사용자 ID입니다. <var>$1user</var>와(과) 함께 사용할 수 없습니다.",
        "apihelp-block-param-expiry": "기한. 상대값(예시: <kbd>5 months</kbd> 또는 </kbd>2 weeks</kbd>) 또는 절대값(예시: <kbd>2014-09-18T12:34:56Z</kbd>)이 될 수 있습니다. <kbd>infinite</kbd>, <kbd>indefinite</kbd> 또는 <kbd>never</kbd>로 설정하면 무기한으로 설정됩니다.",
        "apihelp-block-param-reason": "차단 이유.",
        "apihelp-block-param-anononly": "익명 사용자만 차단합니다. (즉, 이 IP의 익명 편집을 막음)",
        "apihelp-tag-param-reason": "변경 이유.",
        "apihelp-tokens-param-type": "요청할 토큰의 종류.",
        "apihelp-unblock-description": "사용자를 차단 해제합니다.",
+       "apihelp-unblock-param-user": "차단을 해제할 사용자 이름, IP 주소, IP 주소 대역입니다. <var>$1id</var> 또는 <var>$luserid</var>와(과) 함께 사용할 수 없습니다.",
+       "apihelp-unblock-param-userid": "차단을 해제할 사용자 ID입니다. <var>$1id</var> 또는 <var>$1user</var>와(과) 함께 사용할 수 없습니다.",
        "apihelp-unblock-param-reason": "차단 해제 이유.",
        "apihelp-userrights-param-user": "사용자 이름.",
        "apihelp-userrights-param-userid": "사용자 ID.",
        "apierror-noedit": "문서를 편집할 권한이 없습니다.",
        "apierror-noimageredirect": "그림 넘겨주기를 만들 권한이 없습니다.",
        "apierror-nosuchrevid": "ID $1에 해당하는 판이 없습니다.",
+       "apierror-nosuchuserid": "ID $1에 해당하는 사용자가 없습니다.",
        "apierror-pagecannotexist": "이름공간은 실제 페이지를 허용하지 않습니다.",
        "apierror-permissiondenied": "$1에 대한 권한이 없습니다.",
        "apierror-permissiondenied-generic": "권한이 없습니다.",
index 57cd43f..0d8e1ad 100644 (file)
        "api-help-param-deprecated": "Displayed in the API help for any deprecated parameter\n{{Identical|Deprecated}}",
        "api-help-param-required": "Displayed in the API help for any required parameter",
        "api-help-datatypes-header": "Header for the data type section in the API help output",
-       "api-help-datatypes": "{{technical}} {{doc-important|Do not translate or reformat dates inside &lt;kbd&gt; tags}} Documentation of certain API data types\nSee also:\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-datatypes": "{{technical}} {{doc-important|Do not translate or reformat dates inside <nowiki><kbd></kbd></nowiki> or <nowiki><var></var></nowiki> tags}} Documentation of certain API data types\nSee also:\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd&gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-integer": "{{technical}} Used to indicate that a parameter is an integer or list of integers. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-boolean": "{{technical}} {{doc-important|Do not translate <code>Special:ApiHelp</code> in this message.}} Used to indicate that a parameter is a boolean. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
index 6a747d0..5d3f2d4 100644 (file)
@@ -38,6 +38,7 @@
        "apihelp-main-param-uselang": "Язык, используемый для перевода Сообщений. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> с <kbd>siprop=языки</kbd> возвращает список кодов языков, или указать <kbd>пользователей</kbd> , чтобы использовать текущий язык пользователя предпочтения, или указать <kbd>контента</kbd> для использования этой Вики содержание язык.",
        "apihelp-block-description": "Блокировка участника.",
        "apihelp-block-param-user": "Имя участника, IP-адрес или диапазон IP-адресов, которые вы хотите заблокировать. Нельзя использовать вместе с <var>$1userid</var>",
+       "apihelp-block-param-userid": "ID участника, которого вы хотите заблокировать. Нельзя использовать одновременно с <var>$1user</var>.",
        "apihelp-block-param-expiry": "Время истечения срока действия. Могут быть относительными (например, <kbd>5 месяцев</kbd> или <kbd>2 недели</kbd>) или абсолютными (например, <kbd>2014-09-18T12:34:56Z</kbd>). Если задано <kbd>бессрочно</kbd>, <kbd>бессрочно</kbd>, или <kbd>не</kbd>, блок никогда не истекает.",
        "apihelp-block-param-reason": "Причина блокировки.",
        "apihelp-block-param-anononly": "Блокировать только анонимных пользователей (т. е. запретить анонимные правки для этого IP-адреса).",
        "apihelp-query+deletedrevs-example-mode1": "Юҡ ителгән һуңғы биттәрҙең исемлеге, баш биттең<kbd>-һы </kbd> һәм<kbd>әйтергә:баш биттең /kbd>-һы,континент менән(режим 1).",
        "apihelp-query+deletedrevs-example-mode3-talk": "Юҡ ителгән тәүге 50 биттең исемлеге {{НС:фекер алышыу}} исемдәр арауығы(режим 3).",
        "apihelp-query+duplicatefiles-example-generated": "Поиск дубликатов всех файлов.",
+       "apihelp-query+imageinfo-paramvalue-prop-badfile": "Добавляет указание на то, находится ли файл в списке [[MediaWiki:Bad image list]]",
        "apihelp-query+logevents-description": "Получать события из журналов.",
        "apihelp-query+logevents-example-simple": "Список последних зарегистрированных событий.",
        "apihelp-query+pagepropnames-example-simple": "Получить первые 10 имен свойств.",
        "apihelp-revisiondelete-description": "удалить и восстановить редакции",
        "apihelp-stashedit-param-sectiontitle": "Заголовок нового раздела.",
        "apihelp-unblock-description": "Разблокировать пользователя.",
+       "apihelp-unblock-param-user": "Имя участника, IP-адрес или диапазон IP-адресов, которые вы хотите разблокировать. Нельзя использовать одновременно с <var>$1id</var> или <var>$luserid</var>.",
+       "apihelp-unblock-param-userid": "ID участника, которого вы хотите разблокировать. Нельзя использовать одновременно с <var>$1id</var> или <var>$1user</var>.",
        "apihelp-unblock-param-reason": "Причина разблокировки",
        "apihelp-unblock-example-id": "Разблокировать блок с идентификатором #<kbd>105</kbd>.",
        "apihelp-unblock-example-user": "Разблокировать пользователя <kbd>Bob</kbd> по причине <kbd>Sorry Bob</kbd>.",
index 36b9d1e..90a0fec 100644 (file)
        "apihelp-query+imageinfo-paramvalue-prop-archivename": "添加用于非最新修订的存档修订的文件名。",
        "apihelp-query+imageinfo-paramvalue-prop-bitdepth": "添加修订的字节深度。",
        "apihelp-query+imageinfo-paramvalue-prop-uploadwarning": "由Special:Upload所使用,以获取关于现有文件的信息。不适用于MediaWiki核心以外代码。",
+       "apihelp-query+imageinfo-paramvalue-prop-badfile": "无论文件是否在[[MediaWiki:Bad image list]]都添加",
        "apihelp-query+imageinfo-param-limit": "每个文件返回多少文件修订。",
        "apihelp-query+imageinfo-param-start": "开始列举的时间戳。",
        "apihelp-query+imageinfo-param-end": "列举的结束时间戳。",
        "apierror-canthide": "您没有权限从封禁日志中隐藏用户名。",
        "apierror-cantimport-upload": "您没有权限导入上传的页面。",
        "apierror-cantimport": "您没有权限导入页面。",
+       "apierror-cantoverwrite-sharedfile": "目标文件存在于分享存储库,并且您没有权限覆盖它。",
        "apierror-cantsend": "您没有登录,您没有已确认的电子邮件地址,或者您未被允许向其他用户发送电子邮件,所以您不能发送电子邮件。",
+       "apierror-cantundelete": "不能还原:请求的修订版本可能不存在,或可能已被还原。",
        "apierror-changeauth-norequest": "创建更改请求失败。",
+       "apierror-chunk-too-small": "对于非最终块,最小块大小为$1{{PLURAL:$1|字节}}。",
        "apierror-cidrtoobroad": "比/$2更宽的$1 CIDR地址段不被接受。",
        "apierror-contentserializationexception": "内容序列化失败:$1",
        "apierror-contenttoobig": "您提供的内容超过了$1{{PLURAL:$1|千字节}}的条目大小限制。",
        "apierror-sectionreplacefailed": "不能合并更新的章节。",
        "apierror-sectionsnotsupported": "内容模型$1不支持章节。",
        "apierror-sectionsnotsupported-what": "章节不被$1所支持。",
+       "apierror-siteinfo-includealldenied": "除非<var>$wgShowHostNames</var>为真,否则不能查看所有服务器的信息。",
        "apierror-sizediffdisabled": "大小差异在Miser模式中被禁用。",
        "apierror-spamdetected": "您的编辑被拒绝,因为它包含破坏性碎片:<code>$1</code>。",
        "apierror-specialpage-cantexecute": "您没有权限查看此特殊页面的结果。",
+       "apierror-stashedfilenotfound": "无法在暂存处找到文件:$1。",
        "apierror-stashfilestorage": "不能在暂存处存储上传:$1",
        "apierror-stashnosuchfilekey": "没有这个filekey:$1。",
        "apierror-stashwrongowner": "错误所有者:$1",
index 4e6b2fd..0aca213 100644 (file)
@@ -22,6 +22,7 @@
  */
 use MediaWiki\MediaWikiServices;
 use Wikimedia\ScopedCallback;
+use MediaWiki\Logger\LoggerFactory;
 
 /**
  * MediaWiki message cache structure version.
@@ -52,6 +53,11 @@ class MessageCache {
         */
        protected $mCache;
 
+       /**
+        * @var bool[] Map of (language code => boolean)
+        */
+       protected $mCacheVolatile = [];
+
        /**
         * Should mean that database cannot be used, but check
         * @var bool $mDisable
@@ -65,10 +71,12 @@ class MessageCache {
        protected $mExpiry;
 
        /**
-        * Message cache has its own parser which it uses to transform
-        * messages.
+        * Message cache has its own parser which it uses to transform messages
+        * @var ParserOptions
         */
-       protected $mParserOptions, $mParser;
+       protected $mParserOptions;
+       /** @var Parser */
+       protected $mParser;
 
        /**
         * Variable for tracking which variables are already loaded
@@ -129,6 +137,7 @@ class MessageCache {
         */
        public static function normalizeKey( $key ) {
                global $wgContLang;
+
                $lckey = strtr( $key, ' ', '_' );
                if ( ord( $lckey ) < 128 ) {
                        $lckey[0] = strtolower( $lckey[0] );
@@ -258,6 +267,7 @@ class MessageCache {
                # Hash of the contents is stored in memcache, to detect if data-center cache
                # or local cache goes out of date (e.g. due to replace() on some other server)
                list( $hash, $hashVolatile ) = $this->getValidationHash( $code );
+               $this->mCacheVolatile[$code] = $hashVolatile;
 
                # Try the local cache and check against the cluster hash key...
                $cache = $this->getLocalCache( $code );
@@ -473,9 +483,16 @@ class MessageCache {
                $bigConds[] = 'page_len > ' . intval( $wgMaxMsgCacheEntrySize );
 
                # Load titles for all oversized pages in the MediaWiki namespace
-               $res = $dbr->select( 'page', 'page_title', $bigConds, __METHOD__ . "($code)-big" );
+               $res = $dbr->select(
+                       'page',
+                       [ 'page_title', 'page_latest' ],
+                       $bigConds,
+                       __METHOD__ . "($code)-big"
+               );
                foreach ( $res as $row ) {
                        $cache[$row->page_title] = '!TOO BIG';
+                       // At least include revision ID so page changes are reflected in the hash
+                       $cache['EXCESSIVE'][$row->page_title] = $row->page_latest;
                }
 
                # Conditions to load the remaining pages with their contents
@@ -525,7 +542,7 @@ class MessageCache {
         * Updates cache as necessary when message page is changed
         *
         * @param string|bool $title Name of the page changed (false if deleted)
-        * @param mixed $text New contents of the page.
+        * @param string|bool $text New contents of the page (false if deleted)
         */
        public function replace( $title, $text ) {
                global $wgMaxMsgCacheEntrySize, $wgContLang, $wgLanguageCode;
@@ -545,31 +562,32 @@ class MessageCache {
                // a self-deadlock. This is safe as no reads happen *directly* in this
                // method between getReentrantScopedLock() and load() below. There is
                // no risk of data "changing under our feet" for replace().
-               $cacheKey = wfMemcKey( 'messages', $code );
-               $scopedLock = $this->getReentrantScopedLock( $cacheKey );
+               $scopedLock = $this->getReentrantScopedLock( wfMemcKey( 'messages', $code ) );
+               // Load the messages from the master DB to avoid race conditions
                $this->load( $code, self::FOR_UPDATE );
 
-               $titleKey = wfMemcKey( 'messages', 'individual', $title );
+               // Load the new value into the process cache...
                if ( $text === false ) {
-                       // Article was deleted
                        $this->mCache[$code][$title] = '!NONEXISTENT';
-                       $this->wanCache->delete( $titleKey );
                } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
-                       // Check for size
                        $this->mCache[$code][$title] = '!TOO BIG';
-                       $this->wanCache->set( $titleKey, ' ' . $text, $this->mExpiry );
+                       // Pre-fill the individual key cache with the known latest message text
+                       $key = $this->wanCache->makeKey( 'messages-big', $this->mCache[$code]['HASH'], $title );
+                       $this->wanCache->set( $key, " $text", $this->mExpiry );
                } else {
                        $this->mCache[$code][$title] = ' ' . $text;
-                       $this->wanCache->delete( $titleKey );
                }
-
-               // Mark this cache as definitely "latest" (non-volatile) so
-               // load() calls do try to refresh the cache with replica DB data
+               // Mark this cache as definitely being "latest" (non-volatile) so
+               // load() calls do not try to refresh the cache with replica DB data
                $this->mCache[$code]['LATEST'] = time();
 
                // Update caches if the lock was acquired
                if ( $scopedLock ) {
                        $this->saveToCaches( $this->mCache[$code], 'all', $code );
+               } else {
+                       LoggerFactory::getInstance( 'MessageCache' )->error(
+                               __METHOD__ . ': could not acquire lock to update {title} ({code})',
+                               [ 'title' => $title, 'code' => $code ] );
                }
 
                ScopedCallback::consume( $scopedLock );
@@ -650,24 +668,26 @@ class MessageCache {
        protected function getValidationHash( $code ) {
                $curTTL = null;
                $value = $this->wanCache->get(
-                       wfMemcKey( 'messages', $code, 'hash', 'v1' ),
+                       $this->wanCache->makeKey( 'messages', $code, 'hash', 'v1' ),
                        $curTTL,
                        [ wfMemcKey( 'messages', $code ) ]
                );
 
-               if ( !$value ) {
-                       // No hash found at all; cache must regenerate to be safe
-                       $hash = false;
-                       $expired = true;
-               } else {
+               if ( $value ) {
                        $hash = $value['hash'];
-                       if ( ( time() - $value['latest'] ) < WANObjectCache::HOLDOFF_TTL ) {
-                               // Cache was recently updated via replace() and should be up-to-date
+                       if ( ( time() - $value['latest'] ) < WANObjectCache::TTL_MINUTE ) {
+                               // Cache was recently updated via replace() and should be up-to-date.
+                               // That method is only called in the primary datacenter and uses FOR_UPDATE.
+                               // Also, it is unlikely that the current datacenter is *now* secondary one.
                                $expired = false;
                        } else {
                                // See if the "check" key was bumped after the hash was generated
                                $expired = ( $curTTL < 0 );
                        }
+               } else {
+                       // No hash found at all; cache must regenerate to be safe
+                       $hash = false;
+                       $expired = true;
                }
 
                return [ $hash, $expired ];
@@ -677,14 +697,15 @@ class MessageCache {
         * Set the md5 used to validate the local disk cache
         *
         * If $cache has a 'LATEST' UNIX timestamp key, then the hash will not
-        * be treated as "volatile" by getValidationHash() for the next few seconds
+        * be treated as "volatile" by getValidationHash() for the next few seconds.
+        * This is triggered when $cache is generated using FOR_UPDATE mode.
         *
         * @param string $code
         * @param array $cache Cached messages with a version
         */
        protected function setValidationHash( $code, array $cache ) {
                $this->wanCache->set(
-                       wfMemcKey( 'messages', $code, 'hash', 'v1' ),
+                       $this->wanCache->makeKey( 'messages', $code, 'hash', 'v1' ),
                        [
                                'hash' => $cache['HASH'],
                                'latest' => isset( $cache['LATEST'] ) ? $cache['LATEST'] : 0
@@ -847,6 +868,7 @@ class MessageCache {
         */
        private function getMessageForLang( $lang, $lckey, $useDB, &$alreadyTried ) {
                global $wgContLang;
+
                $langcode = $lang->getCode();
 
                // Try checking the database for the requested language
@@ -905,6 +927,7 @@ class MessageCache {
         */
        private function getMessagePageName( $langcode, $uckey ) {
                global $wgLanguageCode;
+
                if ( $langcode === $wgLanguageCode ) {
                        // Messages created in the content language will not have the /lang extension
                        return $uckey;
@@ -930,8 +953,7 @@ class MessageCache {
                if ( isset( $this->mCache[$code][$title] ) ) {
                        $entry = $this->mCache[$code][$title];
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
-                               // The message exists, so make sure a string
-                               // is returned.
+                               // The message exists, so make sure a string is returned.
                                return (string)substr( $entry, 1 );
                        } elseif ( $entry === '!NONEXISTENT' ) {
                                return false;
@@ -950,17 +972,19 @@ class MessageCache {
                }
 
                // Try the individual message cache
-               $titleKey = wfMemcKey( 'messages', 'individual', $title );
-
-               $curTTL = null;
-               $entry = $this->wanCache->get(
-                       $titleKey,
-                       $curTTL,
-                       [ wfMemcKey( 'messages', $code ) ]
-               );
-               $entry = ( $curTTL >= 0 ) ? $entry : false;
+               $titleKey = $this->wanCache->makeKey( 'messages-big', $this->mCache[$code]['HASH'], $title );
+
+               if ( $this->mCacheVolatile[$code] ) {
+                       $entry = false;
+                       // Make sure that individual keys respect the WAN cache holdoff period too
+                       LoggerFactory::getInstance( 'MessageCache' )->debug(
+                               __METHOD__ . ': loading volatile key \'{titleKey}\'',
+                               [ 'titleKey' => $titleKey, 'code' => $code ] );
+               } else {
+                       $entry = $this->wanCache->get( $titleKey );
+               }
 
-               if ( $entry ) {
+               if ( $entry !== false ) {
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
                                $this->mCache[$code][$title] = $entry;
                                // The message exists, so make sure a string is returned
@@ -975,7 +999,7 @@ class MessageCache {
                        }
                }
 
-               // Try loading it from the database
+               // Try loading the message from the database
                $dbr = wfGetDB( DB_REPLICA );
                $cacheOpts = Database::getCacheSetOptions( $dbr );
                // Use newKnownCurrent() to avoid querying revision/user tables
@@ -992,32 +1016,18 @@ class MessageCache {
 
                if ( $revision ) {
                        $content = $revision->getContent();
-                       if ( !$content ) {
-                               // A possibly temporary loading failure.
-                               wfDebugLog(
-                                       'MessageCache',
-                                       __METHOD__ . ": failed to load message page text for {$title} ($code)"
-                               );
-                               $message = null; // no negative caching
-                       } else {
-                               // XXX: Is this the right way to turn a Content object into a message?
-                               // NOTE: $content is typically either WikitextContent, JavaScriptContent or
-                               //       CssContent. MessageContent is *not* used for storing messages, it's
-                               //       only used for wrapping them when needed.
-                               $message = $content->getWikitextForTransclusion();
-
-                               if ( $message === false || $message === null ) {
-                                       wfDebugLog(
-                                               'MessageCache',
-                                               __METHOD__ . ": message content doesn't provide wikitext "
-                                                       . "(content model: " . $content->getModel() . ")"
-                                       );
-
-                                       $message = false; // negative caching
-                               } else {
+                       if ( $content ) {
+                               $message = $this->getMessageTextFromContent( $content );
+                               if ( is_string( $message ) ) {
                                        $this->mCache[$code][$title] = ' ' . $message;
                                        $this->wanCache->set( $titleKey, ' ' . $message, $this->mExpiry, $cacheOpts );
                                }
+                       } else {
+                               // A possibly temporary loading failure
+                               LoggerFactory::getInstance( 'MessageCache' )->warning(
+                                       __METHOD__ . ': failed to load message page text for \'{titleKey}\'',
+                                       [ 'titleKey' => $titleKey, 'code' => $code ] );
+                               $message = null; // no negative caching
                        }
                } else {
                        $message = false; // negative caching
@@ -1069,6 +1079,7 @@ class MessageCache {
         */
        function getParser() {
                global $wgParser, $wgParserConf;
+
                if ( !$this->mParser && isset( $wgParser ) ) {
                        # Do some initialisation so that we don't have to do it twice
                        $wgParser->firstCallInit();
@@ -1096,6 +1107,8 @@ class MessageCache {
        public function parse( $text, $title = null, $linestart = true,
                $interface = false, $language = null
        ) {
+               global $wgTitle;
+
                if ( $this->mInParser ) {
                        return htmlspecialchars( $text );
                }
@@ -1110,7 +1123,6 @@ class MessageCache {
                $popts->setTargetLanguage( $language );
 
                if ( !$title || !$title instanceof Title ) {
-                       global $wgTitle;
                        wfDebugLog( 'GlobalTitleFail', __METHOD__ . ' called by ' .
                                wfGetAllCallers( 6 ) . ' with no title set.' );
                        $title = $wgTitle;
@@ -1198,6 +1210,7 @@ class MessageCache {
         */
        public function getAllMessageKeys( $code ) {
                global $wgContLang;
+
                $this->load( $code );
                if ( !isset( $this->mCache[$code] ) ) {
                        // Apparently load() failed
@@ -1207,10 +1220,61 @@ class MessageCache {
                $cache = $this->mCache[$code];
                unset( $cache['VERSION'] );
                unset( $cache['EXPIRY'] );
+               unset( $cache['EXCESSIVE'] );
                // Remove any !NONEXISTENT keys
                $cache = array_diff( $cache, [ '!NONEXISTENT' ] );
 
                // Keys may appear with a capital first letter. lcfirst them.
                return array_map( [ $wgContLang, 'lcfirst' ], array_keys( $cache ) );
        }
+
+       /**
+        * Purge message caches when a MediaWiki: page is created, updated, or deleted
+        *
+        * @param Title $title Message page title
+        * @param Content|null $content New content for edit/create, null on deletion
+        * @since 1.29
+        */
+       public function updateMessageOverride( Title $title, Content $content = null ) {
+               global $wgContLang;
+
+               $msgText = $this->getMessageTextFromContent( $content );
+               if ( $msgText === null ) {
+                       $msgText = false; // treat as not existing
+               }
+
+               $this->replace( $title->getDBkey(), $msgText );
+
+               if ( $wgContLang->hasVariants() ) {
+                       $wgContLang->updateConversionTable( $title );
+               }
+       }
+
+       /**
+        * @param Content|null $content Content or null if the message page does not exist
+        * @return string|bool|null Returns false if $content is null and null on error
+        */
+       private function getMessageTextFromContent( Content $content = null ) {
+               // @TODO: could skip pseudo-messages like js/css here, based on content model
+               if ( $content ) {
+                       // Message page exists...
+                       // XXX: Is this the right way to turn a Content object into a message?
+                       // NOTE: $content is typically either WikitextContent, JavaScriptContent or
+                       //       CssContent. MessageContent is *not* used for storing messages, it's
+                       //       only used for wrapping them when needed.
+                       $msgText = $content->getWikitextForTransclusion();
+                       if ( $msgText === false || $msgText === null ) {
+                               // This might be due to some kind of misconfiguration...
+                               $msgText = null;
+                               LoggerFactory::getInstance( 'MessageCache' )->warning(
+                                       __METHOD__ . ": message content doesn't provide wikitext "
+                                       . "(content model: " . $content->getModel() . ")" );
+                       }
+               } else {
+                       // Message page does not exist...
+                       $msgText = false;
+               }
+
+               return $msgText;
+       }
 }
index 1c11b4f..071cee7 100644 (file)
@@ -1151,22 +1151,8 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
-                       // @todo move this logic to MessageCache
-                       if ( $this->exists() ) {
-                               // NOTE: use transclusion text for messages.
-                               //       This is consistent with  MessageCache::getMsgFromNamespace()
-
-                               $content = $this->getContent();
-                               $text = $content === null ? null : $content->getWikitextForTransclusion();
-
-                               if ( $text === null ) {
-                                       $text = false;
-                               }
-                       } else {
-                               $text = false;
-                       }
-
-                       MessageCache::singleton()->replace( $this->mTitle->getDBkey(), $text );
+                       $messageCache = MessageCache::singleton();
+                       $messageCache->updateMessageOverride( $this->mTitle, $this->getContent() );
                }
 
                return true;
@@ -2212,7 +2198,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   - 'no-change': don't update the article count, ever
         */
        public function doEditUpdates( Revision $revision, User $user, array $options = [] ) {
-               global $wgRCWatchCategoryMembership, $wgContLang;
+               global $wgRCWatchCategoryMembership;
 
                $options += [
                        'changed' => true,
@@ -2350,17 +2336,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
-                       // XXX: could skip pseudo-messages like js/css here, based on content model.
-                       $msgtext = $content ? $content->getWikitextForTransclusion() : null;
-                       if ( $msgtext === false || $msgtext === null ) {
-                               $msgtext = '';
-                       }
-
-                       MessageCache::singleton()->replace( $shortTitle, $msgtext );
-
-                       if ( $wgContLang->hasVariants() ) {
-                               $wgContLang->updateConversionTable( $this->mTitle );
-                       }
+                       MessageCache::singleton()->updateMessageOverride( $this->mTitle, $content );
                }
 
                if ( $options['created'] ) {
@@ -3358,8 +3334,6 @@ class WikiPage implements Page, IDBAccessObject {
         * @param Title $title
         */
        public static function onArticleDelete( Title $title ) {
-               global $wgContLang;
-
                // Update existence markers on article/talk tabs...
                $other = $title->getOtherPage();
 
@@ -3376,11 +3350,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Messages
                if ( $title->getNamespace() == NS_MEDIAWIKI ) {
-                       MessageCache::singleton()->replace( $title->getDBkey(), false );
-
-                       if ( $wgContLang->hasVariants() ) {
-                               $wgContLang->updateConversionTable( $title );
-                       }
+                       MessageCache::singleton()->updateMessageOverride( $title, null );
                }
 
                // Images
index cc98abd..426b550 100644 (file)
@@ -48,7 +48,13 @@ abstract class Preprocessor {
                        'names' => [ 2 => null ],
                        'min' => 2,
                        'max' => 2,
-               ]
+               ],
+               '-{' => [
+                       'end' => '}-',
+                       'names' => [ 1 => null ],
+                       'min' => 1,
+                       'max' => 1,
+               ],
        ];
 
        /**
index 5da7cd7..950d66d 100644 (file)
@@ -193,6 +193,8 @@ class Preprocessor_DOM extends Preprocessor {
         * @return string
         */
        public function preprocessToXml( $text, $flags = 0 ) {
+               global $wgDisableLangConversion;
+
                $forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
 
                $xmlishElements = $this->parser->getStripList();
@@ -220,6 +222,10 @@ class Preprocessor_DOM extends Preprocessor {
                $stack = new PPDStack;
 
                $searchBase = "[{<\n"; # }
+               if ( !$wgDisableLangConversion ) {
+                       $searchBase .= '-';
+               }
+
                // For fast reverse searches
                $revText = strrev( $text );
                $lengthText = strlen( $text );
@@ -298,7 +304,10 @@ class Preprocessor_DOM extends Preprocessor {
                                                break;
                                        }
                                } else {
-                                       $curChar = $text[$i];
+                                       $curChar = $curTwoChar = $text[$i];
+                                       if ( ( $i + 1 ) < $lengthText ) {
+                                               $curTwoChar .= $text[$i + 1];
+                                       }
                                        if ( $curChar == '|' ) {
                                                $found = 'pipe';
                                        } elseif ( $curChar == '=' ) {
@@ -311,11 +320,20 @@ class Preprocessor_DOM extends Preprocessor {
                                                } else {
                                                        $found = 'line-start';
                                                }
+                                       } elseif ( $curTwoChar == $currentClosing ) {
+                                               $found = 'close';
+                                               $curChar = $curTwoChar;
                                        } elseif ( $curChar == $currentClosing ) {
                                                $found = 'close';
+                                       } elseif ( isset( $this->rules[$curTwoChar] ) ) {
+                                               $curChar = $curTwoChar;
+                                               $found = 'open';
+                                               $rule = $this->rules[$curChar];
                                        } elseif ( isset( $this->rules[$curChar] ) ) {
                                                $found = 'open';
                                                $rule = $this->rules[$curChar];
+                                       } elseif ( $curChar == '-' ) {
+                                               $found = 'dash';
                                        } else {
                                                # Some versions of PHP have a strcspn which stops on null characters
                                                # Ignore and continue
@@ -595,7 +613,8 @@ class Preprocessor_DOM extends Preprocessor {
                                // input pointer.
                        } elseif ( $found == 'open' ) {
                                # count opening brace characters
-                               $count = strspn( $text, $curChar, $i );
+                               $curLen = strlen( $curChar );
+                               $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i );
 
                                # we need to add to stack only if opening brace count is enough for one of the rules
                                if ( $count >= $rule['min'] ) {
@@ -615,12 +634,13 @@ class Preprocessor_DOM extends Preprocessor {
                                        # Add literal brace(s)
                                        $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
                                }
-                               $i += $count;
+                               $i += $curLen * $count;
                        } elseif ( $found == 'close' ) {
                                $piece = $stack->top;
                                # lets check if there are enough characters for closing brace
                                $maxCount = $piece->count;
-                               $count = strspn( $text, $curChar, $i, $maxCount );
+                               $curLen = strlen( $curChar );
+                               $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i, $maxCount );
 
                                # check for maximum matching characters (if there are 5 closing
                                # characters, we will probably need only 3 - depending on the rules)
@@ -643,7 +663,7 @@ class Preprocessor_DOM extends Preprocessor {
                                        # No matching element found in callback array
                                        # Output a literal closing brace and continue
                                        $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
-                                       $i += $count;
+                                       $i += $curLen * $count;
                                        continue;
                                }
                                $name = $rule['names'][$matchingCount];
@@ -682,7 +702,7 @@ class Preprocessor_DOM extends Preprocessor {
                                }
 
                                # Advance input pointer
-                               $i += $matchingCount;
+                               $i += $curLen * $matchingCount;
 
                                # Unwind the stack
                                $stack->pop();
@@ -716,6 +736,9 @@ class Preprocessor_DOM extends Preprocessor {
                                $stack->getCurrentPart()->eqpos = strlen( $accum );
                                $accum .= '=';
                                ++$i;
+                       } elseif ( $found == 'dash' ) {
+                               $accum .= '-';
+                               ++$i;
                        }
                }
 
index 8a4637e..1317e60 100644 (file)
@@ -117,6 +117,8 @@ class Preprocessor_Hash extends Preprocessor {
         * @return PPNode_Hash_Tree
         */
        public function preprocessToObj( $text, $flags = 0 ) {
+               global $wgDisableLangConversion;
+
                $tree = $this->cacheGetTree( $text, $flags );
                if ( $tree !== false ) {
                        $store = json_decode( $tree );
@@ -152,6 +154,10 @@ class Preprocessor_Hash extends Preprocessor {
                $stack = new PPDStack_Hash;
 
                $searchBase = "[{<\n";
+               if ( !$wgDisableLangConversion ) {
+                       $searchBase .= '-';
+               }
+
                // For fast reverse searches
                $revText = strrev( $text );
                $lengthText = strlen( $text );
@@ -229,7 +235,10 @@ class Preprocessor_Hash extends Preprocessor {
                                                break;
                                        }
                                } else {
-                                       $curChar = $text[$i];
+                                       $curChar = $curTwoChar = $text[$i];
+                                       if ( ( $i + 1 ) < $lengthText ) {
+                                               $curTwoChar .= $text[$i + 1];
+                                       }
                                        if ( $curChar == '|' ) {
                                                $found = 'pipe';
                                        } elseif ( $curChar == '=' ) {
@@ -242,11 +251,20 @@ class Preprocessor_Hash extends Preprocessor {
                                                } else {
                                                        $found = 'line-start';
                                                }
+                                       } elseif ( $curTwoChar == $currentClosing ) {
+                                               $found = 'close';
+                                               $curChar = $curTwoChar;
                                        } elseif ( $curChar == $currentClosing ) {
                                                $found = 'close';
+                                       } elseif ( isset( $this->rules[$curTwoChar] ) ) {
+                                               $curChar = $curTwoChar;
+                                               $found = 'open';
+                                               $rule = $this->rules[$curChar];
                                        } elseif ( isset( $this->rules[$curChar] ) ) {
                                                $found = 'open';
                                                $rule = $this->rules[$curChar];
+                                       } elseif ( $curChar == '-' ) {
+                                               $found = 'dash';
                                        } else {
                                                # Some versions of PHP have a strcspn which stops on null characters
                                                # Ignore and continue
@@ -538,7 +556,8 @@ class Preprocessor_Hash extends Preprocessor {
                                // input pointer.
                        } elseif ( $found == 'open' ) {
                                # count opening brace characters
-                               $count = strspn( $text, $curChar, $i );
+                               $curLen = strlen( $curChar );
+                               $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i );
 
                                # we need to add to stack only if opening brace count is enough for one of the rules
                                if ( $count >= $rule['min'] ) {
@@ -557,12 +576,13 @@ class Preprocessor_Hash extends Preprocessor {
                                        # Add literal brace(s)
                                        self::addLiteral( $accum, str_repeat( $curChar, $count ) );
                                }
-                               $i += $count;
+                               $i += $curLen * $count;
                        } elseif ( $found == 'close' ) {
                                $piece = $stack->top;
                                # lets check if there are enough characters for closing brace
                                $maxCount = $piece->count;
-                               $count = strspn( $text, $curChar, $i, $maxCount );
+                               $curLen = strlen( $curChar );
+                               $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i, $maxCount );
 
                                # check for maximum matching characters (if there are 5 closing
                                # characters, we will probably need only 3 - depending on the rules)
@@ -585,7 +605,7 @@ class Preprocessor_Hash extends Preprocessor {
                                        # No matching element found in callback array
                                        # Output a literal closing brace and continue
                                        self::addLiteral( $accum, str_repeat( $curChar, $count ) );
-                                       $i += $count;
+                                       $i += $curLen * $count;
                                        continue;
                                }
                                $name = $rule['names'][$matchingCount];
@@ -627,7 +647,7 @@ class Preprocessor_Hash extends Preprocessor {
                                }
 
                                # Advance input pointer
-                               $i += $matchingCount;
+                               $i += $curLen * $matchingCount;
 
                                # Unwind the stack
                                $stack->pop();
@@ -661,6 +681,9 @@ class Preprocessor_Hash extends Preprocessor {
                                $accum[] = [ 'equals', [ '=' ] ];
                                $stack->getCurrentPart()->eqpos = count( $accum ) - 1;
                                ++$i;
+                       } elseif ( $found == 'dash' ) {
+                               self::addLiteral( $accum, '-' );
+                               ++$i;
                        }
                }
 
index a2a6760..3ee3ed5 100644 (file)
@@ -21,7 +21,7 @@ class DummySearchIndexFieldDefinition extends SearchIndexFieldDefinition {
                ];
 
                foreach ( $this->subfields as $subfield ) {
-                       $mapping['subfields'][] = $subfield->getMapping();
+                       $mapping['subfields'][] = $subfield->getMapping( $engine );
                }
 
                return $mapping;
index 1b38668..3aa27c9 100644 (file)
        "log-action-filter-protect-modify": "Зьмена абароны",
        "log-action-filter-protect-unprotect": "Зьняцьце абароны",
        "log-action-filter-protect-move_prot": "Перанос абароны",
+       "log-action-filter-rights-rights": "Ручная зьмена",
        "log-action-filter-rights-autopromote": "Аўтаматычнае зьмяненьне",
+       "log-action-filter-suppress-event": "Утойваньне журнала",
        "log-action-filter-upload-upload": "Новая загрузка",
        "authmanager-realname-label": "Сапраўднае імя",
        "authmanager-provider-temporarypassword": "Часовы пароль",
index 4b25593..0530685 100644 (file)
        "restrictionsfield-badip": "Ungültige IP-Adresse oder ungültiger IP-Adressbereich: $1",
        "restrictionsfield-label": "Erlaubte IP-Adressbereiche:",
        "restrictionsfield-help": "Eine IP-Adresse oder ein CIDR-Bereich pro Zeile. Um alles zu aktivieren, verwende<br><code>0.0.0.0/0</code><br><code>::/0</code>",
-       "revid": "v$1",
+       "revid": "Version $1",
        "pageid": "Seitenkennung $1"
 }
index 35a9ebd..1d7c9ae 100644 (file)
        "mainpage": "Pela Seri",
        "mainpage-description": "Pela seri",
        "policy-url": "Project:Terzê hereketi",
-       "portal": "Portalé cemati",
-       "portal-url": "Project:Portalé cemati",
+       "portal": "Portalê cemaeti",
+       "portal-url": "Project:Portalê cemaeti",
        "privacy": "Politikay nımıtışi",
        "privacypage": "Project:Xısusiyetê nımıtışi",
        "badaccess": "Xeta mısadey",
index 35806aa..0ef9760 100644 (file)
        "wlshowhidemine": "omat muokkaukseni",
        "wlshowhidecategorization": "sivujen luokkien muutokset",
        "watchlist-options": "Tarkkailulistan asetukset",
+       "watchlist-mark-all-visited": "Oletko varma, että haluat merkata nähdyiksi kaikki tarkkailulistan sivut, joita et ole avannut?",
        "watching": "Lisätään tarkkailulistalle...",
        "unwatching": "Poistetaan tarkkailulistalta...",
        "watcherrortext": "Sivun ”$1” tarkkailulista-asetusten muutoksissa tapahtui virhe.",
index 7593a37..8cba799 100644 (file)
        "restrictionsfield-badip": "Adresse IP ou plage non valide : $1",
        "restrictionsfield-label": "Plages IP autorisées :",
        "restrictionsfield-help": "Une adresse IP ou une plage CIDR par ligne. Pour tout activer, utiliser <br><code>0.0.0.0/0</code><br><code>::/0</code>",
+       "revid": "version $1",
        "pageid": "ID de page $1"
 }
index a2fd8c9..6f632db 100644 (file)
        "passwordreset-emaildisabled": "ამ ვიკიში ელ.ფოსტის ფუნქციები გამორთულია.",
        "passwordreset-username": "მომხმარებლის სახელი:",
        "passwordreset-domain": "დომენი:",
-       "passwordreset-capture": "გამოსული ელ-ფოსტის ხილვა?",
-       "passwordreset-capture-help": "თუ მონიშნავთ ამ უჯრას, მაშინ თქვენ შეგეძლებათ მომხმარებლისათვის გაგზავნილი ელ.ფოსტის ნახვა (დროებითი პაროლით).",
        "passwordreset-email": "ელ. ფოსტის მისამართი:",
        "passwordreset-emailtitle": "ანგარიშის მონაცემები {{SITENAME}}-თვის",
        "passwordreset-emailtext-ip": "ვიღაცამ (შესაძლოა თქვენ, ამ IP-მისამართიდან $1) მოითხოვა თქვენი \nპაროლის თავიდან დაყენება საიტისათვის {{SITENAME}} ($4).\n{{PLURAL:$3|შემდეგი ანგარიში მიბმულია|შემდეგი ანგარიშები მიბმულია}} ამ ელ.ფოსტის მისამართზე:\n\n$2\n\n{{PLURAL:$3|ეს დროებითი პაროლი|ეს დროებითი პაროლები}} იმოქმედებს {{PLURAL:$5|ერთი დღე|$5 დღე}}.\nთქვენ უნდა შეხვიდეთ სისტემაში და აირჩიოთ ახალი პაროლი.\nთუ თქვენ არ გაგიკეთებიათ აღნიშნული მოთხოვნა, ან გაიხსენეთ თქვენი პაროლი\nდა აღარ გსურთ მისი შეცვლა, მაშინ შეგიძლიათ იგნორირება გაუკეთოთ ამ შეტყობინებას\nდა გააგრძელოთ თქვენი ძველი პაროლის გამოყენება.",
        "passwordreset-emailelement": "მომხმარებლის სახელი: \n$1\n\nდროებითი პაროლი: \n$2",
        "passwordreset-emailsentemail": "თუ ეს მეილი თქვენს ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
        "passwordreset-emailsentusername": "თუ არსებობს მეილი, რომელიც ამ ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
-       "passwordreset-emailsent-capture2": "პაროლის გაუქმების შესახებ {{PLURAL:$1|მეილი|მეილები}} გაიგზავნა. {{PLURAL:$1|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
-       "passwordreset-emailerror-capture2": "{{GENDER:$2|მომხმარებელთან}} მეილის გაგზავნა ვერ მოხერხდა: $1 {{PLURAL:$3|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-nocaller": "გამომძახებელი უნდა იყოს მიწოდებული",
        "passwordreset-nosuchcaller": "გამომძახებელი არ არსებობს: $1",
        "passwordreset-ignored": "პაროლის გაუქმება არ იქნა შესრულებული. შეიძლება კონფიგურაციაში პროვაიდერი არ იყო გათვალისწინებული?",
        "userrights-reason": "შეცვლის მიზეზი:",
        "userrights-no-interwiki": "თქვენ არ გაქვთ მომხმარებლის უფლებების რედაქტირების უფლება სხვა ვიკი-ებში.",
        "userrights-nodatabase": "მონაცემთა ბაზა $1 არ არსებობს, ან არ არის ლოკალური.",
-       "userrights-nologin": "თქვენ უნდა [[Special:UserLogin|წარადგინოთ თავი სისტემისადმი]] ადმინისისტრატორის ანგარიშით იმისთვის, რომ გასცეთ მომხმარებელთა უფლებები.",
-       "userrights-notallowed": "თქვენ არ გაქვთ მომხმარებელთა უფლებების შეცვლის უფლება.",
        "userrights-changeable-col": "ჯგუფები, რომლებიც შეგიძლიათ შეცვალოთ",
        "userrights-unchangeable-col": "ჯგუფები, რომლებიც არ შეგიძლიათ შეცვალოთ",
        "userrights-irreversible-marker": "$1*",
        "userrights-conflict": "მომხმარებელთა უფლებების კონფლიქტი! გთხოვთ ხელმეორედ შეინახოთ თქვენი ცვლილებები.",
-       "userrights-removed-self": "თქვენ წაშალეთ საკუთარი უფლება. შესაბამისად, თქვენ აღარ გექნებათ ამ გვერდთან წვდომის საშუალება",
        "group": "ჯგუფი:",
        "group-user": "მომხმარებლები",
        "group-autoconfirmed": "ავტომატურად დადასტურებული მომხმარებლები",
        "right-siteadmin": "მონაცემთა ბაზის დაბლოკვა და განბლოკვა",
        "right-override-export-depth": "გვერდების ექსპორტირება, დაკავშირებული გვერდების ჩათვლით 5-მდე სიიღრმით",
        "right-sendemail": "გაგუგზავნე ელექტრონული ფოსტა სხვა მომხმარებლებს",
-       "right-passwordreset": "ელ.ფოსტის ნახვა პაროლის შეცვლით",
        "right-managechangetags": "[[Special:Tags|ტეგების]] შექმნა და (დე)აქტივაცია",
        "right-applychangetags": "[[Special:Tags|tags]] მიღება თქვენ ცვლილებებთან ერთად",
        "right-changetags": "თვითნებური [[Special:Tags|tags]] დამატება ან წაშლა ცალკეულ ცვლილებებსა და ჟურნალის ჩანაწერებში",
        "listfiles_date": "თარიღი",
        "listfiles_name": "სახელი",
        "listfiles_user": "მომხმარებელი",
-       "listfiles_size": "ზომა (ბაიტები)",
+       "listfiles_size": "ზომა",
        "listfiles_description": "აღწერილობა",
        "listfiles_count": "ვერსიები",
        "listfiles-show-all": "სურათების ძველი ვერსიების ჩართვა",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "სიმბოლოები",
        "special-characters-group-greek": "ბერძნული",
+       "special-characters-group-greekextended": "ბერძნული გაფართოებული",
        "special-characters-group-cyrillic": "კირილიცა",
        "special-characters-group-arabic": "არაბული",
        "special-characters-group-arabicextended": "არაბული გაფართოება",
        "mw-widgets-dateinput-no-date": "თარიღი არ არის არჩეული",
        "mw-widgets-dateinput-placeholder-day": "წწწწ-თთ-დდ",
        "mw-widgets-dateinput-placeholder-month": "წწწწ-თთ",
+       "mw-widgets-mediasearch-noresults": "შედეგები ვერ მოიძებნა.",
        "mw-widgets-titleinput-description-new-page": "გვერდი ჯერ არ არსებობს",
        "mw-widgets-titleinput-description-redirect": "გადამისამართება $1-ზე",
        "sessionmanager-tie": "შეუძლებელია მრავალი მოთხოვნის ავთენთიფიკაციის ტიპების გაერთიანება: $1.",
index 12b6ad5..2f0e429 100644 (file)
        "htmlform-user-not-exists": "<strong>$1</strong> neegzistuoja.",
        "htmlform-user-not-valid": "<strong>$1</strong> nėra tinkamas naudotojo vardas.",
        "logentry-delete-delete": "$1 {{GENDER:$2|ištrynė}} puslapį $3",
+       "logentry-delete-delete_redir": "$1 pervadindamas {{GENDER:$2|ištrynė}} buvusį nukreipimą $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|atkūrė}} puslapį $3",
        "logentry-delete-event": "$1 {{GENDER:$2|pakeitė}} matomumą {{PLURAL:$5|žurnalo įvykio|$5 žurnalo įvykių}} $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|pakeitė}} matomumą {{PLURAL:$5|versijos|$5 versijų}} puslapyje $3: $4",
index 6cb3e84..6ec8aec 100644 (file)
        "category-media-header": "Media \"$1\" ipan neneuhcayotl",
        "category-empty": "''Ahtle oncah ipan neneuhcayotl.''",
        "hidden-categories": "{{PLURAL:$1|tlatlatilli neneuhcayotl|tlatlatiltin neneuhcayomeh}}",
-       "hidden-category-category": "Tlatlàtìlkàtlaìxmatkàtlàlilòmë",
+       "hidden-category-category": "Tlahyanalli neneuhcayotl",
        "category-subcat-count": "{{PLURAL:$2|Inin neneuhcayotl zan quipiya in tetoquilli tlani-neneuhcayotl.|Inin neneuhcayotl {{PLURAL:$1|quipiya in tetoquilli tlani-neneuhcayotl|in tetoquiltin $1 tlani-neneuhcayomeh}}, itech tlacecempohualoni $2.}}",
        "category-subcat-count-limited": "Inin {{PLURAL:$1|neneuhcayotl quipiya|$1 in tetoquilli tlani-neneuhcayotl|in tetoquiltin tlani-neneuhcayomeh}}.",
        "category-article-count": "{{PLURAL:$2|Inin neneuhcayotl zan quipiya in tetoquilli tlahcuilolli.|{{PLURAL:$1|In tetoquilli tlahcuilolli itech pohui|In tetoquiltin $1 tlahcuiloltin itech pohui}}, inin neneuhcayotl itech tlacecempohualoni ipan $2.}}",
        "category-article-count-limited": "Inīn {{PLURAL:$1|zāzanilli cah|$1 zāzanilli cateh}} inīn neneuhcāyōc.",
        "category-file-count": "{{PLURAL:$2|Inìn tlaìxmatkàyòtlàlilòtl san kipia|Inìn tlaìxmatkàyòtlalilòtl kimpia {{PLURAL:$1|inìn èwalli|inîke $1 èwaltìn}}, ìwikpa $2.}}",
-       "category-file-count-limited": "{{PLURAL:$1|Inìn tlâkuilòlèwalli kä|Inîkë $1 tlâkuilòlèwaltìn katêkë}} ìpan inìn tlaìxmatkàtlàlilòtl.",
+       "category-file-count-limited": "{{PLURAL:$1|Inin tlahcuilolehualli kah|Inihqueh $1 tlahcuilolehualtin catehqueh}} ipan inin neneuhcayotl.",
        "listingcontinuesabbrev": "niman",
        "about": "Itechcopa",
        "article": "Tlahcuilolamatl",
        "newpage": "Yancuic tlahcuilolli",
        "talkpage": "Xictlahto inīn tlaīxtli ītechcopa",
        "talkpagelinktext": "Teixnamiquiliztli",
-       "specialpage": "Nònkuâkìskàtlaìxtlapalli",
+       "specialpage": "Noncuahquizcatlahcuilolamatl",
        "personaltools": "In tlein nitēquitiltilia",
        "articlepage": "Xiquitta tlahcuilolamatl",
        "talk": "Teixnamiquiliztli",
        "nstab-project": "Ìtlaìxtlapal in tlayẻkàntekitl",
        "nstab-image": "Tlahcuilolpiyalli",
        "nstab-mediawiki": "Tlahcuilōltzintli",
-       "nstab-template": "Nemachiòtl",
+       "nstab-template": "Nemachiyotilli",
        "nstab-help": "Tèpalèwilistli",
        "nstab-category": "Neneuhcayotl",
        "mainpage-nstab": "Yacatlahcuilolli",
        "withoutinterwiki-submit": "Tiquittāz",
        "nbytes": "$1 {{PLURAL:$1|byte}}",
        "ncategories": "$1 {{PLURAL:$1|tlaìxmatkàyòtlàlilòtl|tlaìxmatkàyòtlàlilòme}}",
-       "nlinks": "$1 {{PLURAL:$1|tzòwilistli|tzòwilistìn}}",
+       "nlinks": "$1 {{PLURAL:$1|tzohuililiztli|tzohuililiztin}}",
        "nmembers": "$1 {{PLURAL:$1|tlahcuilolamatl|tlahcuilolamameh}}",
        "nrevisions": "$1 {{PLURAL:$1|tlapiyaliztli}}",
        "nimagelinks": "Motekìuhtia ìpan $1 {{PLURAL:$1|tlaìxtlapalli|tlaìxtlapaltìn}}",
index f6a017d..1e3a940 100644 (file)
        "htmlform-user-not-exists": "<strong>$1</strong> nu există.",
        "htmlform-user-not-valid": "<strong>$1</strong> nu este un nume de utilizator valid.",
        "logentry-delete-delete": "$1 {{GENDER:$2|a șters}} pagina $3",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|a șters}} pagina de redirecționare $3 prin suprascriere",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restaurat}} pagina $3",
        "logentry-delete-event": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unui eveniment din jurnal|a $5 evenimente din jurnal|a $5 de evenimente din jurnal}} pentru $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unei versiuni|a $5 versiuni|a $5 de versiuni}} pentru pagina $3: $4",
index 01e785c..b496a9a 100644 (file)
        "restrictionsfield-badip": "Neveljaven IP-naslov ali obseg: $1",
        "restrictionsfield-label": "Dovoljeni IP-obsegi:",
        "restrictionsfield-help": "En IP-naslov ali CIDR-območje na vrstico. Da omogočite vse, uporabite<br><code>0.0.0.0/0</code><br><code>::/0</code>",
+       "revid": "redakcija $1",
        "pageid": "ID strani $1"
 }
index d2355c7..3e45278 100644 (file)
        "special-characters-group-ipa": "ХФӘ (IPA)",
        "special-characters-group-symbols": "Тамгалар",
        "special-characters-group-greek": "Грек",
+       "special-characters-group-greekextended": "Грек (киңәйтелгән)",
        "special-characters-group-cyrillic": "Кирилл",
        "special-characters-group-arabic": "Гарәп",
+       "special-characters-group-arabicextended": "Гарәп (киңәйтелгән)",
        "special-characters-group-persian": "Фарсы",
        "special-characters-group-hebrew": "Яхүд",
        "special-characters-group-bangla": "Бенгаль",
index 18c7f11..befafc1 100644 (file)
@@ -163,7 +163,7 @@ class BackupDumper extends Maintenance {
                                        $val = explode( ':', $param );
 
                                        if ( count( $val ) === 1 ) {
-                                               $this->loadPlugin( $val[0] );
+                                               $this->loadPlugin( $val[0], '' );
                                        } elseif ( count( $val ) === 2 ) {
                                                $this->loadPlugin( $val[0], $val[1] );
                                        } else {
index 07d50a8..b18f1e7 100644 (file)
@@ -20595,6 +20595,17 @@ language=sr variant=sr-ec
 </p>
 !! end
 
+!! test
+T146304: Don't break template parsing if language converter markup is in the parameter.
+!! options
+language=sr variant=sr-ec
+!! wikitext
+{{echo|-{R|foo}-}}
+!! html/php
+<p>foo
+</p>
+!! end
+
 # FIXME: This test is currently broken in the PHP parser (bug 52661)
 !! test
 Don't break image parsing if language converter markup is in the caption.
index fba01af..5259ec9 100644 (file)
@@ -305,8 +305,6 @@ return [
                "PhanParamSignatureMismatch",
                // approximate error count: 7
                "PhanParamSignatureMismatchInternal",
-               // approximate error count: 2
-               "PhanParamTooFew",
                // approximate error count: 308
                "PhanParamTooMany",
                // approximate error count: 3