Merge "More user related Revision construction test cases"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 24 Nov 2017 13:00:45 +0000 (13:00 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 24 Nov 2017 13:00:45 +0000 (13:00 +0000)
211 files changed:
RELEASE-NOTES-1.31
autoload.php
includes/Html.php
includes/OutputPage.php
includes/Preferences.php
includes/api/ApiBase.php
includes/api/ApiHelp.php
includes/api/ApiParamInfo.php
includes/api/ApiStashEdit.php
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/fr.json
includes/api/i18n/it.json
includes/api/i18n/ko.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/api/i18n/zh-hans.json
includes/changes/ChangesList.php
includes/diff/DifferenceEngine.php
includes/installer/InstallDocFormatter.php
includes/installer/i18n/pt.json
includes/jobqueue/jobs/CategoryMembershipChangeJob.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/lbfactory/LBFactoryMulti.php
includes/libs/rdbms/lbfactory/LBFactorySimple.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/media/TransformationalImageHandler.php
includes/parser/CoreParserFunctions.php
includes/parser/Sanitizer.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialUnblock.php
includes/specials/SpecialUserrights.php
languages/LanguageConverter.php
languages/classes/LanguageCrh.php [new file with mode: 0644]
languages/data/CrhExceptions.php [new file with mode: 0644]
languages/i18n/ais.json
languages/i18n/ar.json
languages/i18n/be-tarask.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/crh-cyrl.json
languages/i18n/crh-latn.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dty.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/eu.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/map-bms.json
languages/i18n/mk.json
languages/i18n/my.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/pl.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sat.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/ta.json
languages/i18n/tl.json
languages/i18n/ur.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesCrh.php
languages/messages/MessagesCrh_cyrl.php
languages/messages/MessagesCrh_latn.php
maintenance/Maintenance.php
maintenance/backup.inc
maintenance/benchmarks/benchmarkJSMinPlus.php
maintenance/benchmarks/benchmarkParse.php
maintenance/benchmarks/benchmarkPurge.php
maintenance/benchmarks/benchmarkTidy.php
maintenance/changePassword.php
maintenance/checkComposerLockUpToDate.php
maintenance/cleanupSpam.php
maintenance/cleanupTitles.php
maintenance/cleanupUploadStash.php
maintenance/compareParsers.php
maintenance/convertExtensionToRegistration.php
maintenance/copyFileBackend.php
maintenance/copyJobQueue.php
maintenance/createAndPromote.php
maintenance/createCommonPasswordCdb.php
maintenance/deleteBatch.php
maintenance/deleteDefaultMessages.php
maintenance/deleteEqualMessages.php
maintenance/dumpBackup.php
maintenance/dumpIterator.php
maintenance/edit.php
maintenance/eraseArchivedFile.php
maintenance/exportSites.php
maintenance/findHooks.php
maintenance/findOrphanedFiles.php
maintenance/fixDoubleRedirects.php
maintenance/fixTimestamps.php
maintenance/formatInstallDoc.php
maintenance/generateJsonI18n.php
maintenance/generateSitemap.php
maintenance/getConfiguration.php
maintenance/getText.php
maintenance/hhvm/makeRepo.php
maintenance/importDump.php
maintenance/importImages.php
maintenance/importTextFiles.php
maintenance/install.php
maintenance/invalidateUserSessions.php
maintenance/language/generateCollationData.php
maintenance/language/generateNormalizerDataAr.php
maintenance/language/langmemusage.php
maintenance/makeTestEdits.php
maintenance/manageJobs.php
maintenance/mctest.php
maintenance/mergeMessageFileList.php
maintenance/migrateFileRepoLayout.php
maintenance/migrateUserGroup.php
maintenance/minify.php
maintenance/moveBatch.php
maintenance/mwdocgen.php
maintenance/pageExists.php
maintenance/populateContentModel.php
maintenance/populateImageSha1.php
maintenance/populateRevisionLength.php
maintenance/populateRevisionSha1.php
maintenance/protect.php
maintenance/pruneFileCache.php
maintenance/purgeParserCache.php
maintenance/reassignEdits.php
maintenance/rebuildFileCache.php
maintenance/rebuildLocalisationCache.php
maintenance/rebuildSitesCache.php
maintenance/rebuildrecentchanges.php
maintenance/rebuildtextindex.php
maintenance/recountCategories.php
maintenance/refreshImageMetadata.php
maintenance/refreshLinks.php
maintenance/removeUnusedAccounts.php
maintenance/renameDbPrefix.php
maintenance/resetUserEmail.php
maintenance/rollbackEdits.php
maintenance/runJobs.php
maintenance/shell.php
maintenance/sql.php
maintenance/sqlite.php
maintenance/storage/checkStorage.php
maintenance/storage/compressOld.php
maintenance/storage/dumpRev.php
maintenance/storage/orphanStats.php
maintenance/syncFileBackend.php
maintenance/undelete.php
maintenance/update.php
maintenance/updateDoubleWidthSearch.php
maintenance/updateExtensionJsonSchema.php
maintenance/updateRestrictions.php
maintenance/updateSpecialPages.php
maintenance/userOptions.php
maintenance/validateRegistrationFile.php
maintenance/view.php
maintenance/wrapOldPasswords.php
package.json
resources/Resources.php
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.userrights.css
resources/src/mediawiki.special/mediawiki.special.userrights.js
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/MessageTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/diff/DifferenceEngineTest.php
tests/phpunit/includes/installer/InstallDocFormatterTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php
tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php [deleted file]
tests/phpunit/languages/classes/LanguageCrhTest.php [new file with mode: 0644]
tests/phpunit/structure/ApiStructureTest.php

index 8caab05..3de0e17 100644 (file)
@@ -98,6 +98,13 @@ changes to languages because of Phabricator reports.
 * Revision::setUserIdAndName() was deprecated.
 * Access to TitleValue class properties was deprecated, the relevant getters
   should be used instead.
+* DifferenceEngine::getDiffBodyCacheKey() is deprecated. Subclasses should
+  override DifferenceEngine::getDiffBodyCacheKeyParams() instead.
+* The deprecated MW_DIFF_VERSION constant was removed.
+  DifferenceEngine::MW_DIFF_VERSION should be used instead.
+* Use of Maintenance::error( $err, $die ) to exit script was deprecated. Use
+  Maintenance::fatalError() instead.
+* Passing a ParserOptions object to OutputPage::parserOptions() is deprecated.
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. There is experimental support for
index 5005534..5a2156a 100644 (file)
@@ -312,6 +312,7 @@ $wgAutoloadLocalClasses = [
        'CreateAndPromote' => __DIR__ . '/maintenance/createAndPromote.php',
        'CreateFileOp' => __DIR__ . '/includes/libs/filebackend/fileop/CreateFileOp.php',
        'CreditsAction' => __DIR__ . '/includes/actions/CreditsAction.php',
+       'CrhConverter' => __DIR__ . '/languages/classes/LanguageCrh.php',
        'CryptHKDF' => __DIR__ . '/includes/libs/CryptHKDF.php',
        'CryptRand' => __DIR__ . '/includes/libs/CryptRand.php',
        'CssContent' => __DIR__ . '/includes/content/CssContent.php',
@@ -706,6 +707,7 @@ $wgAutoloadLocalClasses = [
        'LanguageBs' => __DIR__ . '/languages/classes/LanguageBs.php',
        'LanguageCode' => __DIR__ . '/languages/LanguageCode.php',
        'LanguageConverter' => __DIR__ . '/languages/LanguageConverter.php',
+       'LanguageCrh' => __DIR__ . '/languages/classes/LanguageCrh.php',
        'LanguageCu' => __DIR__ . '/languages/classes/LanguageCu.php',
        'LanguageDsb' => __DIR__ . '/languages/classes/LanguageDsb.php',
        'LanguageEn' => __DIR__ . '/languages/classes/LanguageEn.php',
@@ -885,6 +887,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Interwiki\\ClassicInterwikiLookup' => __DIR__ . '/includes/interwiki/ClassicInterwikiLookup.php',
        'MediaWiki\\Interwiki\\InterwikiLookup' => __DIR__ . '/includes/interwiki/InterwikiLookup.php',
        'MediaWiki\\Interwiki\\InterwikiLookupAdapter' => __DIR__ . '/includes/interwiki/InterwikiLookupAdapter.php',
+       'MediaWiki\\Languages\\Data\\CrhExceptions' => __DIR__ . '/languages/data/CrhExceptions.php',
        'MediaWiki\\Languages\\Data\\Names' => __DIR__ . '/languages/data/Names.php',
        'MediaWiki\\Languages\\Data\\ZhConversion' => __DIR__ . '/languages/data/ZhConversion.php',
        'MediaWiki\\Linker\\LinkRenderer' => __DIR__ . '/includes/linker/LinkRenderer.php',
index 524fdcd..dfd80a8 100644 (file)
@@ -683,7 +683,7 @@ class Html {
         * @param string $heading (optional)
         * @return string of HTML representing a box.
         */
-       public static function messageBox( $html, $className, $heading = '' ) {
+       private static function messageBox( $html, $className, $heading = '' ) {
                if ( $heading ) {
                        $html = self::element( 'h2', [], $heading ) . $html;
                }
index 4635f99..a5f9c18 100644 (file)
@@ -1573,10 +1573,14 @@ class OutputPage extends ContextSource {
         * Get/set the ParserOptions object to use for wikitext parsing
         *
         * @param ParserOptions|null $options Either the ParserOption to use or null to only get the
-        *   current ParserOption object
+        *   current ParserOption object. This parameter is deprecated since 1.31.
         * @return ParserOptions
         */
        public function parserOptions( $options = null ) {
+               if ( $options !== null ) {
+                       wfDeprecated( __METHOD__ . ' with non-null $options', '1.31' );
+               }
+
                if ( $options !== null && !empty( $options->isBogus ) ) {
                        // Someone is trying to set a bogus pre-$wgUser PO. Check if it has
                        // been changed somehow, and keep it if so.
index e383f03..924e3ad 100644 (file)
@@ -1134,18 +1134,20 @@ class Preferences {
                        $defaultPreferences['watchlisttoken'] = [
                                'type' => 'api',
                        ];
+
+                       $tokenButton = new OOUI\ButtonWidget( [
+                               'href' => SpecialPage::getTitleFor( 'ResetTokens' )->getLinkURL( [
+                                       'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
+                               ] ),
+                               'label' => $context->msg( 'prefs-watchlist-managetokens' )->text(),
+                       ] );
                        $defaultPreferences['watchlisttoken-info'] = [
                                'type' => 'info',
                                'section' => 'watchlist/tokenwatchlist',
                                'label-message' => 'prefs-watchlist-token',
-                               'default' => $user->getTokenFromOption( 'watchlisttoken' ),
-                               'help-message' => 'prefs-help-watchlist-token2',
-                       ];
-                       $defaultPreferences['watchlisttoken-info2'] = [
-                               'type' => 'info',
-                               'section' => 'watchlist/tokenwatchlist',
+                               'help-message' => 'prefs-help-tokenmanagement',
                                'raw' => true,
-                               'default' => $context->msg( 'prefs-help-watchlist-token2' )->parse(),
+                               'default' => (string)$tokenButton,
                        ];
                }
        }
index bf2b977..83d2ae9 100644 (file)
@@ -217,6 +217,18 @@ abstract class ApiBase extends ContextSource {
         */
        const PARAM_ISMULTI_LIMIT2 = 22;
 
+       /**
+        * (integer) Maximum length of a string in bytes (in UTF-8 encoding).
+        * @since 1.31
+        */
+       const PARAM_MAX_BYTES = 23;
+
+       /**
+        * (integer) Maximum length of a string in characters (unicode codepoints).
+        * @since 1.31
+        */
+       const PARAM_MAX_CHARS = 24;
+
        /**@}*/
 
        const ALL_DEFAULT_STRING = '*';
@@ -1173,9 +1185,9 @@ abstract class ApiBase extends ContextSource {
                        );
                }
 
-               // More validation only when choices were not given
-               // choices were validated in parseMultiValue()
                if ( isset( $value ) ) {
+                       // More validation only when choices were not given
+                       // choices were validated in parseMultiValue()
                        if ( !is_array( $type ) ) {
                                switch ( $type ) {
                                        case 'NULL': // nothing to do
@@ -1285,6 +1297,23 @@ abstract class ApiBase extends ContextSource {
                                $value = array_unique( $value );
                        }
 
+                       if ( in_array( $type, [ 'NULL', 'string', 'text', 'password' ], true ) ) {
+                               foreach ( (array)$value as $val ) {
+                                       if ( isset( $paramSettings[self::PARAM_MAX_BYTES] )
+                                               && strlen( $val ) > $paramSettings[self::PARAM_MAX_BYTES]
+                                       ) {
+                                               $this->dieWithError( [ 'apierror-maxbytes', $encParamName,
+                                                       $paramSettings[self::PARAM_MAX_BYTES] ] );
+                                       }
+                                       if ( isset( $paramSettings[self::PARAM_MAX_CHARS] )
+                                               && mb_strlen( $val, 'UTF-8' ) > $paramSettings[self::PARAM_MAX_CHARS]
+                                       ) {
+                                               $this->dieWithError( [ 'apierror-maxchars', $encParamName,
+                                                       $paramSettings[self::PARAM_MAX_CHARS] ] );
+                                       }
+                               }
+                       }
+
                        // Set a warning if a deprecated parameter has been passed
                        if ( $deprecated && $value !== false ) {
                                $feature = $encParamName;
index ea4f724..529b32c 100644 (file)
@@ -713,6 +713,15 @@ class ApiHelp extends ApiBase {
                                                }
                                        }
 
+                                       if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
+                                               $info[] = $context->msg( 'api-help-param-maxbytes' )
+                                                       ->numParams( $settings[self::PARAM_MAX_BYTES] );
+                                       }
+                                       if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
+                                               $info[] = $context->msg( 'api-help-param-maxchars' )
+                                                       ->numParams( $settings[self::PARAM_MAX_CHARS] );
+                                       }
+
                                        // Add default
                                        $default = isset( $settings[ApiBase::PARAM_DFLT] )
                                                ? $settings[ApiBase::PARAM_DFLT]
index 2fa20a9..93fc51a 100644 (file)
@@ -471,6 +471,12 @@ class ApiParamInfo extends ApiBase {
                        if ( !empty( $settings[ApiBase::PARAM_RANGE_ENFORCE] ) ) {
                                $item['enforcerange'] = true;
                        }
+                       if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
+                               $item['maxbytes'] = $settings[self::PARAM_MAX_BYTES];
+                       }
+                       if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
+                               $item['maxchars'] = $settings[self::PARAM_MAX_CHARS];
+                       }
                        if ( !empty( $settings[ApiBase::PARAM_DEPRECATED_VALUES] ) ) {
                                $deprecatedValues = array_keys( $settings[ApiBase::PARAM_DEPRECATED_VALUES] );
                                if ( is_array( $item['type'] ) ) {
index 4bd6a3f..b4b9321 100644 (file)
@@ -181,9 +181,14 @@ class ApiStashEdit extends ApiBase {
                $title = $page->getTitle();
                $key = self::getStashKey( $title, self::getContentHash( $content ), $user );
 
-               // Use the master DB for fast blocking locks
+               // Use the master DB to allow for fast blocking locks on the "save path" where this
+               // value might actually be used to complete a page edit. If the edit submission request
+               // happens before this edit stash requests finishes, then the submission will block until
+               // the stash request finishes parsing. For the lock acquisition below, there is not much
+               // need to duplicate parsing of the same content/user/summary bundle, so try to avoid
+               // blocking at all here.
                $dbw = wfGetDB( DB_MASTER );
-               if ( !$dbw->lock( $key, __METHOD__, 1 ) ) {
+               if ( !$dbw->lock( $key, __METHOD__, 0 ) ) {
                        // De-duplicate requests on the same key
                        return self::ERROR_BUSY;
                }
index d3273db..ef2776f 100644 (file)
        "api-help-param-disabled-in-miser-mode": "Deaktiviert aufgrund des [[mw:Special:MyLanguage/Manual:$wgMiserMode|Miser-Modus]].",
        "api-help-param-continue": "Falls weitere Ergebnisse verfügbar sind, dies zum Fortfahren verwenden.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(keine Beschreibung)</span>",
+       "api-help-param-maxbytes": "Kann nicht länger sein als {{PLURAL:$1|ein Byte|$1 Bytes}}.",
+       "api-help-param-maxchars": "Kann nicht länger sein als {{PLURAL:$1|ein|$1}} Zeichen.",
        "api-help-examples": "{{PLURAL:$1|Beispiel|Beispiele}}:",
        "api-help-permissions": "{{PLURAL:$1|Berechtigung|Berechtigungen}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Gewährt an}}: $2",
        "apierror-invalid-file-key": "Kein gültiger Dateischlüssel.",
        "apierror-invalidsection": "Der Parameter <var>section</var> muss eine gültige Abschnittskennung oder <kbd>new</kbd> sein.",
        "apierror-invaliduserid": "Die Benutzerkennung <var>$1</var> ist nicht gültig.",
+       "apierror-maxbytes": "Der Parameter <var>$1</var> kann nicht länger sein als {{PLURAL:$2|ein Byte|$2 Bytes}}",
+       "apierror-maxchars": "Der Parameter <var>$1</var> kann nicht länger sein als {{PLURAL:$2|ein|$2}} Zeichen",
        "apierror-nosuchsection": "Es gibt keinen Abschnitt $1.",
        "apierror-nosuchuserid": "Es gibt keinen Benutzer mit der Kennung $1.",
        "apierror-offline": "Aufgrund von Problemen bei der Netzwerkverbindung kannst du nicht weitermachen. Stelle sicher, dass du eine funktionierende Internetverbindung hast und versuche es erneut.",
index dbd5451..85f17de 100644 (file)
        "api-help-param-direction": "In which direction to enumerate:\n;newer:List oldest first. Note: $1start has to be before $1end.\n;older:List newest first (default). Note: $1start has to be later than $1end.",
        "api-help-param-continue": "When more results are available, use this to continue.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(no description)</span>",
+       "api-help-param-maxbytes": "Cannot be longer than $1 {{PLURAL:$1|byte|bytes}}.",
+       "api-help-param-maxchars": "Cannot be longer than $1 {{PLURAL:$1|character|characters}}.",
        "api-help-examples": "{{PLURAL:$1|Example|Examples}}:",
        "api-help-permissions": "{{PLURAL:$1|Permission|Permissions}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Granted to}}: $2",
        "apierror-invalidurlparam": "Invalid value for <var>$1urlparam</var> (<kbd>$2=$3</kbd>).",
        "apierror-invaliduser": "Invalid username \"$1\".",
        "apierror-invaliduserid": "User ID <var>$1</var> is not valid.",
+       "apierror-maxbytes": "Parameter <var>$1</var> cannot be longer than $2 {{PLURAL:$2|byte|bytes}}",
+       "apierror-maxchars": "Parameter <var>$1</var> cannot be longer than $2 {{PLURAL:$2|character|characters}}",
        "apierror-maxlag-generic": "Waiting for a database server: $1 {{PLURAL:$1|second|seconds}} lagged.",
        "apierror-maxlag": "Waiting for $2: $1 {{PLURAL:$1|second|seconds}} lagged.",
        "apierror-mimesearchdisabled": "MIME search is disabled in Miser Mode.",
index bdaf58c..c5ab016 100644 (file)
        "api-help-param-direction": "Dans quelle direction énumérer :\n;newer:Lister les plus anciens en premier. Note : $1start doit être avant $1end.\n;older:Lister les nouveaux en premier (par défaut). Note : $1start doit être postérieur à $1end.",
        "api-help-param-continue": "Quand plus de résultats sont disponibles, utiliser cela pour continuer.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(aucune description)</span>",
+       "api-help-param-maxbytes": "Ne peut excéder $1 octet{{PLURAL:$1||s}}.",
+       "api-help-param-maxchars": "Ne peut excéder $1 caractères{{PLURAL:$1||s}}.",
        "api-help-examples": "{{PLURAL:$1|Exemple|Exemples}} :",
        "api-help-permissions": "{{PLURAL:$1|Droit|Droits}} :",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Accordé à}} : $2",
        "apierror-invalidurlparam": "Valeur non valide pour <var>$1urlparam</var> (<kbd>$2=$3</kbd>).",
        "apierror-invaliduser": "Nom d'utilisateur invalide \"$1\".",
        "apierror-invaliduserid": "L'ID d'utilisateur <var>$1</var> n'est pas valide.",
+       "apierror-maxbytes": "Le paramètre <var>$1</var> ne peut excéder $2 octets{{PLURAL:$2||s}}",
+       "apierror-maxchars": "Le paramètre <var>$1</var> ne peut excéder $2 catactères{{PLURAL:$2||s}}",
        "apierror-maxlag-generic": "Attente d’un serveur de base de données : $1 {{PLURAL:$1|seconde|secondes}} de délai.",
        "apierror-maxlag": "Attente de $2 : $1 {{PLURAL:$1|seconed|secondes}} de délai.",
        "apierror-mimesearchdisabled": "La recherche MIME est désactivée en mode Misère.",
index fd88c18..5682036 100644 (file)
        "api-help-param-token": "Un token \"$1\" recuperato da [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
        "api-help-param-continue": "Quando più risultati sono disponibili, usa questo per continuare.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(nessuna descrizione)</span>",
+       "api-help-param-maxbytes": "Non può essere più lungo di $1 {{PLURAL:$1|byte}}.",
+       "api-help-param-maxchars": "Non può essere più lungo di $1 {{PLURAL:$1|carattere|caratteri}}.",
        "api-help-examples": "{{PLURAL:$1|Esempio|Esempi}}:",
        "api-help-permissions": "{{PLURAL:$1|Permesso|Permessi}}:",
        "api-help-open-in-apisandbox": "<small>[apri in una sandbox]</small>",
        "api-help-authmanagerhelper-additional-params": "Questo modulo accetta parametri aggiuntivi a seconda delle richieste di autenticazione disponibili. Utilizza <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd> (o una precedente risposta da questo modulo, se applicabile) per determinare le richieste disponibili e i campi usati da queste.",
        "apierror-invalidoldimage": "Il parametro <var>oldimage</var> ha un formato non valido.",
        "apierror-invaliduserid": "L'ID utente <var>$1</var> non è valido.",
+       "apierror-maxbytes": "Il parametro <var>$1</var> non può essere più lungo di $2 {{PLURAL:$2|byte}}",
+       "apierror-maxchars": "Il parametro <var>$1</var> non può essere più lungo di $2 {{PLURAL:$2|carattere|caratteri}}",
        "apierror-nosuchuserid": "Non c'è alcun utente con ID $1.",
        "apierror-timeout": "Il server non ha risposto entro il tempo previsto.",
        "api-credits-header": "Crediti"
index a2dc344..034a033 100644 (file)
        "api-help-param-token-webui": "호환성을 위해, 웹 UI에 사용된 토큰도 허용합니다.",
        "api-help-param-continue": "더 많은 결과를 이용할 수 있을 때, 계속하려면 이것을 사용하십시오.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(설명 없음)</span>",
+       "api-help-param-maxbytes": "$1{{PLURAL:$1|바이트}}를 초과할 수 없습니다.",
+       "api-help-param-maxchars": "$1{{PLURAL:$1|자}}를 초과할 수 없습니다.",
        "api-help-examples": "{{PLURAL:$1|예시}}:",
        "api-help-permissions": "{{PLURAL:$1|권한}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|다음 그룹에 부여됨}}: $2",
        "apierror-invalidtitle": "잘못된 제목 \"$1\".",
        "apierror-invaliduser": "잘못된 사용자 이름 \"$1\".",
        "apierror-invaliduserid": "<var>$1</var> 사용자 ID는 유효하지 않습니다.",
+       "apierror-maxbytes": "<var>$1</var> 변수는 $2{{PLURAL:$2|바이트}}를 초과할 수 없습니다",
+       "apierror-maxchars": "<var>$1</var> 변수는 $2{{PLURAL:$2|자}}를 초과할 수 없습니다",
        "apierror-maxlag-generic": "데이터베이스 서버 대기 중: $1 {{PLURAL:$1|초}} 지연되었습니다.",
        "apierror-maxlag": "$2 대기 중: $1 {{PLURAL:$1|초}} 지연되었습니다.",
        "apierror-missingcontent-revid": "ID $1 판에 해당하는 내용이 없습니다.",
index 8d4cedc..a4ee954 100644 (file)
        "api-help-param-direction": "A direção da enumeração:\n;newer:Listar o mais antigo primeiro. Nota: $1start tem de estar antes de $1end.\n;older:Listar o mais recente primeiro (padrão). Nota: $1start tem de estar depois de $1end.",
        "api-help-param-continue": "Quando houver mais resultados disponíveis, usar isto para continuar",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(sem descrição)</span>",
+       "api-help-param-maxbytes": "Não pode exceder $1 {{PLURAL:$1|byte|bytes}}.",
+       "api-help-param-maxchars": "Não pode exceder $1 {{PLURAL:$1|carácter|caracteres}}.",
        "api-help-examples": "{{PLURAL:$1|Exemplo|Exemplos}}:",
        "api-help-permissions": "{{PLURAL:$1|Permissão|Permissões}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Concedida a|Concedidas a}}: $2",
        "apierror-invalidurlparam": "Valor inválido para <var>$1urlparam</var> (<kbd>$2=$3</kbd>).",
        "apierror-invaliduser": "Nome de utilizador inválido \"$1\".",
        "apierror-invaliduserid": "O identificador de utilizador <var>$1</var> não é válido.",
+       "apierror-maxbytes": "O parâmetro <var>$1</var> não pode exceder $2 {{PLURAL:$2|byte|bytes}}",
+       "apierror-maxchars": "O parâmetro <var>$1</var> não pode exceder $2 {{PLURAL:$2|carácter|caracteres}}",
        "apierror-maxlag-generic": "À espera de um servidor de base de dados: $1 {{PLURAL:$1|segundo|segundos}} de atraso.",
        "apierror-maxlag": "À espera de $2: $1 {{PLURAL:$1|segundo|segundos}} de atraso.",
        "apierror-mimesearchdisabled": "A pesquisa MIME é desativada no modo avarento.",
index 6aaaac7..3bdf7c6 100644 (file)
        "api-help-param-direction": "{{doc-apihelp-param|description=any standard \"dir\" parameter|noseealso=1}}",
        "api-help-param-continue": "{{doc-apihelp-param|description=any standard \"continue\" parameter, or other parameter with the same semantics|noseealso=1}}",
        "api-help-param-no-description": "Displayed on API parameters that lack any description",
+       "api-help-param-maxbytes": "Used to display the maximum allowed length of a parameter, in bytes.",
+       "api-help-param-maxchars": "Used to display the maximum allowed length of a parameter, in characters.",
        "api-help-examples": "Label for the API help examples section\n\nParameters:\n* $1 - Number of examples to be displayed\n{{Identical|Example}}",
        "api-help-permissions": "Label for the \"permissions\" section in the main module's help output.\n\nParameters:\n* $1 - Number of permissions displayed\n{{Identical|Permission}}",
        "api-help-permissions-granted-to": "Used to introduce the list of groups each permission is assigned to.\n\nParameters:\n* $1 - Number of groups\n* $2 - List of group names, comma-separated",
        "apierror-invalidurlparam": "{{doc-apierror}}\n\nParameters:\n* $1 - Module parameter prefix, e.g. \"bl\".\n* $2 - Key\n* $3 - Value.",
        "apierror-invaliduser": "{{doc-apierror}}\n\nParameters:\n* $1 - User name that is invalid.",
        "apierror-invaliduserid": "{{doc-apierror}}",
+       "apierror-maxbytes": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n* $2 - Maximum allowed bytes.",
+       "apierror-maxchars": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n* $2 - Maximum allowed characters.",
        "apierror-maxlag-generic": "{{doc-apierror}}\n\nParameters:\n* $1 - Database is lag in seconds.",
        "apierror-maxlag": "{{doc-apierror}}\n\nParameters:\n* $1 - Database lag in seconds.\n* $2 - Database server that is lagged.",
        "apierror-mimesearchdisabled": "{{doc-apierror}}",
index d09c651..5ee0ec4 100644 (file)
        "api-help-param-direction": "列举的方向:\n;newer:最早的优先。注意:$1start应早于$1end。\n;older:最新的优先(默认)。注意:$1start应晚于$1end。",
        "api-help-param-continue": "当更多结果可用时,使用这个继续。",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(没有说明)</span>",
+       "api-help-param-maxbytes": "不能超过$1{{PLURAL:$1|字节}}。",
+       "api-help-param-maxchars": "不能超过$1个{{PLURAL:$1|字符}}。",
        "api-help-examples": "{{PLURAL:$1|例子}}:",
        "api-help-permissions": "{{PLURAL:$1|权限}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|授予}}:$2",
        "apierror-invalidurlparam": "<var>$1urlparam</var>的值无效(<kbd>$2=$3</kbd>)。",
        "apierror-invaliduser": "无效用户名“$1”。",
        "apierror-invaliduserid": "用户ID<var>$1</var>无效。",
+       "apierror-maxbytes": "参数<var>$1</var>不能超过$2{{PLURAL:$2|字节}}",
+       "apierror-maxchars": "参数<var>$1</var>不能超过$2个{{PLURAL:$2|字符}}",
        "apierror-maxlag-generic": "正在等待数据库服务器:已延迟$1{{PLURAL:$1|秒}}。",
        "apierror-maxlag": "正在等待$2:已延迟$1{{PLURAL:$1|秒}}。",
        "apierror-mimesearchdisabled": "MIME搜索在Miser模式中被禁用。",
index bc50096..5b8559e 100644 (file)
@@ -576,7 +576,9 @@ class ChangesList extends ContextSource {
                        return '';
                }
                $cache = $this->watchMsgCache;
-               return $cache->getWithSetCallback( $count, $cache::TTL_INDEFINITE,
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'watching-users-msg', $count ),
+                       $cache::TTL_INDEFINITE,
                        function () use ( $count ) {
                                return $this->msg( 'number_of_watching_users_RCview' )
                                        ->numParams( $count )->escaped();
index 813ee08..51b9f15 100644 (file)
@@ -23,9 +23,6 @@
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Shell\Shell;
 
-/** @deprecated use class constant instead */
-define( 'MW_DIFF_VERSION', '1.11a' );
-
 /**
  * @todo document
  * @ingroup DifferenceEngine
@@ -37,7 +34,7 @@ class DifferenceEngine extends ContextSource {
         * fixes important bugs or such to force cached diff views to
         * clear.
         */
-       const DIFF_VERSION = MW_DIFF_VERSION;
+       const DIFF_VERSION = '1.12';
 
        /** @var int */
        public $mOldid;
@@ -753,14 +750,22 @@ class DifferenceEngine extends ContextSource {
                $key = false;
                $cache = ObjectCache::getMainWANInstance();
                if ( $this->mOldid && $this->mNewid ) {
+                       // Check if subclass is still using the old way
+                       // for backwards-compatibility
                        $key = $this->getDiffBodyCacheKey();
+                       if ( $key === null ) {
+                               $key = call_user_func_array(
+                                       [ $cache, 'makeKey' ],
+                                       $this->getDiffBodyCacheKeyParams()
+                               );
+                       }
 
                        // Try cache
                        if ( !$this->mRefreshCache ) {
                                $difftext = $cache->get( $key );
                                if ( $difftext ) {
                                        wfIncrStats( 'diff_cache.hit' );
-                                       $difftext = $this->localiseLineNumbers( $difftext );
+                                       $difftext = $this->localiseDiff( $difftext );
                                        $difftext .= "\n<!-- diff cache key $key -->\n";
 
                                        return $difftext;
@@ -788,9 +793,9 @@ class DifferenceEngine extends ContextSource {
                } else {
                        wfIncrStats( 'diff_cache.uncacheable' );
                }
-               // Replace line numbers with the text in the user's language
+               // localise line numbers and title attribute text
                if ( $difftext !== false ) {
-                       $difftext = $this->localiseLineNumbers( $difftext );
+                       $difftext = $this->localiseDiff( $difftext );
                }
 
                return $difftext;
@@ -799,18 +804,49 @@ class DifferenceEngine extends ContextSource {
        /**
         * Returns the cache key for diff body text or content.
         *
+        * @deprecated since 1.31, use getDiffBodyCacheKeyParams() instead
         * @since 1.23
         *
         * @throws MWException
-        * @return string
+        * @return string|null
         */
        protected function getDiffBodyCacheKey() {
+               return null;
+       }
+
+       /**
+        * Get the cache key parameters
+        *
+        * Subclasses can replace the first element in the array to something
+        * more specific to the type of diff (e.g. "inline-diff"), or append
+        * if the cache should vary on more things. Overriding entirely should
+        * be avoided.
+        *
+        * @since 1.31
+        *
+        * @return array
+        * @throws MWException
+        */
+       protected function getDiffBodyCacheKeyParams() {
                if ( !$this->mOldid || !$this->mNewid ) {
                        throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
                }
 
-               return wfMemcKey( 'diff', 'version', self::DIFF_VERSION,
-                       'oldid', $this->mOldid, 'newid', $this->mNewid );
+               $engine = $this->getEngine();
+               $params = [
+                       'diff',
+                       $engine,
+                       self::DIFF_VERSION,
+                       "old-{$this->mOldid}",
+                       "rev-{$this->mNewid}"
+               ];
+
+               if ( $engine === 'wikidiff2' ) {
+                       $params[] = phpversion( 'wikidiff2' );
+                       $params[] = $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' );
+               }
+
+               return $params;
        }
 
        /**
@@ -897,19 +933,14 @@ class DifferenceEngine extends ContextSource {
        }
 
        /**
-        * Generates diff, to be wrapped internally in a logging/instrumentation
+        * Process $wgExternalDiffEngine and get a sane, usable engine
         *
-        * @param string $otext Old text, must be already segmented
-        * @param string $ntext New text, must be already segmented
-        * @return bool|string
-        * @throws Exception
+        * @return bool|string 'wikidiff2', path to an executable, or false
         */
-       protected function textDiff( $otext, $ntext ) {
-               global $wgExternalDiffEngine, $wgContLang;
-
-               $otext = str_replace( "\r\n", "\n", $otext );
-               $ntext = str_replace( "\r\n", "\n", $ntext );
-
+       private function getEngine() {
+               global $wgExternalDiffEngine;
+               // We use the global here instead of Config because we write to the value,
+               // and Config is not mutable.
                if ( $wgExternalDiffEngine == 'wikidiff' || $wgExternalDiffEngine == 'wikidiff3' ) {
                        wfDeprecated( "\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'", '1.27' );
                        $wgExternalDiffEngine = false;
@@ -922,9 +953,34 @@ class DifferenceEngine extends ContextSource {
                        $wgExternalDiffEngine = false;
                }
 
+               if ( is_string( $wgExternalDiffEngine ) && is_executable( $wgExternalDiffEngine ) ) {
+                       return $wgExternalDiffEngine;
+               } elseif ( $wgExternalDiffEngine === false && function_exists( 'wikidiff2_do_diff' ) ) {
+                       return 'wikidiff2';
+               } else {
+                       // Native PHP
+                       return false;
+               }
+       }
+
+       /**
+        * Generates diff, to be wrapped internally in a logging/instrumentation
+        *
+        * @param string $otext Old text, must be already segmented
+        * @param string $ntext New text, must be already segmented
+        * @return bool|string
+        */
+       protected function textDiff( $otext, $ntext ) {
+               global $wgContLang;
+
+               $otext = str_replace( "\r\n", "\n", $otext );
+               $ntext = str_replace( "\r\n", "\n", $ntext );
+
+               $engine = $this->getEngine();
+
                // Better external diff engine, the 2 may some day be dropped
                // This one does the escaping and segmenting itself
-               if ( function_exists( 'wikidiff2_do_diff' ) && $wgExternalDiffEngine === false ) {
+               if ( $engine === 'wikidiff2' ) {
                        $wikidiff2Version = phpversion( 'wikidiff2' );
                        if (
                                $wikidiff2Version !== false &&
@@ -954,7 +1010,7 @@ class DifferenceEngine extends ContextSource {
                        $text .= $this->debug( 'wikidiff2' );
 
                        return $text;
-               } elseif ( $wgExternalDiffEngine !== false && is_executable( $wgExternalDiffEngine ) ) {
+               } elseif ( $engine !== false ) {
                        # Diff via the shell
                        $tmpDir = wfTempDir();
                        $tempName1 = tempnam( $tmpDir, 'diff_' );
@@ -972,7 +1028,7 @@ class DifferenceEngine extends ContextSource {
                        fwrite( $tempFile2, $ntext );
                        fclose( $tempFile1 );
                        fclose( $tempFile2 );
-                       $cmd = [ $wgExternalDiffEngine, $tempName1, $tempName2 ];
+                       $cmd = [ $engine, $tempName1, $tempName2 ];
                        $result = Shell::command( $cmd )
                                ->execute();
                        $exitCode = $result->getExitCode();
@@ -982,7 +1038,7 @@ class DifferenceEngine extends ContextSource {
                                );
                        }
                        $difftext = $result->getStdout();
-                       $difftext .= $this->debug( "external $wgExternalDiffEngine" );
+                       $difftext .= $this->debug( "external $engine" );
                        unlink( $tempName1 );
                        unlink( $tempName2 );
 
@@ -1024,6 +1080,22 @@ class DifferenceEngine extends ContextSource {
                        " -->\n";
        }
 
+       /**
+        * Localise diff output
+        *
+        * @param string $text
+        * @return string
+        */
+       private function localiseDiff( $text ) {
+               $text = $this->localiseLineNumbers( $text );
+               if ( $this->getEngine() === 'wikidiff2' &&
+                       version_compare( phpversion( 'wikidiff2' ), '1.5.1', '>=' )
+               ) {
+                       $text = $this->addLocalisedTitleTooltips( $text );
+               }
+               return $text;
+       }
+
        /**
         * Replace line numbers with the text in the user's language
         *
@@ -1047,6 +1119,31 @@ class DifferenceEngine extends ContextSource {
                return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped();
        }
 
+       /**
+        * Add title attributes for tooltips on moved paragraph indicators
+        *
+        * @param string $text
+        * @return string
+        */
+       private function addLocalisedTitleTooltips( $text ) {
+               return preg_replace_callback(
+                       '/class="mw-diff-movedpara-(left|right)"/',
+                       [ $this, 'addLocalisedTitleTooltipsCb' ],
+                       $text
+               );
+       }
+
+       /**
+        * @param array $matches
+        * @return string
+        */
+       private function addLocalisedTitleTooltipsCb( array $matches ) {
+               $key = $matches[1] === 'right' ?
+                       'diff-paragraph-moved-toold' :
+                       'diff-paragraph-moved-tonew';
+               return $matches[0] . ' title="' . $this->msg( $key )->escaped() . '"';
+       }
+
        /**
         * If there are revisions between the ones being compared, return a note saying so.
         *
index 4163e2f..08cfd86 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 class InstallDocFormatter {
-       static function format( $text ) {
+       public static function format( $text ) {
                $obj = new self( $text );
 
                return $obj->execute();
index a6ebd92..adc3e34 100644 (file)
@@ -35,7 +35,7 @@
        "config-session-expired": "Os seus dados de sessão parecem ter expirado.\nAs sessões estão configuradas para uma duração de $1.\nPode aumentar esta duração configurando <code>session.gc_maxlifetime</code> no php.ini.\nReinicie o processo de instalação.",
        "config-no-session": "Os seus dados de sessão foram perdidos!\nVerifique o seu php.ini e certifique-se de que em <code>session.save_path</code> está definido um diretório apropriado.",
        "config-your-language": "A sua língua:",
-       "config-your-language-help": "Selecione o idioma que será usado durante o processo de instalação.",
+       "config-your-language-help": "Selecione a língua que será usada durante o processo de instalação.",
        "config-wiki-language": "Língua da wiki:",
        "config-wiki-language-help": "Selecione a língua que será predominante na wiki.",
        "config-back": "← Voltar",
index 55c1367..16640be 100644 (file)
@@ -25,6 +25,8 @@ use Wikimedia\Rdbms\LBFactory;
 /**
  * Job to add recent change entries mentioning category membership changes
  *
+ * This allows users to easily scan categories for recent page membership changes
+ *
  * Parameters include:
  *   - pageId : page ID
  *   - revTimestamp : timestamp of the triggering revision
@@ -59,6 +61,13 @@ class CategoryMembershipChangeJob extends Job {
                        return false; // deleted?
                }
 
+               // Cut down on the time spent in safeWaitForMasterPos() in the critical section
+               $dbr = $lb->getConnection( DB_REPLICA, [ 'recentchanges' ] );
+               if ( !$lb->safeWaitForMasterPos( $dbr ) ) {
+                       $this->setLastError( "Timed out while pre-waiting for replica DB to catch up" );
+                       return false;
+               }
+
                // Use a named lock so that jobs for this page see each others' changes
                $lockKey = "CategoryMembershipUpdates:{$page->getId()}";
                $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 3 );
@@ -67,8 +76,7 @@ class CategoryMembershipChangeJob extends Job {
                        return false;
                }
 
-               $dbr = $lb->getConnection( DB_REPLICA, [ 'recentchanges' ] );
-               // Wait till the replica DB is caught up so that jobs for this page see each others' changes
+               // Wait till replica DB is caught up so that jobs for this page see each others' changes
                if ( !$lb->safeWaitForMasterPos( $dbr ) ) {
                        $this->setLastError( "Timed out while waiting for replica DB to catch up" );
                        return false;
@@ -81,28 +89,28 @@ class CategoryMembershipChangeJob extends Job {
                // between COMMIT and actual enqueueing of the CategoryMembershipChangeJob job.
                $cutoffUnix -= self::ENQUEUE_FUDGE_SEC;
 
-               // Get the newest revision that has a SRC_CATEGORIZE row...
+               // Get the newest page revision that has a SRC_CATEGORIZE row.
+               // Assume that category changes before it were already handled.
                $row = $dbr->selectRow(
-                       [ 'revision', 'recentchanges' ],
+                       'revision',
                        [ 'rev_timestamp', 'rev_id' ],
                        [
                                'rev_page' => $page->getId(),
-                               'rev_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( $cutoffUnix ) )
-                       ],
-                       __METHOD__,
-                       [ 'ORDER BY' => 'rev_timestamp DESC, rev_id DESC' ],
-                       [
-                               'recentchanges' => [
-                                       'INNER JOIN',
+                               'rev_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( $cutoffUnix ) ),
+                               'EXISTS (' . $dbr->selectSQLText(
+                                       'recentchanges',
+                                       '1',
                                        [
                                                'rc_this_oldid = rev_id',
                                                'rc_source' => RecentChange::SRC_CATEGORIZE,
                                                // Allow rc_cur_id or rc_timestamp index usage
                                                'rc_cur_id = rev_page',
-                                               'rc_timestamp >= rev_timestamp'
+                                               'rc_timestamp = rev_timestamp'
                                        ]
-                               ]
-                       ]
+                               ) . ')'
+                       ],
+                       __METHOD__,
+                       [ 'ORDER BY' => 'rev_timestamp DESC, rev_id DESC' ]
                );
                // Only consider revisions newer than any such revision
                if ( $row ) {
index d6cb340..8420f11 100644 (file)
@@ -747,10 +747,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * Make a global cache key.
         *
         * @since 1.27
-        * @param string $keys,... Key component (starting with a key collection name)
+        * @param string $class Key class
+        * @param string $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         */
-       public function makeGlobalKey() {
+       public function makeGlobalKey( $class, $component = null ) {
                return $this->makeKeyInternal( 'global', func_get_args() );
        }
 
@@ -758,10 +759,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * Make a cache key, scoped to this instance's keyspace.
         *
         * @since 1.27
-        * @param string $keys,... Key component (starting with a key collection name)
+        * @param string $class Key class
+        * @param string $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         */
-       public function makeKey() {
+       public function makeKey( $class, $component = null ) {
                return $this->makeKeyInternal( $this->keyspace, func_get_args() );
        }
 
index c85a82e..ae434c1 100644 (file)
@@ -86,11 +86,11 @@ class CachedBagOStuff extends HashBagOStuff {
                return $this->backend->deleteObjectsExpiringBefore( $date, $progressCallback );
        }
 
-       public function makeKey() {
+       public function makeKey( $class, $component = null ) {
                return call_user_func_array( [ $this->backend, __FUNCTION__ ], func_get_args() );
        }
 
-       public function makeGlobalKey() {
+       public function makeGlobalKey( $class, $component = null ) {
                return call_user_func_array( [ $this->backend, __FUNCTION__ ], func_get_args() );
        }
 
index 0188991..f7bf86b 100644 (file)
@@ -137,7 +137,7 @@ class MemcachedBagOStuff extends BagOStuff {
                );
 
                if ( $charsLeft < 0 ) {
-                       return $keyspace . ':##' . md5( implode( ':', $args ) );
+                       return $keyspace . ':BagOStuff-long-key:##' . md5( implode( ':', $args ) );
                }
 
                return $keyspace . ':' . implode( ':', $args );
index 200ab79..643f318 100644 (file)
@@ -233,11 +233,11 @@ class MultiWriteBagOStuff extends BagOStuff {
                return $ret;
        }
 
-       public function makeKey() {
+       public function makeKey( $class, $component = null ) {
                return call_user_func_array( [ $this->caches[0], __FUNCTION__ ], func_get_args() );
        }
 
-       public function makeGlobalKey() {
+       public function makeGlobalKey( $class, $component = null ) {
                return call_user_func_array( [ $this->caches[0], __FUNCTION__ ], func_get_args() );
        }
 }
index 73e4a9a..e63b32e 100644 (file)
@@ -135,6 +135,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        const TTL_LAGGED = 30;
        /** Idiom for delete() for "no hold-off" */
        const HOLDOFF_NONE = 0;
+       /** Idiom for set() for "do not augment the storage medium TTL" */
+       const STALE_TTL_NONE = 0;
+
        /** Idiom for getWithSetCallback() for "no minimum required as-of timestamp" */
        const MIN_TIMESTAMP_NONE = 0.0;
 
@@ -204,7 +207,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return WANObjectCache
         */
        public static function newEmpty() {
-               return new self( [
+               return new static( [
                        'cache'   => new EmptyBagOStuff(),
                        'pool'    => 'empty'
                ] );
@@ -311,7 +314,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        $wrappedValues += $this->cache->getMulti( $keysGet );
                }
                // Time used to compare/init "check" keys (derived after getMulti() to be pessimistic)
-               $now = microtime( true );
+               $now = $this->getCurrentTime();
 
                // Collect timestamps from all "check" keys
                $purgeValuesForAll = $this->processCheckKeys( $checkKeysForAll, $wrappedValues, $now );
@@ -426,24 +429,25 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *      they certainly should not see ones that ended up getting rolled back.
         *      Default: false
         *   - lockTSE : if excessive replication/snapshot lag is detected, then store the value
-        *      with this TTL and flag it as stale. This is only useful if the reads for
-        *      this key use getWithSetCallback() with "lockTSE" set.
+        *      with this TTL and flag it as stale. This is only useful if the reads for this key
+        *      use getWithSetCallback() with "lockTSE" set. Note that if "staleTTL" is set
+        *      then it will still add on to this TTL in the excessive lag scenario.
         *      Default: WANObjectCache::TSE_NONE
         *   - staleTTL : Seconds to keep the key around if it is stale. The get()/getMulti()
         *      methods return such stale values with a $curTTL of 0, and getWithSetCallback()
         *      will call the regeneration callback in such cases, passing in the old value
         *      and its as-of time to the callback. This is useful if adaptiveTTL() is used
         *      on the old value's as-of time when it is verified as still being correct.
-        *      Default: 0.
+        *      Default: WANObjectCache::STALE_TTL_NONE.
         * @note Options added in 1.28: staleTTL
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
-               $now = microtime( true );
+               $now = $this->getCurrentTime();
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
+               $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : self::STALE_TTL_NONE;
                $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
-               $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
                if ( !empty( $opts['pending'] ) ) {
@@ -590,7 +594,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        $time = $purge[self::FLD_TIME];
                } else {
                        // Casting assures identical floats for the next getCheckKeyTime() calls
-                       $now = (string)microtime( true );
+                       $now = (string)$this->getCurrentTime();
                        $this->cache->add( $key,
                                $this->makePurgeValue( $now, self::HOLDOFF_TTL ),
                                self::CHECK_KEY_TTL
@@ -917,7 +921,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                        // Value existed before with a different version; use variant key.
                                        // Reflect purges to $key by requiring that this key value be newer.
                                        $value = $this->doGetWithSetCallback(
-                                               'cache-variant:' . md5( $key ) . ":$version",
+                                               $this->makeGlobalKey( 'WANCache-key-variant', md5( $key ), $version ),
                                                $ttl,
                                                $callback,
                                                // Regenerate value if not newer than $key
@@ -968,7 +972,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $cValue = $this->get( $key, $curTTL, $checkKeys, $asOf ); // current value
                $value = $cValue; // return value
 
-               $preCallbackTime = microtime( true );
+               $preCallbackTime = $this->getCurrentTime();
                // Determine if a cached value regeneration is needed or desired
                if ( $value !== false
                        && $curTTL > 0
@@ -1044,7 +1048,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                // so use a special INTERIM key to pass the new value around threads.
                if ( ( $isTombstone && $lockTSE > 0 ) && $valueIsCacheable ) {
                        $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
-                       $newAsOf = microtime( true );
+                       $newAsOf = $this->getCurrentTime();
                        $wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
                        // Avoid using set() to avoid pointless mcrouter broadcasting
                        $this->setInterimValue( $key, $wrapped, $tempTTL );
@@ -1077,7 +1081,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         */
        protected function getInterimValue( $key, $versioned, $minTime, &$asOf ) {
                $wrapped = $this->cache->get( self::INTERIM_KEY_PREFIX . $key );
-               list( $value ) = $this->unwrap( $wrapped, microtime( true ) );
+               list( $value ) = $this->unwrap( $wrapped, $this->getCurrentTime() );
                if ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
                        $asOf = $wrapped[self::FLD_TIME];
 
@@ -1388,21 +1392,23 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
        /**
         * @see BagOStuff::makeKey()
-        * @param string $keys,... Key component (starting with a key collection name)
+        * @param string $class Key class
+        * @param string $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         * @since 1.27
         */
-       public function makeKey() {
+       public function makeKey( $class, $component = null ) {
                return call_user_func_array( [ $this->cache, __FUNCTION__ ], func_get_args() );
        }
 
        /**
         * @see BagOStuff::makeGlobalKey()
-        * @param string $keys,... Key component (starting with a key collection name)
+        * @param string $class Key class
+        * @param string $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         * @since 1.27
         */
-       public function makeGlobalKey() {
+       public function makeGlobalKey( $class, $component = null ) {
                return call_user_func_array( [ $this->cache, __FUNCTION__ ], func_get_args() );
        }
 
@@ -1533,7 +1539,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                if ( $this->purgeRelayer instanceof EventRelayerNull ) {
                        // This handles the mcrouter and the single-DC case
                        $ok = $this->cache->set( $key,
-                               $this->makePurgeValue( microtime( true ), self::HOLDOFF_NONE ),
+                               $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
                                $ttl
                        );
                } else {
@@ -1592,7 +1598,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool
         */
        protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
-               if ( $curTTL >= $lowTTL ) {
+               if ( $lowTTL <= 0 ) {
+                       return false;
+               } elseif ( $curTTL >= $lowTTL ) {
                        return false;
                } elseif ( $curTTL <= 0 ) {
                        return true;
@@ -1619,6 +1627,10 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool
         */
        protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now ) {
+               if ( $ageNew < 0 || $timeTillRefresh <= 0 ) {
+                       return false;
+               }
+
                $age = $now - $asOf;
                $timeOld = $age - $ageNew;
                if ( $timeOld <= 0 ) {
@@ -1740,6 +1752,14 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                return isset( $parts[1] ) ? $parts[1] : $parts[0]; // sanity
        }
 
+       /**
+        * @return float UNIX timestamp
+        * @codeCoverageIgnore
+        */
+       protected function getCurrentTime() {
+               return microtime( true );
+       }
+
        /**
         * @param string $value Wrapped value like "PURGED:<timestamp>:<holdoff>"
         * @return array|bool Array containing a UNIX timestamp (float) and holdoff period (integer),
index 0384588..cfa2647 100644 (file)
@@ -107,6 +107,12 @@ class LBFactoryMulti extends LBFactory {
        /** @var string */
        private $lastSection;
 
+       /** @var int */
+       private $maxLag = self::MAX_LAG_DEFAULT;
+
+       /** @var int Default 'maxLag' when unspecified */
+       const MAX_LAG_DEFAULT = 10;
+
        /**
         * @see LBFactory::__construct()
         *
@@ -160,6 +166,7 @@ class LBFactoryMulti extends LBFactory {
         *                                 storage cluster.
         *   - masterTemplateOverrides     Server configuration map overrides for all master servers.
         *   - loadMonitorClass            Name of the LoadMonitor class to always use.
+        *   - maxLag                      Avoid replica DBs with more lag than this many seconds.
         *   - readOnlyBySection           A map of section name to read-only message.
         *                                 Missing or false for read/write.
         */
@@ -171,7 +178,7 @@ class LBFactoryMulti extends LBFactory {
                $optional = [ 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
                        'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
                        'templateOverridesByCluster', 'templateOverridesBySection', 'masterTemplateOverrides',
-                       'readOnlyBySection', 'loadMonitorClass' ];
+                       'readOnlyBySection', 'maxLag', 'loadMonitorClass' ];
 
                foreach ( $required as $key ) {
                        if ( !isset( $conf[$key] ) ) {
@@ -319,6 +326,7 @@ class LBFactoryMulti extends LBFactory {
                        $this->baseLoadBalancerParams(),
                        [
                                'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
+                               'maxLag' => $this->maxLag,
                                'loadMonitor' => [ 'class' => $this->loadMonitorClass ],
                                'readOnlyReason' => $readOnlyReason
                        ]
index df0a806..9a6aa3a 100644 (file)
@@ -41,6 +41,11 @@ class LBFactorySimple extends LBFactory {
 
        /** @var string */
        private $loadMonitorClass;
+       /** @var int */
+       private $maxLag;
+
+       /** @var int Default 'maxLag' when unspecified */
+       const MAX_LAG_DEFAULT = 10;
 
        /**
         * @see LBFactory::__construct()
@@ -72,6 +77,7 @@ class LBFactorySimple extends LBFactory {
                $this->loadMonitorClass = isset( $conf['loadMonitorClass'] )
                        ? $conf['loadMonitorClass']
                        : 'LoadMonitor';
+               $this->maxLag = isset( $conf['maxLag'] ) ? $conf['maxLag'] : self::MAX_LAG_DEFAULT;
        }
 
        /**
@@ -128,6 +134,7 @@ class LBFactorySimple extends LBFactory {
                        $this->baseLoadBalancerParams(),
                        [
                                'servers' => $servers,
+                               'maxLag' => $this->maxLag,
                                'loadMonitor' => [ 'class' => $this->loadMonitorClass ],
                        ]
                ) );
index 22a5805..86c4335 100644 (file)
@@ -96,6 +96,7 @@ interface ILoadBalancer {
         *  - loadMonitor : Name of a class used to fetch server lag and load.
         *  - readOnlyReason : Reason the master DB is read-only if so [optional]
         *  - waitTimeout : Maximum time to wait for replicas for consistency [optional]
+        *  - maxLag: Avoid replica DB servers with more lag than this [optional]
         *  - srvCache : BagOStuff object for server cache [optional]
         *  - wanCache : WANObjectCache object [optional]
         *  - chronologyProtector: ChronologyProtector object [optional]
index 6bb8945..b622ddc 100644 (file)
@@ -115,11 +115,13 @@ class LoadBalancer implements ILoadBalancer {
        private $disabled = false;
        /** @var bool */
        private $chronProtInitialized = false;
+       /** @var int */
+       private $maxLag = self::MAX_LAG_DEFAULT;
 
        /** @var int Warn when this many connection are held */
        const CONN_HELD_WARN_THRESHOLD = 10;
 
-       /** @var int Default 'max lag' when unspecified */
+       /** @var int Default 'maxLag' when unspecified */
        const MAX_LAG_DEFAULT = 10;
        /** @var int Seconds to cache master server read-only status */
        const TTL_CACHE_READONLY = 5;
@@ -178,6 +180,10 @@ class LoadBalancer implements ILoadBalancer {
                        $this->readOnlyReason = $params['readOnlyReason'];
                }
 
+               if ( isset( $params['maxLag'] ) ) {
+                       $this->maxLag = $params['maxLag'];
+               }
+
                if ( isset( $params['loadMonitor'] ) ) {
                        $this->loadMonitorConfig = $params['loadMonitor'];
                } else {
@@ -275,7 +281,7 @@ class LoadBalancer implements ILoadBalancer {
                                # How much lag this server nominally is allowed to have
                                $maxServerLag = isset( $this->mServers[$i]['max lag'] )
                                        ? $this->mServers[$i]['max lag']
-                                       : self::MAX_LAG_DEFAULT; // default
+                                       : $this->maxLag; // default
                                # Constrain that futher by $maxLag argument
                                $maxServerLag = min( $maxServerLag, $maxLag );
 
index de438da..85430d2 100644 (file)
@@ -512,7 +512,7 @@ abstract class TransformationalImageHandler extends ImageHandler {
                $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
                $method = __METHOD__;
                return $cache->getWithSetCallback(
-                       'imagemagick-version',
+                       $cache->makeGlobalKey( 'imagemagick-version' ),
                        $cache::TTL_HOUR,
                        function () use ( $method ) {
                                global $wgImageMagickConvertCommand;
index bb0072c..07944d4 100644 (file)
@@ -930,7 +930,8 @@ class CoreParserFunctions {
         */
        public static function anchorencode( $parser, $text ) {
                $text = $parser->killMarkers( $text );
-               return (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
+               $section = (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
+               return Sanitizer::safeEncodeAttribute( $section );
        }
 
        public static function special( $parser, $text ) {
index 7c9f563..20fee2d 100644 (file)
@@ -1150,6 +1150,7 @@ class Sanitizer {
                        '{'    => '&#123;',
                        '}'    => '&#125;', // prevent unpaired language conversion syntax
                        '['    => '&#91;',
+                       ']'    => '&#93;',
                        "''"   => '&#39;&#39;',
                        'ISBN' => '&#73;SBN',
                        'RFC'  => '&#82;FC',
index a4f16bd..bf8dea6 100644 (file)
@@ -110,6 +110,8 @@ class SpecialPasswordReset extends FormSpecialPage {
        public function alterForm( HTMLForm $form ) {
                $resetRoutes = $this->getConfig()->get( 'PasswordResetRoutes' );
 
+               $form->setSubmitDestructive();
+
                $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
 
                $i = 0;
index 358a309..e4cc3d1 100644 (file)
@@ -30,6 +30,8 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
        /** @var bool|Title */
        protected $rclTargetTitle;
 
+       protected $rclTarget;
+
        function __construct() {
                parent::__construct( 'Recentchangeslinked' );
        }
@@ -44,6 +46,7 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
 
        public function parseParameters( $par, FormOptions $opts ) {
                $opts['target'] = $par;
+               $this->rclTarget = $par;
        }
 
        /**
@@ -293,4 +296,15 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
        public function prefixSearchSubpages( $search, $limit, $offset ) {
                return $this->prefixSearchString( $search, $limit, $offset );
        }
+
+       /**
+        * Get a self-referential title object
+        * with consideration to the given subpage.
+        *
+        * @return Title
+        * @since 1.23
+        */
+       public function getPageTitle() {
+               return parent::getPageTitle( $this->rclTarget );
+       }
 }
index 964a261..d5b0903 100644 (file)
@@ -121,6 +121,7 @@ class SpecialResetTokens extends FormSpecialPage {
         * @param HTMLForm $form
         */
        protected function alterForm( HTMLForm $form ) {
+               $form->setSubmitDestructive();
                if ( $this->getTokensList() ) {
                        $form->setSubmitTextMsg( 'resettokens-resetbutton' );
                } else {
index 0da9b7a..b2d5a16 100644 (file)
@@ -57,7 +57,7 @@ class SpecialUnblock extends SpecialPage {
 
                $out = $this->getOutput();
                $out->setPageTitle( $this->msg( 'unblockip' ) );
-               $out->addModules( [ 'mediawiki.special', 'mediawiki.userSuggest' ] );
+               $out->addModules( [ 'mediawiki.userSuggest' ] );
 
                $form = HTMLForm::factory( 'ooui', $this->getFields(), $this->getContext() );
                $form->setWrapperLegendMsg( 'unblockip' );
index 4e4394d..a5f9ab3 100644 (file)
@@ -140,7 +140,7 @@ class UserrightsPage extends SpecialPage {
                $this->setHeaders();
                $this->outputHeader();
 
-               $out->addModuleStyles( 'mediawiki.special.userrights.styles' );
+               $out->addModuleStyles( 'mediawiki.special' );
                $this->addHelpLink( 'Help:Assigning permissions' );
 
                $this->switchForm();
@@ -835,7 +835,10 @@ class UserrightsPage extends SpecialPage {
                        }
                        $ret .= "\t<td style='vertical-align:top;'>\n";
                        foreach ( $column as $group => $checkbox ) {
-                               $attr = $checkbox['disabled'] ? [ 'disabled' => 'disabled' ] : [];
+                               $attr = [ 'class' => 'mw-userrights-groupcheckbox' ];
+                               if ( $checkbox['disabled'] ) {
+                                       $attr['disabled'] = 'disabled';
+                               }
 
                                $member = UserGroupMembership::getGroupMemberName( $group, $user->getName() );
                                if ( $checkbox['irreversible'] ) {
@@ -847,10 +850,6 @@ class UserrightsPage extends SpecialPage {
                                }
                                $checkboxHtml = Xml::checkLabel( $text, "wpGroup-" . $group,
                                        "wpGroup-" . $group, $checkbox['set'], $attr );
-                               $ret .= "\t\t" . ( ( $checkbox['disabled'] && $checkbox['disabled-expiry'] )
-                                       ? Xml::tags( 'div', [ 'class' => 'mw-userrights-disabled' ], $checkboxHtml )
-                                       : Xml::tags( 'div', [], $checkboxHtml )
-                               ) . "\n";
 
                                if ( $this->canProcessExpiries() ) {
                                        $uiUser = $this->getUser();
@@ -920,7 +919,10 @@ class UserrightsPage extends SpecialPage {
                                                $expiryHtml .= $expiryFormOptions->getHTML() . '<br />';
 
                                                // Add custom expiry field
-                                               $attribs = [ 'id' => "mw-input-wpExpiry-$group-other" ];
+                                               $attribs = [
+                                                       'id' => "mw-input-wpExpiry-$group-other",
+                                                       'class' => 'mw-userrights-expiryfield',
+                                               ];
                                                if ( $checkbox['disabled-expiry'] ) {
                                                        $attribs['disabled'] = 'disabled';
                                                }
@@ -939,8 +941,12 @@ class UserrightsPage extends SpecialPage {
                                                'id' => "mw-userrights-nested-wpGroup-$group",
                                                'class' => 'mw-userrights-nested',
                                        ];
-                                       $ret .= "\t\t\t" . Xml::tags( 'div', $divAttribs, $expiryHtml ) . "\n";
+                                       $checkboxHtml .= "\t\t\t" . Xml::tags( 'div', $divAttribs, $expiryHtml ) . "\n";
                                }
+                               $ret .= "\t\t" . ( ( $checkbox['disabled'] && $checkbox['disabled-expiry'] )
+                                       ? Xml::tags( 'div', [ 'class' => 'mw-userrights-disabled' ], $checkboxHtml )
+                                       : Xml::tags( 'div', [], $checkboxHtml )
+                               ) . "\n";
                        }
                        $ret .= "\t</td>\n";
                }
index 1f720af..a84c4b8 100644 (file)
@@ -39,6 +39,7 @@ class LanguageConverter {
         */
        static public $languagesWithVariants = [
                'en',
+               'crh',
                'gan',
                'iu',
                'kk',
diff --git a/languages/classes/LanguageCrh.php b/languages/classes/LanguageCrh.php
new file mode 100644 (file)
index 0000000..f384471
--- /dev/null
@@ -0,0 +1,296 @@
+<?php
+/**
+ * Crimean Tatar (Qırımtatarca) specific code.
+ *
+ * Adapted from https://crh.wikipedia.org/wiki/Qullan%C4%B1c%C4%B1:Don_Alessandro/Translit
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Language
+ */
+
+/**
+ * Crimean Tatar (Qırımtatarca) converter routines
+ *
+ * @ingroup Language
+ */
+class CrhConverter extends LanguageConverter {
+       // Defines working character ranges
+       const WORD_BEGINS = '\r\s\"\'\(\)\-<>\[\]\/.,:;!?';
+       const WORD_ENDS = '\r\s\"\'\(\)\-<>\[\]\/.,:;!?';
+
+       // Cyrillic
+       const C_UC = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'; # Crimean Tatar Cyrillic uppercase
+       const C_LC = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'; # Crimean Tatar Cyrillic lowercase
+       const C_CONS_UC = 'БВГДЖЗЙКЛМНПРСТФХЦЧШЩCÑ'; # Crimean Tatar Cyrillic + CÑ uppercase consonants
+       const C_CONS_LC = 'бвгджзйклмнпрстфхцчшщcñ'; # Crimean Tatar Cyrillic + CÑ lowercase consonants
+       const C_M_CONS = 'бгкмпшcБГКМПШC'; # Crimean Tatar Cyrillic M-type consonants
+
+       # Crimean Tatar Cyrillic + CÑ consonants
+       const C_CONS = 'бвгджзйклмнпрстфхцчшщcñБВГДЖЗЙКЛМНПРСТФХЦЧШЩCÑ';
+
+       // Latin
+       const L_UC = 'AÂBCÇDEFGĞHIİJKLMNÑOÖPQRSŞTUÜVYZ'; # Crimean Tatar Latin uppercase
+       const L_LC = 'aâbcçdefgğhıijklmnñoöpqrsştuüvyz'; # Crimean Tatar Latin lowercase
+       const L_N_CONS_UC = 'ÇNRSTZ'; # Crimean Tatar Latin N-type upper case consonants
+       const L_N_CONS_LC = 'çnrstz'; # Crimean Tatar Latin N-type lower case consonants
+       const L_N_CONS = 'çnrstzÇNRSTZ'; # Crimean Tatar Latin N-type consonants
+       const L_M_CONS = 'bcgkmpşBCGKMPŞ'; # Crimean Tatar Latin M-type consonants
+       const L_CONS_UC = 'BCÇDFGHJKLMNÑPRSŞTVZ'; # Crimean Tatar Latin uppercase consonants
+       const L_CONS_LC = 'bcçdfghjklmnñprsştvz'; # Crimean Tatar Latin lowercase consonants
+       const L_CONS = 'bcçdfghjklmnñprsştvzBCÇDFGHJKLMNÑPRSŞTVZ'; # Crimean Tatar Latin consonants
+       const L_VOW_UC = 'AÂEIİOÖUÜ'; # Crimean Tatar Latin uppercase vowels
+       const L_VOW = 'aâeıioöuüAÂEIİOÖUÜ'; # Crimean Tatar Latin vowels
+       const L_F_UC = 'EİÖÜ'; # Crimean Tatar Latin uppercase front vowels
+       const L_F = 'eiöüEİÖÜ'; # Crimean Tatar Latin front vowels
+
+       public $mCyrillicToLatin = [
+
+               ## these are independent of location in the word, but have
+               ## to go first so other transforms don't bleed them
+               'гъ' => 'ğ', 'Гъ' => 'Ğ', 'ГЪ' => 'Ğ',
+               'къ' => 'q', 'Къ' => 'Q', 'КЪ' => 'Q',
+               'нъ' => 'ñ', 'Нъ' => 'Ñ', 'НЪ' => 'Ñ',
+               'дж' => 'c', 'Дж' => 'C', 'ДЖ' => 'C',
+
+               'А' => 'A', 'а' => 'a', 'Б' => 'B', 'б' => 'b',
+               'В' => 'V', 'в' => 'v', 'Г' => 'G', 'г' => 'g',
+               'Д' => 'D', 'д' => 'd', 'Ж' => 'J', 'ж' => 'j',
+               'З' => 'Z', 'з' => 'z', 'И' => 'İ', 'и' => 'i',
+               'Й' => 'Y', 'й' => 'y', 'К' => 'K', 'к' => 'k',
+               'Л' => 'L', 'л' => 'l', 'М' => 'M', 'м' => 'm',
+               'Н' => 'N', 'н' => 'n', 'П' => 'P', 'п' => 'p',
+               'Р' => 'R', 'р' => 'r', 'С' => 'S', 'с' => 's',
+               'Т' => 'T', 'т' => 't', 'Ф' => 'F', 'ф' => 'f',
+               'Х' => 'H', 'х' => 'h', 'Ч' => 'Ç', 'ч' => 'ç',
+               'Ш' => 'Ş', 'ш' => 'ş', 'Ы' => 'I', 'ы' => 'ı',
+               'Э' => 'E', 'э' => 'e', 'Е' => 'E', 'е' => 'e',
+               'Я' => 'Â', 'я' => 'â', 'У' => 'U', 'у' => 'u',
+               'О' => 'O', 'о' => 'o',
+
+               'Ё' => 'Yo', 'ё' => 'yo', 'Ю' => 'Yu', 'ю' => 'yu',
+               'Ц' => 'Ts', 'ц' => 'ts', 'Щ' => 'Şç', 'щ' => 'şç',
+               'Ь' => '', 'ь' => '', 'Ъ' => '', 'ъ' => '',
+
+       ];
+
+       public $mLatinToCyrillic = [
+               'Â' => 'Я', 'â' => 'я', 'B' => 'Б', 'b' => 'б',
+               'Ç' => 'Ч', 'ç' => 'ч', 'D' => 'Д', 'd' => 'д',
+               'F' => 'Ф', 'f' => 'ф', 'G' => 'Г', 'g' => 'г',
+               'H' => 'Х', 'h' => 'х', 'I' => 'Ы', 'ı' => 'ы',
+               'İ' => 'И', 'i' => 'и', 'J' => 'Ж', 'j' => 'ж',
+               'K' => 'К', 'k' => 'к', 'L' => 'Л', 'l' => 'л',
+               'M' => 'М', 'm' => 'м', 'N' => 'Н', 'n' => 'н',
+               'O' => 'О', 'o' => 'о', 'P' => 'П', 'p' => 'п',
+               'R' => 'Р', 'r' => 'р', 'S' => 'С', 's' => 'с',
+               'Ş' => 'Ш', 'ş' => 'ш', 'T' => 'Т', 't' => 'т',
+               'V' => 'В', 'v' => 'в', 'Z' => 'З', 'z' => 'з',
+
+               'ya' => 'я', 'Ya' => 'Я', 'YA' => 'Я',
+               'ye' => 'е', 'YE' => 'Е', 'Ye' => 'Е',
+
+               // hack, hack, hack
+               'A' => 'А', 'a' => 'а', 'E' => 'Е', 'e' => 'е',
+               'Ö' => 'О', 'ö' => 'о', 'U' => 'У', 'u' => 'у',
+               'Ü' => 'У', 'ü' => 'у', 'Y' => 'Й', 'y' => 'й',
+
+               'C' => 'Дж', 'c' => 'дж', 'Ğ' => 'Гъ', 'ğ' => 'гъ',
+               'Ñ' => 'Нъ', 'ñ' => 'нъ', 'Q' => 'Къ', 'q' => 'къ',
+
+               ];
+
+       public $mExceptions = [];
+       public $mCyrl2LatnPatterns = [];
+       public $mLatn2CyrlPatterns = [];
+       public $mCyrlCleanUpRegexes = [];
+
+       public $mExceptionsLoaded = false;
+
+       function loadDefaultTables() {
+               $this->mTables = [
+                       'crh-latn' => new ReplacementArray( $this->mCyrillicToLatin ),
+                       'crh-cyrl' => new ReplacementArray( $this->mLatinToCyrillic ),
+                       'crh' => new ReplacementArray()
+               ];
+       }
+
+       function postLoadTables() {
+               $this->loadExceptions();
+       }
+
+       function loadExceptions() {
+               if ( $this->mExceptionsLoaded ) {
+                       return;
+               }
+
+               $this->mExceptionsLoaded = true;
+               $crhExceptions = new MediaWiki\Languages\Data\CrhExceptions();
+               list( $this->mExceptions, $this->mCyrl2LatnPatterns, $this->mLatn2CyrlPatterns,
+                       $this->mCyrlCleanUpRegexes ) = $crhExceptions->loadExceptions( self::L_LC . self::C_LC,
+                       self::L_UC . self::C_UC );
+       }
+
+       /**
+        * A function wrapper:
+        *   - if there is no selected variant, leave the link
+        *     names as they were
+        *   - do not try to find variants for usernames
+        *
+        * @param string &$link
+        * @param Title &$nt
+        * @param bool $ignoreOtherCond
+        */
+       function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
+               // check for user namespace
+               if ( is_object( $nt ) ) {
+                       $ns = $nt->getNamespace();
+                       if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
+                               return;
+                       }
+               }
+
+               $oldlink = $link;
+               parent::findVariantLink( $link, $nt, $ignoreOtherCond );
+               if ( $this->getPreferredVariant() == $this->mMainLanguageCode ) {
+                       $link = $oldlink;
+               }
+       }
+
+       /**
+        *  It translates text into variant, specials:
+        *    - ommiting roman numbers
+        *
+        * @param string $text
+        * @param bool $toVariant
+        *
+        * @throws MWException
+        * @return string
+        */
+       function translate( $text, $toVariant ) {
+               $letters = '';
+               switch ( $toVariant ) {
+                       case 'crh-cyrl':
+                               $letters = self::L_UC . self::L_LC . "\'";
+                               break;
+                       case 'crh-latn':
+                               $letters = self::C_UC . self::C_LC . "";
+                               break;
+                       default:
+                               return $text;
+                               break;
+               }
+
+               if ( !$this->mTablesLoaded ) {
+                       $this->loadTables();
+               }
+
+               if ( !isset( $this->mTables[$toVariant] ) ) {
+                       throw new MWException( "Broken variant table: " . implode( ',', array_keys( $this->mTables ) ) );
+               }
+
+               // check for roman numbers like VII, XIX...
+               $roman = '/^M{0,3}(C[DM]|D{0,1}C{0,3})(X[LC]|L{0,1}X{0,3})(I[VX]|V{0,1}I{0,3})$/u';
+
+               # match any sub-string of the relevant letters and convert it
+               $matches = preg_split( '/(\b|^)[^' . $letters . ']+(\b|$)/u',
+                       $text, -1, PREG_SPLIT_OFFSET_CAPTURE );
+               $mstart = 0;
+               $ret = '';
+               foreach ( $matches as $m ) {
+                       # copy over the non-matching bit
+                       $ret .= substr( $text, $mstart, $m[1] - $mstart );
+                       # skip certain classes of strings
+
+                       if ( array_key_exists( $m[0], $this->mExceptions ) ) {
+                               # if it's an exception, just copy down the right answer
+                               $ret .= $this->mExceptions[$m[0]];
+                       } elseif ( ! $m[0] || # empty strings
+                                        preg_match( $roman, $m[0] ) || # roman numerals
+                                        preg_match( '/[^' . $letters . ']/', $m[0] ) # mixed orthography
+                                       ) {
+                               $ret .= $m[0];
+                       } else {
+                               # convert according to the rules
+                               $token = $this->regsConverter( $m[0], $toVariant );
+                               $ret .= parent::translate( $token, $toVariant );
+                       }
+                       $mstart = $m[1] + strlen( $m[0] );
+               }
+
+               # pick up stray quote marks
+               switch ( $toVariant ) {
+                       case 'crh-cyrl':
+                               $ret = strtr( $ret, [ '“' => '«', '”' => '»', ] );
+                               $ret = $this->regsConverter( $ret, 'cyrl-cleanup' );
+                               break;
+                       case 'crh-latn':
+                               $ret = strtr( $ret, [ '«' => '"', '»' => '"', ] );
+                               break;
+               }
+
+               return $ret;
+       }
+
+       private function regsConverter( $text, $toVariant ) {
+               if ( $text == '' ) return $text;
+
+               $pat = [];
+               $rep = [];
+               switch ( $toVariant ) {
+                       case 'crh-latn':
+                               foreach ( $this->mCyrl2LatnPatterns as $pat => $rep ) {
+                                       $text = preg_replace( $pat, $rep, $text );
+                               }
+                               return $text;
+                       case 'crh-cyrl':
+                               foreach ( $this->mLatn2CyrlPatterns as $pat => $rep ) {
+                                       $text = preg_replace( $pat, $rep, $text );
+                               }
+                               return $text;
+                       case 'cyrl-cleanup':
+                               foreach ( $this->mCyrlCleanUpRegexes as $pat => $rep ) {
+                                       $text = preg_replace( $pat, $rep, $text );
+                               }
+                               return $text;
+                       default:
+                               return $text;
+               }
+       }
+
+}
+
+/**
+ * Crimean Tatar (Qırımtatarca)
+ *
+ * @ingroup Language
+ */
+class LanguageCrh extends Language {
+
+       function __construct() {
+               parent::__construct();
+
+               $variants = [ 'crh', 'crh-cyrl', 'crh-latn' ];
+               $variantfallbacks = [
+                       'crh' => 'crh-latn',
+                       'crh-cyrl' => 'crh-latn',
+                       'crh-latn' => 'crh-cyrl',
+               ];
+
+               $this->mConverter = new CrhConverter( $this, 'crh', $variants, $variantfallbacks );
+       }
+}
diff --git a/languages/data/CrhExceptions.php b/languages/data/CrhExceptions.php
new file mode 100644 (file)
index 0000000..dc4b1ef
--- /dev/null
@@ -0,0 +1,830 @@
+<?php
+/**
+ * Exceptions Tables for Crimean Tatar (crh / Qırımtatarca)
+ *
+ * Adapted from https://crh.wikipedia.org/wiki/Qullan%C4%B1c%C4%B1:Don_Alessandro/Translit
+ *
+ * @file
+ */
+
+namespace MediaWiki\Languages\Data;
+
+use \CrhConverter as Crh;
+
+class CrhExceptions {
+
+       function __construct() {
+               $this->loadRegs();
+       }
+
+       public $exceptionMap = [];
+       public $Cyrl2LatnPatterns = [];
+       public $Latn2CyrlPatterns = [];
+
+       private $lc2uc;
+       private $uc2lc;
+
+       private function initLcUc( $lcChars, $ucChars, $reinit = false ) {
+               # bail if we've already done this, unless we are re-initializing
+               if ( !$reinit && $this->lc2uc && $this->uc2lc ) {
+                       return;
+               }
+
+               # split up the lc and uc lists in a unicode-friendly way
+               $myLc = [];
+               preg_match_all( '/./u', $lcChars, $myLc );
+               $myLc = $myLc[0];
+
+               $myUc = [];
+               preg_match_all( '/./u', $ucChars, $myUc );
+               $myUc = $myUc[0];
+
+               # map lc to uc and vice versa
+               $this->lc2uc = array_combine( array_values( $myLc ), array_values( $myUc ) );
+               $this->uc2lc = array_combine( array_values( $myUc ), array_values( $myLc ) );
+       }
+
+       private function myLc( $string ) {
+               return strtr( $string, $this->uc2lc );
+       }
+
+       private function myUc( $string ) {
+               return strtr( $string, $this->lc2uc );
+       }
+
+       private function myUcWord( $string ) {
+               return $this->myUc( mb_substr( $string, 0, 1 ) ) . $this->myLc( mb_substr( $string, 1 ) );
+       }
+
+       private function addMappings( $mapArray, &$A2B, &$B2A, $exactCase = false,
+                       $prePat = '', $postPat = '' ) {
+               foreach ( $mapArray as $WordA => $WordB ) {
+                       $ucA = $this->myUc( $WordA );
+                       $ucWordA = $this->myUcWord( $WordA );
+                       $ucB = $this->myUc( $WordB );
+                       $ucWordB = $this->myUcWord( $WordB );
+
+                       # if there are regexes, only map toward backregs
+                       if ( ! preg_match( '/\$[1-9]/', $WordA ) ) {
+                               $A2B[ $prePat . $WordA . $postPat ] = $WordB;
+                               if ( ! $exactCase ) {
+                                       $A2B[ $prePat . $ucWordA . $postPat ] = $ucWordB;
+                                       $A2B[ $prePat . $ucA . $postPat ] = $ucB;
+                               }
+                       }
+
+                       if ( ! preg_match( '/\$[1-9]/', $WordB ) ) {
+                               $B2A[ $prePat . $WordB . $postPat ] = $WordA;
+                               if ( ! $exactCase ) {
+                                       $B2A[ $prePat . $ucWordB . $postPat ] = $ucWordA;
+                                       $B2A[ $prePat . $ucB . $postPat ] = $ucA;
+                               }
+                       }
+               }
+       }
+
+       function loadExceptions( $lcChars, $ucChars ) {
+               # init lc and uc, as needed
+               $this->initLcUc( $lcChars, $ucChars );
+               # load C2L and L2C whole-word exceptions into the same array, since it's just a look up
+               # no regex prefix/suffix needed
+               $this->addMappings( $this->wordMappings, $this->exceptionMap, $this->exceptionMap );
+               $this->addMappings( $this->exactCaseMappings, $this->exceptionMap, $this->exceptionMap, true );
+
+               # load C2L and L2C bidirectional prefix mappings
+               $this->addMappings( $this->prefixMapping,
+                       $this->Cyrl2LatnPatterns, $this->Latn2CyrlPatterns, false, '/^', '/u' );
+               $this->addMappings( $this->suffixMapping,
+                       $this->Cyrl2LatnPatterns, $this->Latn2CyrlPatterns, false, '/', '$/u' );
+
+               # tack on one-way mappings to the ends of the prefix and suffix patterns
+               $this->Cyrl2LatnPatterns += $this->Cyrl2LatnRegexes;
+               $this->Latn2CyrlPatterns += $this->Latn2CyrlRegexes;
+
+               return [ $this->exceptionMap, $this->Cyrl2LatnPatterns,
+                       $this->Latn2CyrlPatterns, $this->CyrlCleanUpRegexes ];
+       }
+
+       # map Cyrillic to Latin and back, whole word match only
+       # variants: all lowercase, all uppercase, first letter capitalized
+       # items with capture group refs (e.g., $1) are only mapped from the
+       # regex to the reference
+       private $wordMappings = [
+
+               #### originally Cyrillic to Latin
+               'аджыумер' => 'acıümer', 'аджыусеин' => 'acıüsein', 'алейкум' => 'aleyküm',
+               'бейуде' => 'beyüde', 'боливия' => 'boliviya', 'большевик' => 'bolşevik', 'борис' => 'boris',
+               'борнен' => 'bornen', 'бугун' => 'bugün', 'бузкесен' => 'buzkesen', 'буксир' => 'buksir',
+               'бульбуль' => 'bülbül', 'бульвар' => 'bulvar', 'бульдозер' => 'buldozer', 'бульон' => 'bulyon',
+               'бунен' => 'bunen', 'буннен' => 'bunnen', 'бус-бутюн' => 'büs-bütün',
+               'бутерброд' => 'buterbrod', 'буфер' => 'bufer', 'буфет' => 'bufet', 'гонъюл' => 'göñül',
+               'горизонт' => 'gorizont', 'госпиталь' => 'gospital', 'гуливер' => 'guliver', 'гуна' => 'güna',
+               'гунях' => 'günâh', 'гургуль' => 'gürgül', 'гуя' => 'güya', 'демирёл' => 'demiryol',
+               'джуньджу' => 'cüncü', 'ёлнен' => 'yolnen', 'зумбуль' => 'zümbül', 'ильи' => 'ilyi', 'ишунь' =>
+               'işün', 'кодекс' => 'kodeks', 'кодифик' => 'kodifik', 'койлю' => 'köylü', 'коккоз' =>
+               'kökköz', 'коккозь' => 'kökköz', 'коккозю' => 'kökközü', 'кокос' => 'kokos',
+               'коллег' => 'kolleg', 'коллект' => 'kollekt', 'коллекц' => 'kollekts', 'кольцов' => 'koltsov',
+               'комбин' => 'kombin', 'комедия' => 'komediya', 'коменда' => 'komenda', 'комета' => 'kometa',
+               'комис' => 'komis', 'комит' => 'komit', 'комите' => 'komite', 'коммент' => 'komment',
+               'коммерс' => 'kommers', 'коммерц' => 'kommerts', 'компенс' => 'kompens', 'компил' => 'kompil',
+               'компьютер' => 'kompyuter', 'конвейер' => 'konveyer', 'конвен' => 'konven',
+               'конверт' => 'konvert', 'конденс' => 'kondens', 'кондитер' => 'konditer',
+               'кондиц' => 'kondits', 'коник' => 'konik', 'консерв' => 'konserv', 'контейнер' => 'konteyner',
+               'континент' => 'kontinent', 'конфе' => 'konfe', 'конфискац' => 'konfiskats',
+               'концен' => 'kontsen', 'концерт' => 'kontsert', 'конъюктур' => 'konyuktur',
+               'коньки' => 'konki', 'коньяк' => 'konyak', 'копирле' => 'kopirle', 'копия' => 'kopiya',
+               'корбекул' => 'körbekül', 'кореиз' => 'koreiz', 'коренн' => 'korenn', 'корея' => 'koreya',
+               'коридор' => 'koridor', 'корнеев' => 'korneyev', 'корре' => 'korre', 'корьбекул' =>
+               'körbekül', 'косме' => 'kosme', 'космик' => 'kosmik', 'костюм' => 'kostüm', 'котельн' =>
+               'koteln', 'котировка' => 'kotirovka', 'котлет' => 'kotlet', 'кочергин' => 'koçergin',
+               'коше' => 'köşe', 'кудрин' => 'kudrin', 'кузнец' => 'kuznets', 'кулинар' => 'kulinar',
+               'кулич' => 'kuliç', 'кульминац' => 'kulminats', 'культив' => 'kultiv',
+               'культура' => 'kultura', 'куркулет' => 'kürkület', 'курсив' => 'kursiv', 'кушку' => 'küşkü',
+               'куюк' => 'küyük', 'къарагоз' => 'qaragöz', 'къолязма' => 'qolyazma', 'къуртумер' =>
+               'qurtümer', 'къуртусеин' => 'qurtüsein', 'марьино' => 'maryino', 'медьюн' => 'medyun',
+               'месули' => 'mesüli', 'месуль' => 'mesül', 'мефкуре' => 'mefküre', 'могедек' => 'mögedek',
+               'муур' => 'müür', 'муче' => 'müçe', 'муюз' => 'müyüz', 'огнево' => 'ognevo',
+               'одеколон' => 'odekolon', 'одеса' => 'odesa', 'одесса' => 'odessa', 'озерки' => 'ozerki',
+               'озерн' => 'ozern', 'озёрн' => 'ozörn', 'океан' => 'okean', 'оленев' => 'olenev',
+               'олимп' => 'olimp', 'ольчер' => 'ölçer', 'онен' => 'onen', 'оннен' => 'onnen',
+               'опера' => 'opera', 'оптим' => 'optim', 'опци' => 'optsi', 'опция' => 'optsiya',
+               'орден' => 'orden', 'ордер' => 'order', 'ореанда' => 'oreanda', 'орех' => 'oreh',
+               'оригинал' => 'original', 'ориент' => 'oriyent', 'оркестр' => 'orkestr', 'орлин' => 'orlin',
+               'офис' => 'ofis', 'офицер' => 'ofitser', 'офсет' => 'ofset', 'оюннен' => 'oyunnen', 'побед' =>
+               'pobed', 'полево' => 'polevo', 'поли' => 'poli', 'полюшко' => 'polüşko',
+               'помидор' => 'pomidor', 'пониз' => 'poniz', 'порфир' => 'porfir', 'потелов' => 'potelov',
+               'почетн' => 'poçetn', 'почётн' => 'poçötn', 'публик' => 'publik', 'публиц' => 'publits',
+               'пушкин' => 'puşkin', 'сеитумер' => 'seitümer', 'сеитусеин' => 'seitüsein', 'сеитягъя' =>
+               'seityağya', 'сеитягья' => 'seityagya', 'сеитяхья' => 'seityahya', 'сеитяя' => 'seityaya',
+               'сейитумер' => 'seyitümer', 'сейитусеин' => 'seyitüsein', 'сейитягъя' => 'seyityağya',
+               'сейитягья' => 'seyityagya', 'сейитяхья' => 'seyityahya', 'сейитяя' => 'seyityaya',
+               'ультимат' => 'ultimat', 'ультра' => 'ultra', 'ульянов' => 'ulyanov', 'универ' => 'univer',
+               'уника' => 'unika', 'унтер' => 'unter', 'урьян' => 'uryan', 'уткин' => 'utkin', 'учебн' =>
+               'uçebn', 'шовини' => 'şovini', 'шоссе' => 'şosse', 'шубин' => 'şubin', 'шунен' => 'şunen',
+               'шуннен' => 'şunnen', 'щёлкино' => 'şçolkino', 'эмирусеин' => 'emirüsein',
+               'юзбашы' => 'yüzbaşı', 'юзйыл' => 'yüzyıl', 'юртер' => 'yurter', 'ющенко' => 'yuşçenko',
+
+               'кою' => 'köyü', 'кок' => 'kök', 'ком-кок' => 'köm-kök', 'коп' => 'köp', 'ог' => 'ög',
+               'юрип' => 'yürip', 'юз' => 'yüz', 'юк' => 'yük', 'буюп' => 'büyüp', 'буюк' => 'büyük',
+               'джонк' => 'cönk', 'джонкю' => 'cönkü', 'устке' => 'üstke', 'устте' => 'üstte',
+               'усттен' => 'üstten',
+
+               # шофёр needs to come after шофер to override it in the Latin-to-Cyrillic direction
+               'шофер' => 'şoför',
+               'шофёр' => 'şoför',
+
+               #### originally Latin to Cyrillic (deduped from above)
+
+               # слова на -аль
+               # words in -аль
+               'актуаль' => 'aktual', 'диагональ' => 'diagonal', 'документаль' => 'dokumental',
+               'эмсаль' => 'emsal', 'фааль' => 'faal', 'феодаль' => 'feodal', 'фестиваль' => 'festival',
+               'горизонталь' => 'gorizontal', 'хроникаль' => 'hronikal', 'идеаль' => 'ideal',
+               'инструменталь' => 'instrumental', 'икъмаль' => 'iqmal', 'икъбаль' => 'iqbal',
+               'истикъбаль' => 'istiqbal', 'истикъляль' => 'istiqlâl', 'италия' => 'italiya',
+               'италья' => 'italya', 'ишгъаль' => 'işğal', 'кафедраль' => 'kafedral', 'казуаль' => 'kazual',
+               'коллегиаль' => 'kollegial', 'колоссаль' => 'kolossal', 'коммуналь' => 'kommunal',
+               'кординаль' => 'kordinal', 'криминаль' => 'kriminal', 'легаль' => 'legal', 'леталь' => 'letal',
+               'либераль' => 'liberal', 'локаль' => 'lokal', 'магистраль' => 'magistral',
+               'материаль' => 'material', 'машиналь' => 'maşinal', 'меаль' => 'meal',
+               'медальон' => 'medalyon', 'медаль' => 'medal', 'меридиональ' => 'meridional',
+               'мешъаль' => 'meşal', 'минераль' => 'mineral', 'минималь' => 'minimal', 'мисаль' => 'misal',
+               'модаль' => 'modal', 'музыкаль' => 'muzıkal', 'номиналь' => 'nominal', 'нормаль' => 'normal',
+               'оптималь' => 'optimal', 'орбиталь' => 'orbital', 'оригиналь' => 'original',
+               'педаль' => 'pedal', 'пропорциональ' => 'proportsional', 'профессиональ' => 'professional',
+               'радикаль' => 'radikal', 'рациональ' => 'ratsional', 'реаль' => 'real',
+               'региональ' => 'regional', 'суаль' => 'sual', 'шималь' => 'şimal',
+               'территориаль' => 'territorial', 'тимсаль' => 'timsal', 'тоталь' => 'total',
+               'уникаль' => 'unikal', 'универсаль' => 'universal', 'вертикаль' => 'vertikal',
+               'виртуаль' => 'virtual', 'визуаль' => 'vizual', 'вуаль' => 'vual', 'зональ' => 'zonal',
+               'зуаль' => 'zual',
+
+               # слова с мягким знаком перед а, о, у, э
+               # Words with a soft sign before а, о, у, э
+               'бильакис' => 'bilakis', 'маальэсеф' => 'maalesef',
+               'мельун' => 'melun', 'озьара' => 'özara', 'вельасыл' => 'velasıl',
+               'ельаякъ' => 'yelayaq',
+               # these are ordered so C2L is correct (the later Latin one)
+               'февкъульаде' => 'fevqülade','февкъульаде' => 'fevqulade',
+
+               # другие слова с мягким знаком
+               # Other words with a soft sign
+               'альбатрос' => 'albatros', 'альбинос' => 'albinos', 'альбом' => 'albom',
+               'альбумин' => 'albumin', 'алфавит' => 'alfavit', 'альфа' => 'alfa', 'альманах' => 'almanah',
+               'альпинист' => 'alpinist', 'альтерн' => 'altern', 'альтру' => 'altru', 'альвеола' => 'alveola',
+               'ансамбль' => 'ansambl', 'аньане' => 'anane', 'асфальт' => 'asfalt', 'бальнео' => 'balneo',
+               'баарь' => 'baar', 'базальт' => 'bazalt', 'бинокль' => 'binokl', 'джурьат' => 'curat',
+               'джурьат' => 'cürat', 'девальв' => 'devalv', 'факульт' => 'fakult', 'фальсиф' => 'falsif',
+               'фольклор' => 'folklor', 'гальван' => 'galvan', 'геральд' => 'gerald', 'женьшень' => 'jenşen',
+               'инвентарь' => 'inventar', 'кальк' => 'kalk', 'кальмар' => 'kalmar', 'консульт' => 'konsult',
+               'контроль' => 'kontrol', 'кульмин' => 'kulmin', 'культур' => 'kultur', 'лагерь' => 'lager',
+               'макъбуль' => 'maqbul', 'макъуль' => 'maqul', 'мальт' => 'malt', 'мальземе' => 'malzeme',
+               'меджуль' => 'mecul', 'мешгуль' => 'meşgül', 'мешгъуль' => 'meşğul', 'мульти' => 'multi',
+               'мусульман' => 'musulman', 'нефть' => 'neft', 'пальто' => 'palto', 'пароль' => 'parol',
+               'патруль' => 'patrul', 'пенальти' => 'penalti', 'къальби' => 'qalbi', 'къальпке' => 'qalpke',
+               'къальплер' => 'qalpler', 'къальпни' => 'qalpni', 'къальпте' => 'qalpte', 'къаарь' => 'qaar',
+               'ресуль' => 'resul', 'рыцарь' => 'rıtsar', 'рояль' => 'royal', 'саарь' => 'saar',
+               'спираль' => 'spiral', 'сульх' => 'sulh', 'сумбуль' => 'sumbul', 'суньий' => 'suniy',
+               'темаюль' => 'temayul', 'шампунь' => 'şampun', 'вальс' => 'vals', 'вальц' => 'valts',
+               'ведомость' => 'vedomost', 'зулькъарнейн' => 'zulqarneyn', 'январь' => 'yanvar',
+               'февраль' => 'fevral', 'июнь' => 'iyün', 'сентябрь' => 'sentâbr', 'октябрь' => 'oktâbr',
+               'ноябрь' => 'noyabr', 'декабрь' => 'dekabr',
+
+               # слова с твёрдым знаком
+               # Words with a solid sign
+               'бидъат' => 'bidat', 'бузъюрек' => 'buzyürek', 'атешъюрек' => 'ateşyürek',
+               'алъянакъ' => 'alyanaq', 'демиръёл' => 'demiryol', 'деръал' => 'deral', 'инъекц' => 'inyekts',
+               'мефъум' => 'mefum', 'мешъум' => 'meşum', 'объект' => 'obyekt', 'разъезд' => 'razyezd',
+               'субъект' => 'subyekt', 'хавъяр' => 'havyar', 'ямъям' => 'yamyam',
+
+               # слова с буквой щ
+               # words with щ
+               'ящик' => 'yaşçik', 'мещан' => 'meşçan',
+
+               # слова с буквой ц
+               # words with ц
+               'акциз' => 'aktsiz', 'ацет' => 'atset', 'блиц' => 'blits', 'бруцеллёз' => 'brutsellöz',
+               'доцент' => 'dotsent', 'фармацевт' => 'farmatsevt', 'глицер' => 'glitser',
+               'люцерна' => 'lütserna', 'лицей' => 'litsey', 'меццо' => 'metstso', 'наци' => 'natsi',
+               'проце' => 'protse', 'рецеп' => 'retsep', 'реценз' => 'retsenz', 'теплица' => 'teplitsa',
+               'вице' => 'vitse', 'цепс' => 'tseps', 'швейцар' => 'şveytsar',
+
+               # слова без буквы тс
+               # words with тс
+               'агъартс' => 'ağarts', 'агъыртс' => 'ağırts', 'бильдиртс' => 'bildirts', 'битсин' => 'bitsin',
+               'буюльтс' => 'büyülts', 'буютс' => 'büyüts', 'гебертс' => 'geberts', 'делиртс' => 'delirts',
+               'эгрильтс' => 'egrilts', 'эксильтс' => 'eksilts', 'эшитс' => 'eşits', 'иритс' => 'irits',
+               'иситс' => 'isits', 'ичиртс' => 'içirts', 'кертсин' => 'kertsin', 'кенишлетс' => 'kenişlets',
+               'кийсетс' => 'kiysets', 'копюртс' => 'köpürts', 'косьтертс' => 'kösterts',
+               'кучертс' => 'küçerts', 'кучюльтс' => 'küçülts', 'пертсин' => 'pertsin', 'къайтс' => 'qayts',
+               'къутсуз' => 'qutsuz', 'орьтс' => 'örts', 'отьс' => 'öts', 'тартс' => 'tarts',
+               'тутсун' => 'tutsun', 'тюнъюльтс' => 'tüñülts', 'тюртс' => 'türts', 'янъартс' => 'yañarts',
+               'ебертс' => 'yeberts', 'етсин' => 'yetsin', 'ешертс' => 'yeşerts', 'йиритс' => 'yirits',
+
+               # разные исключения
+               # different exceptions
+               'бейуде' => 'beyude', 'бугунь' => 'bugün', 'бюджет' => 'bücet', 'бюллет' => 'büllet',
+               'бюро' => 'büro', 'бюст' => 'büst', 'джонк' => 'cönk', 'диалог' => 'dialog',
+               'гонъюль' => 'göñül', 'ханымэфенди' => 'hanımefendi', 'каньон' => 'kanyon', 'кирил' => 'kiril',
+               'кирил' => 'kirill', 'кёрджа' => 'körca', 'кой' => 'köy', 'кулеръюзь' => 'küleryüz',
+               'маалле' => 'маальle', 'майор' => 'mayor', 'маниал' => 'manиаль', 'мефкуре' => 'mefküre',
+               'месуль' => 'mesul', 'месуль' => 'mesül', 'муурь' => 'müür',
+               'нормала' => 'нормальa', 'нумюне' => 'nümüne', 'проект' => 'proekt', 'район' => 'rayon',
+               'сойады' => 'soyadı', 'спортсмен' => 'sportsmen', 'услюп' => 'üslüp', 'услюб' => 'üslüb',
+               'вакъиал' => 'vaqиаль', 'юзйыллыкъ' => 'yüzyıllıq',
+
+               # имена собственные
+               # proper names
+               'адольф' => 'adolf', 'альберт' => 'albert', 'бешуй' => 'beşüy', 'эмирусеин' => 'emirüsein',
+               'флотск' => 'flotsk', 'гайана' => 'gayana', 'грэсовский' => 'gresovskiy', 'гриц' => 'grits',
+               'гурджи' => 'gürci', 'игорь' => 'igor', 'ильич' => 'ilyiç', 'ильин' => 'ilyin',
+               'исмаил' => 'ismail', 'киттс' => 'kitts', 'комсомольск' => 'komsomolsk',
+               'корьбекулю' => 'körbekülü', 'корьбекуль' => 'körbekül', 'куницын' => 'kunitsın',
+               'львив' => 'lviv', 'львов' => 'lvov', 'марьино' => 'maryino', 'махульдюр' => 'mahuldür',
+               'павел' => 'pavel', 'пантикапейон' => 'pantikapeyon', 'къарагозь' => 'qaragöz',
+               'къуртсейит' => 'qurtseyit', 'къуртсеит' => 'qurtseit', 'къуртумер' => 'qurtümer',
+               'сейитумер' => 'seyitümer', 'сеитумер' => 'seitümer', 'смаил' => 'smail',
+               'советск' => 'sovetsk', 'шемьи-заде' => 'şemi-zade', 'щёлкино' => 'şçolkino',
+               'тсвана' => 'tsvana', 'учьэвли' => 'üçevli', 'йохан' => 'yohan', 'йорк' => 'york',
+               'ющенко' => 'yuşçenko', 'льная' => 'lnaya', 'льное' => 'lnoye', 'льный' => 'lnıy',
+               'льская' => 'lskaya', 'льский' => 'lskiy', 'льское' => 'lskoye', 'ополь' => 'opol',
+
+               # originally Latin to Cyrillic, deduped from above
+               'ань' => 'an', 'аньге' => 'ange', 'аньде' => 'ande', 'аньки' => 'anki', 'кёр' => 'kör',
+               'мэр' => 'mer', 'этсин' => 'etsin',
+
+               # exceptions added after speaker review
+               # see https://www.mediawiki.org/wiki/User:TJones_(WMF)/T23582
+               'аджизленювинъиз' => 'acizlenüviñiz', 'акъшам' => 'aqşam', 'алчакъгонъюлли' => 'alçaqgöñülli',
+               'аньанелер' => 'ananeler', 'аньанелеримиз' => 'ananelerimiz',
+               'аньанелеримизден' => 'ananelerimizden', 'аньанелеримизни' => 'ananelerimizni',
+               'аньанели' => 'ananeli', 'асфальтке' => 'asfaltke', 'баарьде' => 'baarde', 'бахтсыз' => 'bahtsız',
+               'берилюви' => 'berilüvi', 'берювден' => 'berüvden', 'берювни' => 'berüvni',
+               'большевиклер' => 'bolşevikler', 'большевиклерге' => 'bolşeviklerge', 'болюк' => 'bölük',
+               'болюнген' => 'bölüngen', 'болюнгенини' => 'bölüngenini', 'болюшип' => 'bölüşip',
+               'бугуннинъ' => 'bugünniñ', 'бугуньден' => 'bugünden', 'бугуньки' => 'bugünki',
+               'букюльген' => 'bükülgen', 'букюльди' => 'büküldi', 'буллюр' => 'büllür',
+               'бурюмчик' => 'bürümçik', 'бурюнген' => 'bürüngen', 'бутюн' => 'bütün', 'бутюнлей' => 'bütünley',
+               'буюген' => 'büyügen', 'буюй' => 'büyüy', 'волость' => 'volost', 'волостьларгъа' => 'volostlarğa',
+               'гонъюлини' => 'göñülini', 'гонъюлли' => 'göñülli', 'гонъюллилер' => 'göñülliler',
+               'госпиталинде' => 'gospitalinde', 'госпитальге' => 'gospitalge', 'госпитальде' => 'gospitalde',
+               'гренадёр' => 'grenadör', 'гугюм' => 'gügüm', 'гугюмлер' => 'gügümler',
+               'гугюмлери' => 'gügümleri', 'гугюмлерини' => 'gügümlerini', 'гурьсюльди' => 'gürsüldi',
+               'гурюльдештилер' => 'gürüldeştiler', 'гурюльти' => 'gürülti', 'гурюльтили' => 'gürültili',
+               'гурюльтисидир' => 'gürültisidir', 'дарульмуаллиминде' => 'darülmualliminde',
+               'дарульмуаллимининде' => 'darülmuallimininde', 'дарульмуаллиминнинъ' => 'darülmualliminniñ',
+               'дёгюльген' => 'dögülgen', 'декабрьде' => 'dekabrde', 'дёндюрилип' => 'döndürilip',
+               'дёнермиз' => 'dönermiz', 'дёнмектелер' => 'dönmekteler', 'денъишюв' => 'deñişüv',
+               'дёрдю' => 'dördü', 'дёрдюмиз' => 'dördümiz', 'дёрдюнджи' => 'dördünci', 'дёрт' => 'dört',
+               'дертлешювге' => 'dertleşüvge', 'джесюр' => 'cesür', 'джесюране' => 'cesürane',
+               'джесюрликлерини' => 'cesürliklerini', 'джонегенлерини' => 'cönegenlerini',
+               'джонедим' => 'cönedim', 'джонейлер' => 'cöneyler', 'джурьатсызлыгъына' => 'cüratsızlığına',
+               'дюгюнлер' => 'dügünler', 'дюгюнлерле' => 'dügünlerle', 'дюдюк' => 'düdük', 'дюльбер' => 'dülber',
+               'дюльбери' => 'dülberi', 'дюльберлер' => 'dülberler', 'дюльберлернинъ' => 'dülberlerniñ',
+               'дюльгер' => 'dülger', 'дюльгерге' => 'dülgerge', 'дюльгерлернинъки' => 'dülgerlerniñki',
+               'дюльгерни' => 'dülgerni', 'дюльгернинъ' => 'dülgerniñ', 'дюмбюрдетти' => 'dümbürdetti',
+               'дюмен' => 'dümen', 'дюмени' => 'dümeni', 'дюнья' => 'dünya', 'дюньявий' => 'dünyaviy',
+               'дюньяда' => 'dünyada', 'дюньяларгъа' => 'dünyalarğa', 'дюньяларда' => 'dünyalarda',
+               'дюньяны' => 'dünyanı', 'дюньянынъ' => 'dünyanıñ', 'дюньясы' => 'dünyası',
+               'ельаякълылар' => 'yelayaqlılar', 'елькъуваны' => 'yelquvanı', 'ильич' => 'i̇liç',
+               'ичюн' => 'içün', 'ичюнми' => 'içünmi', 'келюви' => 'kelüvi', 'келювини' => 'kelüvini',
+               'келювинъизде' => 'kelüviñizde', 'келювни' => 'kelüvni', 'кемирювлер' => 'kemirüvler',
+               'кесювде' => 'kesüvde', 'кетюв' => 'ketüv', 'кетювге' => 'ketüvge', 'кетюви' => 'ketüvi',
+               'кетювимни' => 'ketüvimni', 'кетювлер' => 'ketüvler', 'кетювлери' => 'ketüvleri',
+               'кетювлеринънинъ' => 'ketüvleriñniñ', 'кетювнинъ' => 'ketüvniñ', 'кирюв' => 'kirüv',
+               'князь' => 'knâz', 'козькъапакъларыны' => 'közqapaqlarını', 'козьлю' => 'közlü', 'козю' => 'közü',
+               'козюме' => 'közüme', 'козюнде' => 'közünde', 'козюне' => 'közüne', 'козюнен' => 'közünen',
+               'козюнинъ' => 'közüniñ', 'козюнъни' => 'közüñni', 'койлюде' => 'köylüde',
+               'койлюлер' => 'köylüler', 'койлюлерде' => 'köylülerde', 'койлюлерни' => 'köylülerni',
+               'койлюлернинъ' => 'köylülerniñ', 'койлюнинъ' => 'köylüniñ', 'коккозьге' => 'kökközge',
+               'коккозьде' => 'kökközde', 'коккозьдеки' => 'kökközdeki', 'коккозьден' => 'kökközden',
+               'кокюс' => 'köküs', 'кокюси' => 'köküsi', 'кокюсим' => 'köküsim', 'кокюсиме' => 'köküsime',
+               'кокюсинъе' => 'köküsiñe', 'комиссарлар' => 'komissarlar', 'комиссарлары' => 'komissarları',
+               'комитетининъ' => 'komitetiniñ', 'концлагерь' => 'kontslager', 'копьмеди' => 'köpmedi',
+               'копьти' => 'köpti', 'копюр' => 'köpür', 'копюрге' => 'köpürge', 'копюрден' => 'köpürden',
+               'копюри' => 'köpüri', 'копюрнинъ' => 'köpürniñ', 'коридорда' => 'koridorda',
+               'корьсюн' => 'körsün', 'корюв' => 'körüv', 'корюльген' => 'körülgen', 'корюнди' => 'köründi',
+               'корюндинъ' => 'köründiñ', 'корюне' => 'körüne', 'корюнип' => 'körünip',
+               'корюнмеген' => 'körünmegen', 'корюнмеди' => 'körünmedi', 'корюнмедилер' => 'körünmediler',
+               'корюнмей' => 'körünmey', 'корюнмейсинъиз' => 'körünmeysiñiz', 'корюнмекте' => 'körünmekte',
+               'корюнмектелер' => 'körünmekteler', 'корюнъиз' => 'körüñiz', 'корюше' => 'körüşe',
+               'корюшеджекмиз' => 'körüşecekmiz', 'корюшим' => 'körüşim', 'корюшип' => 'körüşip',
+               'корюширмиз' => 'körüşirmiz', 'корюшкен' => 'körüşken', 'корюшкенде' => 'körüşkende',
+               'корюшмеге' => 'körüşmege', 'корюшмегенимиз' => 'körüşmegenimiz', 'корюштик' => 'körüştik',
+               'корюштим' => 'körüştim', 'корюшюв' => 'körüşüv', 'корюшювде' => 'körüşüvde',
+               'корюшювден' => 'körüşüvden', 'корюшюви' => 'körüşüvi', 'корюшювимден' => 'körüşüvimden',
+               'корюшювимизге' => 'körüşüvimizge', 'корюшювимизден' => 'körüşüvimizden',
+               'костюми' => 'kostümi', 'кузю' => 'küzü', 'кулькюден' => 'külküden', 'кулькюнинъ' => 'külküniñ',
+               'кулькюсининъ' => 'külküsiniñ', 'кулю' => 'külü', 'кулюмсиреген' => 'külümsiregen',
+               'кулюмсиреди' => 'külümsiredi', 'кулюмсиредим' => 'külümsiredim', 'кулюмсирей' => 'külümsirey',
+               'кулюмсирейим' => 'külümsireyim', 'кулюмсиреп' => 'külümsirep', 'кулюни' => 'külüni',
+               'кулюнчли' => 'külünçli', 'кулюшинде' => 'külüşinde', 'кулюштилер' => 'külüştiler',
+               'кумюш' => 'kümüş', 'куньдюз' => 'kündüz', 'куньдюзлери' => 'kündüzleri', 'куньлюк' => 'künlük',
+               'куню' => 'künü', 'кунюмде' => 'künümde', 'кунюнде' => 'kününde', 'кунюндеми' => 'künündemi',
+               'кунюнъ' => 'künüñ', 'курькчю' => 'kürkçü', 'курьсю' => 'kürsü', 'курьсюге' => 'kürsüge',
+               'курьсюлер' => 'kürsüler', 'курючтен' => 'kürüçten', 'кутюклерни' => 'kütüklerni',
+               'кутюкли' => 'kütükli', 'кучьлю' => 'küçlü', 'кучьлюклер' => 'küçlükler',
+               'кучьсюнмезсинъ' => 'küçsünmezsiñ', 'кучюджик' => 'küçücik', 'кучюк' => 'küçük',
+               'кучюм' => 'küçüm', 'кучюмле' => 'küçümle', 'кучюнден' => 'küçünden', 'кучюни' => 'küçüni',
+               'къаарьлене' => 'qaarlene', 'къаарьли' => 'qaarli', 'къальбим' => 'qalbim',
+               'къальбимни' => 'qalbimni', 'къальбинде' => 'qalbinde', 'къальпли' => 'qalpli',
+               'къальптен' => 'qalpten', 'къалюбелядан' => 'qalübelâdan', 'къулюбенъде' => 'qulübeñde',
+               'лёман' => 'löman', 'львованынъ' => 'lvovanıñ', 'лютфи' => 'lütfi', 'лютфиге' => 'lütfige',
+               'лютфини' => 'lütfini', 'мазюн' => 'mazün', 'малюм' => 'malüm', 'малюмат' => 'malümat',
+               'махлюкъаттан' => 'mahlüqattan', 'махлюкътан' => 'mahlüqtan', 'махульдюрге' => 'mahuldürge',
+               'махульдюрде' => 'mahuldürde', 'махульдюрдеки' => 'mahuldürdeki',
+               'махульдюрден' => 'mahuldürden', 'махульдюрли' => 'mahuldürli',
+               'махульдюрлилер' => 'mahuldürliler', 'махульдюрлилермиз' => 'mahuldürlilermiz',
+               'махульдюрми' => 'mahuldürmi', 'махульдюрни' => 'mahuldürni', 'мевджут' => 'mevcut',
+               'мезкюр' => 'mezkür', 'мектюп' => 'mektüp', 'мектюпни' => 'mektüpni', 'мектюпте' => 'mektüpte',
+               'мелитопольге' => 'melitopolge', 'мемнюн' => 'memnün', 'мемнюниетле' => 'memnüniyetle',
+               'мемнюним' => 'memnünim', 'мемнюнмиз' => 'memnünmiz', 'менсюп' => 'mensüp',
+               'мешгъульмиз' => 'meşğulmiz', 'мулькюни' => 'mülküni', 'мумкюн' => 'mümkün',
+               'мумкюнми' => 'mümkünmi', 'мусульманлар' => 'musulmanlar', 'мусульманлармы' => 'musulmanlarmı',
+               'мухкемлендирюв' => 'mühkemlendirüv', 'мушкюль' => 'müşkül', 'ничюн' => 'niçün',
+               'ничюндир' => 'niçündir', 'нумюнеси' => 'nümünesi', 'огю' => 'ögü', 'огюз' => 'ögüz',
+               'огюмде' => 'ögümde', 'огюмдеки' => 'ögümdeki', 'огюме' => 'ögüme', 'огюмизге' => 'ögümizge',
+               'огюмизде' => 'ögümizde', 'огюмиздеки' => 'ögümizdeki', 'огюмни' => 'ögümni',
+               'огюнде' => 'ögünde', 'огюндеки' => 'ögündeki', 'огюндекиси' => 'ögündekisi',
+               'огюнден' => 'ögünden', 'огюне' => 'ögüne', 'огюнъизде' => 'ögüñizde', 'огютини' => 'ögütini',
+               'огютлерини' => 'ögütlerini', 'озю' => 'özü', 'озюм' => 'özüm', 'озюмден' => 'özümden',
+               'озюме' => 'özüme', 'озюмизни' => 'özümizni', 'озюмизнинъ' => 'özümizniñ',
+               'озюмизнинъки' => 'özümizniñki', 'озюмнен' => 'özümnen', 'озюмни' => 'özümni',
+               'озюмнинъ' => 'özümniñ', 'озюнде' => 'özünde', 'озюнден' => 'özünden', 'озюне' => 'özüne',
+               'озюнен' => 'özünen', 'озюни' => 'özüni', 'озюнинъ' => 'özüniñ', 'озюнинъкими' => 'özüniñkimi',
+               'озюнъ' => 'özüñ', 'озюнъе' => 'özüñe', 'озюнъиз' => 'özüñiz', 'озюнъиздеки' => 'özüñizdeki',
+               'озюнъни' => 'özüñni', 'оксюз' => 'öksüz', 'окюндим' => 'ökündim', 'ольдюрип' => 'öldürip',
+               'ольдюрмек' => 'öldürmek', 'ольдюрювде' => 'öldürüvde', 'ольчюде' => 'ölçüde', 'олюм' => 'ölüm',
+               'олюмден' => 'ölümden', 'олюмлер' => 'ölümler', 'омюр' => 'ömür', 'омюрге' => 'ömürge',
+               'омюри' => 'ömüri', 'опькеленюв' => 'öpkelenüv', 'орьтилюви' => 'örtilüvi', 'орьтюли' => 'örtüli',
+               'орюли' => 'örüli', 'орюлип' => 'örülip', 'осюв' => 'ösüv', 'осюмлик' => 'ösümlik',
+               'отькерювни' => 'ötkerüvni', 'отькюр' => 'ötkür', 'офицери' => 'ofitseri',
+               'офицерим' => 'ofitserim', 'офицерлер' => 'ofitserler', 'пальтосыны' => 'paltosını',
+               'пальтосынынъ' => 'paltosınıñ', 'пекинюв' => 'pekinüv', 'пекитювнинъ' => 'pekitüvniñ',
+               'пиширюв' => 'pişirüv', 'повидло' => 'povidlo', 'полис' => 'polis', 'полициясы' => 'politsiyası',
+               'помещик' => 'pomeşçik', 'потюк' => 'potük', 'потюклеринен' => 'potüklerinen',
+               'пулемёт' => 'pülemöt', 'пулемётларны' => 'pülemötlarnı', 'режиссёр' => 'rejissör',
+               'ролюнде' => 'rolünde', 'севастопольнинъ' => 'sevastopolniñ', 'сёгди' => 'sögdi', 'сёз' => 'söz',
+               'сёзлер' => 'sözler', 'сёзлери' => 'sözleri', 'сёзлерим' => 'sözlerim',
+               'сёзлеримден' => 'sözlerimden', 'сёзлериме' => 'sözlerime', 'сёзлеримни' => 'sözlerimni',
+               'сёзлеримнинъ' => 'sözlerimniñ', 'сёзлеринде' => 'sözlerinde', 'сёзлерине' => 'sözlerine',
+               'сёзлерини' => 'sözlerini', 'сёзлерининъ' => 'sözleriniñ', 'сёзлеринъиз' => 'sözleriñiz',
+               'сёзлеринъизни' => 'sözleriñizni', 'сёзлернен' => 'sözlernen', 'сёзлерни' => 'sözlerni',
+               'сёзлернинъ' => 'sözlerniñ', 'сёзнен' => 'söznen', 'сёзни' => 'sözni', 'сёзчиклер' => 'sözçikler',
+               'сёзчиклерден' => 'sözçiklerden', 'сёзю' => 'sözü', 'сёзюмен' => 'sözümen',
+               'сёзюмнинъ' => 'sözümniñ', 'сёзюне' => 'sözüne', 'сёзюни' => 'sözüni', 'сёзюнинъ' => 'sözüniñ',
+               'сёйле' => 'söyle', 'сёйлегенде' => 'söylegende', 'сёйлегенлеринден' => 'söylegenlerinden',
+               'сёйледи' => 'söyledi', 'сёйлей' => 'söyley', 'сёйленди' => 'söylendi',
+               'сёйленмеге' => 'söylenmege', 'сёйленмекте' => 'söylenmekte', 'сёйленъиз' => 'söyleñiz',
+               'сёнген' => 'söngen', 'сёнди' => 'söndi', 'сёндюрди' => 'söndürdi',
+               'сёндюрильген' => 'söndürilgen', 'сёндюрип' => 'söndürip', 'сентябрьнинъ' => 'sentâbrniñ',
+               'сергюзешт' => 'sergüzeşt', 'сергюзештлерни' => 'sergüzeştlerni',
+               'ставропольге' => 'stavropolge', 'сулькевич' => 'sulkeviç', 'сурьат' => 'surat',
+               'суфлёр' => 'suflör', 'сюеги' => 'süyegi', 'сюеклерге' => 'süyeklerge',
+               'сюйрекледи' => 'süyrekledi', 'сюйреле' => 'süyrele', 'сюйрен' => 'süyren',
+               'сюйренге' => 'süyrenge', 'сюйренде' => 'süyrende', 'сюйреп' => 'süyrep', 'сюйрю' => 'süyrü',
+               'сюкюнет' => 'sükünet', 'сюкюнети' => 'süküneti', 'сюкюнетте' => 'sükünette', 'сюкют' => 'süküt',
+               'сюляле' => 'sülâle', 'сюрген' => 'sürgen', 'сюрди' => 'sürdi', 'сюрмеди' => 'sürmedi',
+               'сюрюльмеген' => 'sürülmegen', 'сют' => 'süt', 'тебессюм' => 'tebessüm', 'тёкип' => 'tökip',
+               'тёкти' => 'tökti', 'тёкюльген' => 'tökülgen', 'тёкюльди' => 'töküldi',
+               'тёкюндиси' => 'tökündisi', 'тёле' => 'töle', 'тёледим' => 'töledim', 'телюке' => 'telüke',
+               'телюкели' => 'telükeli', 'тенеффюс' => 'teneffüs', 'тенеффюслер' => 'teneffüsler',
+               'тёпеге' => 'töpege', 'тёпелери' => 'töpeleri', 'тёпелерине' => 'töpelerine',
+               'тёпели' => 'töpeli', 'тёпеси' => 'töpesi', 'тёпесинден' => 'töpesinden',
+               'тёпесини' => 'töpesini', 'тёрге' => 'törge', 'тёрде' => 'törde', 'тёрдеки' => 'tördeki',
+               'тёрюне' => 'törüne', 'тешеббюсим' => 'teşebbüsim', 'тёшегинден' => 'töşeginden',
+               'тёшегине' => 'töşegine', 'тёшек' => 'töşek', 'тешеккюр' => 'teşekkür',
+               'тешеккюрлер' => 'teşekkürler', 'тёшекни' => 'töşekni', 'тёшектен' => 'töşekten',
+               'тёшели' => 'töşeli', 'тёшемек' => 'töşemek', 'тёшеп' => 'töşep', 'теэссюф' => 'teessüf',
+               'тюбю' => 'tübü', 'тюбюнде' => 'tübünde', 'тюбюндеки' => 'tübündeki', 'тюз' => 'tüz',
+               'тюзельгенге' => 'tüzelgenge', 'тюзельтмек' => 'tüzeltmek', 'тюземликлер' => 'tüzemlikler',
+               'тюзетип' => 'tüzetip', 'тюзетирим' => 'tüzetirim', 'тюзеткен' => 'tüzetken',
+               'тюзетмеге' => 'tüzetmege', 'тюзетмесенъ' => 'tüzetmeseñ', 'тюзетти' => 'tüzetti',
+               'тюзетюв' => 'tüzetüv', 'тюкенмез' => 'tükenmez', 'тюкюриктен' => 'tükürikten',
+               'тюкян' => 'tükân', 'тюкяны' => 'tükânı', 'тюкянында' => 'tükânında', 'тюм' => 'tüm',
+               'тюневин' => 'tünevin', 'тюневинки' => 'tünevinki', 'тюпсюз' => 'tüpsüz', 'тюрк' => 'türk',
+               'тюрклернинъ' => 'türklerniñ', 'тюркнинъ' => 'türkniñ', 'тюркче' => 'türkçe', 'тюркю' => 'türkü',
+               'тюркюлерини' => 'türkülerini', 'тюркюнинъ' => 'türküniñ', 'тюрлю' => 'türlü',
+               'тюртип' => 'türtip', 'тюрттинъиз' => 'türttiñiz', 'тютемекте' => 'tütemekte', 'тютюн' => 'tütün',
+               'тютюнджи' => 'tütünci', 'тюфеги' => 'tüfegi', 'тюфегини' => 'tüfegini', 'тюфек' => 'tüfek',
+               'тюфеклеринен' => 'tüfeklerinen', 'тюфеклернен' => 'tüfeklernen', 'тюфеклерни' => 'tüfeklerni',
+               'тюфекнен' => 'tüfeknen', 'тюфексиз' => 'tüfeksiz', 'тюш' => 'tüş', 'тюше' => 'tüşe',
+               'тюшеджек' => 'tüşecek', 'тюшеджексинъми' => 'tüşeceksiñmi', 'тюшем' => 'tüşem',
+               'тюшип' => 'tüşip', 'тюшкен' => 'tüşken', 'тюшкенде' => 'tüşkende', 'тюшкенлер' => 'tüşkenler',
+               'тюшмеге' => 'tüşmege', 'тюшмейим' => 'tüşmeyim', 'тюшмейлер' => 'tüşmeyler',
+               'тюшмек' => 'tüşmek', 'тюшмекте' => 'tüşmekte', 'тюшмеси' => 'tüşmesi', 'тюшсе' => 'tüşse',
+               'тюшти' => 'tüşti', 'тюштик' => 'tüştik', 'тюштилер' => 'tüştiler', 'тюштими' => 'tüştimi',
+               'тюштинъиз' => 'tüştiñiz', 'тюшювден' => 'tüşüvden', 'тюшюджек' => 'tüşücek',
+               'тюшюнген' => 'tüşüngen', 'тюшюнгендже' => 'tüşüngence', 'тюшюндже' => 'tüşünce',
+               'тюшюнджеге' => 'tüşüncege', 'тюшюнджелер' => 'tüşünceler', 'тюшюнджелери' => 'tüşünceleri',
+               'тюшюнджелерим' => 'tüşüncelerim', 'тюшюнджели' => 'tüşünceli', 'тюшюнджеси' => 'tüşüncesi',
+               'тюшюнди' => 'tüşündi', 'тюшюндим' => 'tüşündim', 'тюшюне' => 'tüşüne',
+               'тюшюнелер' => 'tüşüneler', 'тюшюнесинъиз' => 'tüşünesiñiz', 'тюшюнип' => 'tüşünip',
+               'тюшюнмеге' => 'tüşünmege', 'тюшюнмезсинъ' => 'tüşünmezsiñ', 'тюшюнмей' => 'tüşünmey',
+               'тюшюнмемек' => 'tüşünmemek', 'тюшюргенлер' => 'tüşürgenler', 'тюшюрди' => 'tüşürdi',
+               'тюшюрдик' => 'tüşürdik', 'тюшюре' => 'tüşüre', 'тюшюрип' => 'tüşürip', 'тюшюрмек' => 'tüşürmek',
+               'уджюм' => 'ücüm', 'удюр' => 'üdür', 'узюле' => 'üzüle', 'узюлип' => 'üzülip',
+               'узюльгенини' => 'üzülgenini', 'узюльди' => 'üzüldi', 'уйрюлип' => 'üyrülip',
+               'укюмет' => 'ükümet', 'укюмети' => 'ükümeti', 'укюметими' => 'ükümetimi',
+               'укюметимиз' => 'ükümetimiz', 'укюметини' => 'ükümetini', 'укюметининъ' => 'ükümetiniñ',
+               'укюметке' => 'ükümetke', 'укюметкеми' => 'ükümetkemi', 'укюметми' => 'ükümetmi',
+               'укюметнинъ' => 'ükümetniñ', 'укюметтен' => 'ükümetten', 'укюмран' => 'ükümran',
+               'улькюн' => 'ülkün', 'умюдим' => 'ümüdim', 'умют' => 'ümüt', 'умютлери' => 'ümütleri',
+               'умютсизден' => 'ümütsizden', 'усть' => 'üst', 'устьке' => 'üstke', 'устьлеринде' => 'üstlerinde',
+               'устьлериндеки' => 'üstlerindeki', 'устьлерине' => 'üstlerine', 'устьлерини' => 'üstlerini',
+               'устюрткъа' => 'üsturtqa', 'усьнюхаткъа' => 'üsnühatqa', 'усьнюхаты' => 'üsnühatı',
+               'усьтю' => 'üstü', 'усьтюмде' => 'üstümde', 'усьтюмдеки' => 'üstümdeki', 'усьтюме' => 'üstüme',
+               'усьтюнде' => 'üstünde', 'усьтюндеки' => 'üstündeki', 'усьтюндемиз' => 'üstündemiz',
+               'усьтюне' => 'üstüne', 'усьтюни' => 'üstüni', 'усьтюнлик' => 'üstünlik',
+               'усьтюнъизге' => 'üstüñizge', 'утёкунь' => 'ütökün', 'уфюрди' => 'üfürdi', 'учю' => 'üçü',
+               'учюмиз' => 'üçümiz', 'учюн' => 'üçün', 'учюнджи' => 'üçünci', 'учюнджисининъ' => 'üçüncisiniñ',
+               'ушюй' => 'üşüy', 'ушюмез' => 'üşümez', 'ушюмезсинъ' => 'üşümezsiñ',
+               'факультетинде' => 'fakultetinde', 'факультетине' => 'fakultetine',
+               'февральнинъ' => 'fevralniñ', 'харьковдаки' => 'harkovdaki', 'харьковдан' => 'harkovdan',
+               'чёкти' => 'çökti', 'чёкюрли' => 'çökürli', 'чёкюч' => 'çöküç', 'чёллюкке' => 'çöllükke',
+               'чёль' => 'çöl', 'чёльде' => 'çölde', 'чёльмек' => 'çölmek', 'чёткю' => 'çötkü',
+               'чёчамийлер' => 'çöçamiyler', 'чюнки' => 'çünki', 'чюрюди' => 'çürüdi', 'чюрюк' => 'çürük',
+               'шукюр' => 'şükür', 'шукюрлер' => 'şükürler', 'этюв' => 'etüv', 'этювден' => 'etüvden',
+               'этюви' => 'etüvi', 'этюдлар' => 'etüdlar', 'юзден' => 'yüzden', 'юзлеп' => 'yüzlep',
+               'юзлерини' => 'yüzlerini', 'юзлернен' => 'yüzlernen', 'юзлюги' => 'yüzlügi',
+               'юзлюкке' => 'yüzlükke', 'юзю' => 'yüzü', 'юзюм' => 'yüzüm', 'юзюме' => 'yüzüme',
+               'юзюмен' => 'yüzümen', 'юзюмни' => 'yüzümni', 'юзюнде' => 'yüzünde', 'юзюни' => 'yüzüni',
+               'юзюнинъ' => 'yüzüniñ', 'юзюнъ' => 'yüzüñ', 'юзюнъизге' => 'yüzüñizge', 'юклю' => 'yüklü',
+               'юксельтюв' => 'yükseltüv', 'юньлю' => 'yünlü', 'юньлюдже' => 'yünlüce',
+               'юртсеверлик' => 'yurtseverlik', 'юртюде' => 'yürtüde', 'юрьтю' => 'yürtü',
+               'юрьтюге' => 'yürtüge', 'юрьтюнинъ' => 'yürtüniñ', 'юрюльсе' => 'yürülse', 'юрюнъиз' => 'yürüñiz',
+               'юрюш' => 'yürüş', 'юрюши' => 'yürüşi', 'юрюшим' => 'yürüşim', 'юрюшини' => 'yürüşini',
+               'юрюшнен' => 'yürüşnen', 'юрюшни' => 'yürüşni',
+       ];
+
+       # map Cyrillic to Latin and back, whole word match only
+       # no variants: map exactly as is
+       # items with capture group refs (e.g., $1) are only mapped from the
+       # regex to the reference
+       private $exactCaseMappings = [
+               # аббревиатуры
+               # abbreviations
+               'ОБСЕ' => 'OBSE', 'КъМДж' => 'QMC', 'КъАЭ' => 'QAE', 'ГъСМК' => 'ĞSMK', 'ШСДжБ' => 'ŞSCB',
+               'КъМШСДж' => 'QMŞSC', 'КъДМПУ' => 'QDMPU', 'КъМПУ' => 'QMPU', 'КъЮШ' => 'QYŞ', 'ЮШ' => 'YŞ',
+       ];
+
+       # map Cyrillic to Latin and back, match end of word
+       # variants: all lowercase, all uppercase, first letter capitalized
+       # "first letter capitalized" variant was in the source
+       # items with capture group refs (e.g., $1) are only mapped from the
+       # regex to the reference
+       private $suffixMapping = [
+               # originally C2L
+               'иаль' => 'ial', 'нуль' => 'nul', 'кой' => 'köy', 'койнинъ' => 'köyniñ', 'койни' => 'köyni',
+               'койге' => 'köyge', 'койде' => 'köyde', 'койдеки' => 'köydeki', 'койден' => 'köyden',
+               'козь' => 'köz',
+
+               # originally L2C, here swapped
+               'етсин' => 'etsin',
+
+       ];
+
+       # map Cyrillic to Latin and back, match beginning of word
+       # variants: all lowercase, all uppercase, first letter capitalized
+       # items with capture group refs (e.g., $1) are only mapped from the
+       # regex to the reference
+       private $prefixMapping = [
+               # originally C2L
+               'буюк([^ъ])' => 'büyük$1', 'бую([гдйлмнпрстчшc])(и)' => 'büyü$1$2',
+               'буют([^ыа])' => 'büyüt$1', 'джонк([^ъ])' => 'cönk$1', 'коюм' => 'köyüm', 'коюнъ' => 'köyüñ',
+               'коюн([ди])' => 'köyün$1', 'куе' => 'küye', 'куркке' => 'kürkke', 'куркни' => 'kürkni',
+               'куркте' => 'kürkte', 'куркчи' => 'kürkçi', 'куркчю' => 'kürkçü',
+
+               # арабизмы на муи- муэ- / Arabic муи- муэ-
+               'му([иэИЭ])' => 'mü$1',
+
+               # originally L2C, here swapped
+               'итъаль' => 'ital',
+               'роль$1' => 'rol([^ü])',
+               'усть$1' => 'üst([knt])',
+
+       ];
+
+       private $Cyrl2LatnRegexes = [];
+       private $Latn2CyrlRegexes = [];
+
+       function loadRegs() {
+               // Regexes as keys need to be declared in a function.
+               $this->Cyrl2LatnRegexes = [
+                       ############################
+                       # относятся ко всему слову #
+                       # whole words              #
+                       ############################
+                       '/\b([34])(\-)юнджи\b/u' => '$1$2ünci',
+                       '/\b([34])(\-)ЮНДЖИ\b/u' => '$1$2ÜNCİ',
+
+                       # отдельно стоящие Ё и Я
+                       # stand-alone Ё and Я
+                       '/\bЯ\b/u' => 'Ya',
+                       '/\bЁ\b/u' => 'Yo',
+
+                       ############################
+                       # относятся к началу слова #
+                       # word prefixes            #
+                       ############################
+                       '/\bКъЮШн/u' => 'QYŞn',
+                       '/\bЮШн/u' => 'YŞn',
+
+                       # о => ö
+                       '/\b(['.Crh::C_M_CONS.'])о(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => '$1ö$2$3$4',
+                       '/\bо(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ö$1$2$3',
+                       '/\b(['.Crh::C_M_CONS.'])О(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' =>
+                               '$1Ö$2$3$4',
+                       '/\bО(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => 'Ö$1$2$3',
+
+                       '/\b(['.Crh::C_M_CONS.'])о(['.Crh::C_CONS.'])([еиэюьü])/u' => '$1ö$2$3',
+                       '/\bо(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ö$1$2',
+                       '/\b(['.Crh::C_M_CONS.'])О(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => '$1Ö$2$3',
+                       '/\bО(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => 'Ö$1$2',
+
+                       # ё => yö
+                       '/\bё(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([ьеюü])/u' => 'yö$1$2$3',
+                       '/\bЁ(['.Crh::C_CONS_LC.'])(['.Crh::C_CONS_LC.'])([ьеюü])/u' => 'Yö$1$2$3',
+                       '/\bЁ(['.Crh::C_CONS_UC.'])(['.Crh::C_CONS_UC.'])([ЬЕЮÜ])/u' => 'YÖ$1$2$3',
+                       '/\bё(['.Crh::C_CONS.'])([ьеюü])/u' => 'yö$1$2',
+                       '/\bЁ(['.Crh::C_CONS_LC.'])([ьеюü])/u' => 'Yö$1$2',
+                       '/\bЁ(['.Crh::C_CONS_UC.'])([ЬЕЮÜ])/u' => 'YÖ$1$2',
+
+                       # у => ü, ую => üyü
+                       '/\b(['.Crh::C_M_CONS.'])у(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => '$1ü$2$3$4',
+                       '/\bу(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ü$1$2$3',
+                       '/\bую(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => 'üyü$1$2$3',
+                       '/\b(['.Crh::C_M_CONS.'])У(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' =>
+                               '$1Ü$2$3$4',
+                       '/\bУ(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => 'Ü$1$2$3',
+                       '/\bУю(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => 'Üyü$1$2$2',
+                       '/\bУЮ(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ÜYÜ$1$2$3',
+
+                       '/\b(['.Crh::C_M_CONS.'])у(['.Crh::C_CONS.'])([еиэюьü])/u' => '$1ü$2$3',
+                       '/\bу(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ü$1$2',
+                       '/\bую(['.Crh::C_CONS.'])([еиэюьü])/u' => 'üyü$1$2',
+                       '/\b(['.Crh::C_M_CONS.'])У(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => '$1Ü$2$3',
+                       '/\bУ(['.Crh::C_CONS.'])([еиэюьüЕИЭЮЬÜ])/u' => 'Ü$1$2',
+                       '/\bУю(['.Crh::C_CONS.'])([еиэюьü])/u' => 'Üyü$1$2',
+                       '/\bУЮ(['.Crh::C_CONS.'])([еиэюьü])/u' => 'ÜYÜ$1$2',
+
+                       # ю => yü
+                       '/\b([аыоуеиёюАЫОУЕИЁЮ]?)ю(['.Crh::C_CONS.'])(['.Crh::C_CONS.'])([ьеюü])/u' => '$1yü$2$3$4',
+                       '/\b([АЫОУЕИЁЮ]?)Ю(['.Crh::C_CONS_LC.'])(['.Crh::C_CONS_LC.'])([ьеюü])/u' => '$1Yü$2$3$4',
+                       '/\b([АЫОУЕИЁЮ]?)Ю(['.Crh::C_CONS_UC.'])(['.Crh::C_CONS_UC.'])([ЬЕЮÜ])/u' => '$1YÜ$2$3$4',
+                       '/\b([аыоуеиёюАЫОУЕИЁЮ]?)ю(['.Crh::C_CONS.'])([ьеюü])/u' => '$1yü$2$3',
+                       '/\b([АЫОУЕИЁЮ]?)Ю(['.Crh::C_CONS_LC.'])([ьеюü])/u' => '$1Yü$2$3',
+                       '/\b([АЫОУЕИЁЮ]?)Ю(['.Crh::C_CONS_UC.'])([ЬЕЮÜ])/u' => '$1YÜ$2$3',
+
+                       # e => ye, я => ya
+                       '/\bе/u' => 'ye',
+                       '/\bЕ(['.Crh::C_LC.'cğñqöü])/u' => 'Ye$1',
+                       '/\bЕ(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'YE$1',
+                       '/\bя/u' => 'ya',
+                       '/\bЯ(['.Crh::C_LC.'cğñqöü])/u' => 'Ya$1',
+                       '/\bЯ(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'YA$1',
+                       '/([аеёиоуыэюяйьъaeöüАЕЁИОУЫЭЮЯЙЬЪAEÖÜ])е/u' => '$1ye',
+                       '/([аеёиоуыэюяйьъaeöüАЕЁИОУЫЭЮЯЙЬЪAEÖÜ])Е(['.Crh::C_LC.'cğñqöü])/u' => '$1Ye$2',
+                       '/([аеёиоуыэюяйьъaeöüАЕЁИОУЫЭЮЯЙЬЪAEÖÜ])Е(['.Crh::C_UC.'CĞÑQÖÜ])/u' => '$1YE$2',
+                       '/([аеёиоуыэюяйьъaeöüğqАЕЁИОУЫЭЮЯЙЬЪAEÖÜĞQ])я/u' => '$1ya',
+                       '/([аеёиоуыэюяйьъaeöüğqАЕЁИОУЫЭЮЯЙЬЪAEÖÜĞQ])Я(['.Crh::C_LC.'cğñqöü])/u' => '$1Ya$2',
+                       '/([аеёиоуыэюяйьъaeöüğqАЕЁИОУЫЭЮЯЙЬЪAEÖÜĞQ])Я(['.Crh::C_UC.'CĞÑQÖÜ])/u' => '$1YA$2',
+
+                       ###############################
+                       # не зависят от места в слове #
+                       # position independent        #
+                       ###############################
+
+                       # слова на -льон
+                       # words with -льон
+                       '/льон/u' => 'lyon',
+                       '/ЛЬОН/u' => 'LYON',
+
+                       '/козь([^я])/u' => 'köz$1',
+                       '/Козь([^я])/u' => 'Köz$1',
+                       '/КОЗЬ([^Я])/u' => 'KÖZ$1',
+
+                       # Ö, Ü 1-й заход: ё, ю после согласных > ö, ü
+                       # Ö, Ü 1st instance: ё, ю after consonants > ö, ü
+                       '/(['.Crh::C_CONS.'])ю/u' => '$1ü',
+                       '/(['.Crh::C_CONS.'])Ю/u' => '$1Ü',
+                       '/(['.Crh::C_CONS.'])ё/u' => '$1ö',
+                       '/(['.Crh::C_CONS.'])Ё/u' => '$1Ö',
+
+                       # остальные вхождения о, у, ё, ю
+                       # other occurences of о, у, ё, ю
+                       '/Ё(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'YO$2',
+                       '/Ю(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'YU$2',
+
+                       # Ц & Щ
+                       '/Ц(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'TS$2',
+                       '/Щ(['.Crh::C_UC.'CĞÑQÖÜ])/u' => 'ŞÇ$2',
+               ];
+
+               $this->Latn2CyrlRegexes = [
+                       # буква Ё - первый заход
+                       # расставляем Ь после согласных
+                       '/^([yY])ö(['.Crh::L_N_CONS.'])([aAuU'.Crh::L_CONS.']|$)/u' => '$1ö$2ь$3',
+                       '/^([yY])Ö(['.Crh::L_N_CONS.'])([aAuU'.Crh::L_CONS.']|$)/u' => '$1Ö$2Ь$3',
+                       '/^AQŞ(['.Crh::WORD_ENDS.'ngd])/u' => 'АКъШ$1',
+
+                       # буква Ю - первый заход
+                       # расставляем Ь после согласных
+                       '/^([yY])ü(['.Crh::L_N_CONS.'])([aAuU'.Crh::L_CONS.']|$)/u' => '$1ü$2ь$3',
+                       '/^([yY])Ü(['.Crh::L_N_CONS.'])([aAuU'.Crh::L_CONS.']|$)/u' => '$1Ü$2Ь$3',
+
+                       '/^([bcgkpşBCGKPŞ])ö(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1ö$2ь$3',
+                       '/^([bcgkpşBCGKPŞ])Ö(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1Ö$2Ь$3',
+                       '/^([bcgkpşBCGKPŞ])Ö(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1Ö$2Ь$3',
+                       '/^([bcgkpşBCGKPŞ])ü(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1ü$2ь$3',
+                       '/^([bcgkpşBCGKPŞ])Ü(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1Ü$2Ь$3',
+                       '/^([bcgkpşBCGKPŞ])Ü(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => '$1Ü$2Ь$3',
+
+                        # ö и ü в начале слова
+                        # случаи, когда нужен Ь
+                       '/^ö(['.Crh::L_N_CONS.'pP])(['.Crh::L_CONS.']|$)/u' => 'ö$1ь$2',
+                       '/^Ö(['.Crh::L_N_CONS_LC.'p])(['.Crh::L_CONS.']|$)/u' => 'Ö$1ь$2',
+                       '/^Ö(['.Crh::L_N_CONS_UC.'P])(['.Crh::L_CONS.']|$)/u' => 'Ö$1Ь$2',
+                       '/^ü(['.Crh::L_N_CONS.'])(['.Crh::L_CONS.']|$)/u' => 'ü$1ь$2',
+                       '/^Ü(['.Crh::L_N_CONS_LC.'])(['.Crh::L_CONS.']|$)/u' => 'Ü$1ь$2',
+                       '/^Ü(['.Crh::L_N_CONS_UC.'])(['.Crh::L_CONS.']|$)/u' => 'Ü$1Ь$2',
+
+                       '/ts$/u' => 'ц',
+                       '/şç$/u' => 'щ',
+                       '/Ş[çÇ]$/u' => 'Щ',
+                       '/T[sS]$/u' => 'Ц',
+
+                       # Ь после Л
+                       # add Ь after Л
+                       '/(['.Crh::L_F.'])l(['.Crh::L_CONS_LC.']|$)/u' => '$1ль$2',
+                       '/(['.Crh::L_F_UC.'])L(['.Crh::L_CONS.']|$)/u' => '$1ЛЬ$2',
+
+                       # относятся к началу слова
+                       '/^ts/u' => 'ц',
+                       '/^T[sS]/u' => 'Ц',
+
+                       '/^şç/u' => 'щ',
+                       '/^Ş[çÇ]/u' => 'Щ',
+
+                       # Э
+                       '/(^|['.Crh::L_VOW.'аеэяАЕЭЯ])e/u' => '$1э',
+                       '/(^|['.Crh::L_VOW_UC.'АЕЭЯ])E/u' => '$1Э',
+
+                       '/^(['.Crh::L_M_CONS.'])ö/u' => '$1о',
+                       '/^(['.Crh::L_M_CONS.'])Ö/u' => '$1О',
+                       '/^(['.Crh::L_M_CONS.'])ü/u' => '$1у',
+                       '/^(['.Crh::L_M_CONS.'])Ü/u' => '$1У',
+
+                       '/^ö/u' => 'о',
+                       '/^Ö/u' => 'О',
+                       '/^ü/u' => 'у',
+                       '/^Ü/u' => 'У',
+
+                       # некоторые исключения
+                       # some exceptions
+                       '/maal([^e])/u' => 'мааль$1',
+                       '/Maal([^e])/u' => 'Мааль$1',
+                       '/MAAL([^E])/u' => 'МААЛЬ$1',
+                       '/küf([^eü])/u' => 'куфь$1',
+                       '/Küf([^eü])/u' => 'Куфь$1',
+                       '/KÜF([^EÜ])/u' => 'КУФЬ$1',
+                       '/köz([^eü])/u' => 'козь$1',
+                       '/Köz([^eü])/u' => 'Козь$1',
+                       '/KÖZ([^EÜ])/u' => 'КОЗЬ$1',
+
+                       # Punctuation
+                       '/#|No\./' => '№',
+
+                       # некоторые случаи употребления Ц
+                       '/tsi([^zñ])/u' => 'ци$1',
+                       '/T[sS][iİ]([^zZñÑ])/u' => 'ЦИ$1',
+                       '/ts([ou])/u' => 'ц$1',
+                       '/T[sS]([oOuU])/u' => 'Ц$1',
+                       '/ts(['.Crh::L_CONS.'])/u' => 'ц$1',
+                       '/T[sS](['.Crh::L_CONS.'])/u' => 'Ц$1',
+                       '/(['.Crh::L_CONS.'])ts/u' => '$1ц',
+                       '/(['.Crh::L_CONS.'])T[sS]/u' => '$1Ц',
+                       '/tsиал/u' => 'циал',
+                       '/TSИАЛ/u' => 'ЦИАЛ',
+
+                       # убираем ьi
+                       # remove ьi (note Cyrillic ь and Latin i)
+                       '/[ьЬ]([iİ])/u' => '$1',
+
+                       # ya & ye
+                       '/(['.Crh::L_CONS.'])ya/u' => '$1ья',
+                       '/(['.Crh::L_CONS.'])Y[aA]/u' => '$1ЬЯ',
+                       '/(['.Crh::L_CONS.'])ye/u' => '$1ье',
+                       '/(['.Crh::L_CONS.'])Y[eE]/u' => '$1ЬЕ',
+
+                        # расставляем Ь перед Ё
+                        # place Ь in front of Ё
+                       '/(['.Crh::L_CONS.'])y[oö]/u' => '$1ьё',
+                       '/(['.Crh::L_CONS.'])Y[oOöÖ]/u' => '$1ЬЁ',
+                        # оставшиеся вхождения yo и yö
+                        # remaining occurrences of yo and yö
+                       '/y[oö]/u' => 'ё',
+                       '/[yY][oOöÖ]/u' => 'Ё',
+
+                        # расставляем Ь перед Ю
+                        # place Ь in front of Ю
+                       '/(['.Crh::L_CONS.'])y[uü]/u' => '$1ью',
+                       '/(['.Crh::L_CONS.'])Y[uUüÜ]/u' => '$1ЬЮ',
+                        # оставшиеся вхождения yu и yü
+                        # remaining occurrences of yu and yü
+                       '/y[uü]/u' => 'ю',
+                       '/[yY][uUüÜ]/u' => 'Ю',
+
+                       # убираем ьa
+                       # remove ьa (note Cyrillic ь and Latin a)
+                       '/[ьЬ]([aA])/u' => '$1',
+
+                       # дж
+                       '/C(['.Crh::L_UC.Crh::C_UC.'Ъ])/u' => 'ДЖ$1',
+
+                       # гъ, къ, нъ
+                       # гъ, къ, нъ
+                       '/Ğ(['.Crh::L_UC.Crh::C_UC.'Ъ])/u' => 'ГЪ$1',
+                       '/Q(['.Crh::L_UC.Crh::C_UC.'Ъ])/u' => 'КЪ$1',
+                       '/Ñ(['.Crh::L_UC.Crh::C_UC.'Ъ])/u' => 'НЪ$1',
+
+               ];
+       }
+
+       private $CyrlCleanUpRegexes = [
+               '/([клнрст])ь\1/u' => '$1$1',
+               '/([КЛНРСТ])Ь\1/u' => '$1$1',
+               '/К[ьЬ]к/u' => 'Кк',
+               '/Л[ьЬ]л/u' => 'Лл',
+               '/Н[ьЬ]н/u' => 'Нн',
+               '/Р[ьЬ]р/u' => 'Рр',
+               '/С[ьЬ]с/u' => 'Сс',
+               '/Т[ьЬ]т/u' => 'Тт',
+
+               # убираем ьы и ь..ы
+               # remove ьы и ь..ы
+               '/[ьЬ]ы/u' => 'ы',
+               '/ЬЫ/u' => 'Ы',
+               '/[ьЬ]([гдклмнпрстчшГДКЛМНПРСТЧШ])ы/u' => '$1ы',
+               '/Ь([гдклмнпрстчшГДКЛМНПРСТЧШ])Ы/u' => '$1Ы',
+               '/[ьЬ]([гкнГКН])([ъЪ])ы/u' => '$1$2ы',
+               '/Ь([ГКН])ЪЫ/u' => '$1ЪЫ',
+
+               # убираем йь
+               # remove йь
+               '/йь/u' => 'й',
+               '/ЙЬ/u' => 'Й',
+
+               # частичное решение проблемы слова юз - 100
+               # Partial solution of the problem of the word юз ("100")
+               # notice that these are cross-word patterns
+               '/эки юзь/u' => 'эки юз', '/Эки юзь/u' => 'Эки юз', '/ЭКИ ЮЗЬ/u' => 'ЭКИ ЮЗ',
+               '/учь юзь/u' => 'учь юз', '/Учь юзь/u' => 'Учь юз', '/УЧЬ ЮЗЬ/u' => 'УЧЬ ЮЗ',
+               '/дёрт юзь/u' => 'дёрт юз', '/Дёрт юзь/u' => 'Дёрт юз', '/ДЁРТ ЮЗЬ/u' => 'ДЁРТ ЮЗ',
+               '/беш юзь/u' => 'беш юз', '/Беш юзь/u' => 'Беш юз', '/БЕШ ЮЗЬ/u' => 'БЕШ ЮЗ',
+               '/алты юзь/u' => 'алты юз', '/Алты юзь/u' => 'Алты юз', '/АЛТЫ ЮЗЬ/u' => 'АЛТЫ ЮЗ',
+               '/еди юзь/u' => 'еди юз', '/Еди юзь/u' => 'Еди юз', '/ЕДИ ЮЗЬ/u' => 'ЕДИ ЮЗ',
+               '/секиз юзь/u' => 'секиз юз', '/Секиз юзь/u' => 'Секиз юз', '/СЕКИЗ ЮЗЬ/u' => 'СЕКИЗ ЮЗ',
+               '/докъуз юзь/u' => 'докъуз юз', '/Докъуз юзь/u' => 'Докъуз юз', '/ДОКЪУЗ ЮЗЬ/u' => 'ДОКЪУЗ ЮЗ',
+       ];
+}
index d464d9b..2c30c04 100644 (file)
        "rev-deleted-text-permission": "tina kasabelih masumad <strong> masipu </strong>.\nkapah tayza[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} misipu nasulitan nazipa’an] miala pulita cesyun.",
        "rev-suppressed-text-permission": "tina kasabelih sumad nay <strong> masatezep paazih </strong>.\ntaneng tayza [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} satezep paazih nasulitan nazipa’an] maala pulita cesyun.",
        "rev-deleted-text-unhide": "tina kasabelih a sumad nay <strong> masipu </strong>.\nkapah tayza [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} misipu nasulitan nazipa’an] maala pulita cesyun.\namahica kisu palalid, kapahtu kisu [$1 miciwsace tina sumad].",
-       "rev-suppressed-text-unhide": "This page revision has been <strong>suppressed</strong>.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} suppression log].\nYou can still [$1 view this revision] if you wish to proceed.",
+       "rev-suppressed-text-unhide": "tina kasabelih a sumad nay <strong> masatezep paazih </strong>。\nkapah tayza [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} satezep paazih nasulitan nazipa’an] miala pulita cesyun。\namahica kisu apalalid,kapah tu kisu [$1 miciwsace tina sumad]",
        "rev-suppressed-text-view": "tina kasabelih a sumad nay <strong> masatezep paazih </strong>.\nkapah kisu lalid miciwsace misumad,kapah tayza [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} satezep paazih nazipa’an ] miala pulita cesyun.",
-       "rev-deleted-no-diff": "zayhan kasabelih u cacay masumad nu ayaway mapa <strong>masipu</strong>, la’cus kisu miciwsace tu kasasizuma.",
+       "rev-deleted-no-diff": "zayhan kasabelih ilabu’ a cacay a sumad nu ayaway nay <strong> masipu tuway </strong>, la’cus kisu miciwsace   sasizuma.\ntaneng tayza [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}] masipu nasulitan nazipa’an maala pulitaay a cesyun.",
        "rev-suppressed-no-diff": "zayhan kasabelih ilabu’ a cacay a sumad nu ayaway nay <strong>masipu tuway</strong>, la’cus kisu miciwsace sasizuma.",
        "rev-deleted-unhide-diff": "miciwsace sasizuma ilabu’ay ku cacay masumad nu ayaway nay <strong> masipu tu </strong>.\nkapah tyaza [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} masipu nasulitan nazipa’an] maala pulita  kalunasulitan.\namahica kisu apalalid, kapah tu kisu [$1 miwsace tina zuma].",
        "rev-suppressed-unhide-diff": "miciwsace sasizuma ilabu’ay a cacay masumad nu ayaway nay <strong>masatezep paazih</strong>.\nkapah tayza [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} satezep paazih nasulitan nazipa’an] maala pulitaya a cesyun.\namahica kisu palalid, kapah tu kisu [$1 miciwsace tina sasizuma].",
        "recentchangesdays-max": "sayadahay $1 {{PLURAL:$1|a demiad}}",
        "recentchangescount": "pataayaw tu kawaw paazihay nu mikawaway-kalumyiti nikayadah:",
        "prefs-help-recentchangescount": "uyni yamalyilu capi demiad a sumad, kasabelih nasulitan-nazipa’an atu nazipa’an",
-       "prefs-help-watchlist-token2": "miaca aazihan numisuay a miazihay a piazihan-tu-sulit maydihay a mima-sabuhat.\namahicahica tademaw matineng ku mama-sabuhat dada’ taneng maasip numisuay a miazihay a piazihan-tu-sulit,sisa amana kilul tu nizateng kasasimel tu zuma a tadeamw.\namahica izaw ku maydih [[Special:ResetTokens|kapah kisu miliyaw patizeng mima-sabuhat]].",
        "savedprefs": "masuped setin tu kanamuhan nu misu.",
        "savedrights": "masuped tu {{GENDER:$1|$1}} misaungayay a tungus.",
        "timezonelegend": "tatukian a kakitizaan:",
        "rcfilters-filterlist-noresults": "caykatepa sakacucek nu misapili’",
        "rcfilters-noresults-conflict": "zayhan kilim sakacucek sasula’cus,cay katepa ku heci",
        "rcfilters-state-message-subset": "tina sakacucek nu misapili’ inayi’ ku laheci, zayhan kya heci yamalyilu isasa’ subal saahebalay a {{PLURAL:$2|sakacucek nu misapili’}} cacay ku ilabu’ay (mitanam kasiwantan sacuzu’ amipili’):$1",
+       "rcfilters-state-message-fullcoverage": "mipili’ i luyaluy labu’ay sacahamin sakacucek nu misapili’ atu inayi’ mipili’ sa malecad, dayhiw tina sakacucek nu misapili’caay kalahci. luyaluy yamalyilu tu: $1",
        "rcfilters-filtergroup-authorship": "paaninay a masacudaday",
        "rcfilters-filter-editsbyself-label": "numisuay a mikawaway-kalumyiti",
        "rcfilters-filter-editsbyself-description": "numisuay a paanin",
        "rcfilters-filter-categorization-description": "mamicunus i kakuniza saca nay kakuniza masipuay a kasabelih nasulitanㄡ",
        "rcfilters-filter-logactions-label": "saungay a nasulitan nazipa’an",
        "rcfilters-filter-logactions-description": "mikuwan saungay, patizeng canghaw, misipu kasabelih, patapabaw...",
+       "rcfilters-hideminor-conflicts-typeofchange-global": "\"mikilulay mikawaway-kalumyiti\" a sakacucek nu misapili’ sasula’cus tu cacay saca yadah sumad nikalahizaan sakacucek nu misapili’,zayhan izaw hatizaay sumad nikalahizaan la’cus patuzu’ay tu \"mikilulay\". sasula’cusay a sakacucek nu misapili’ sacuzu’ tu i pabaw nu pisaungay a subal nu sakacucek nu misapili’.",
        "rcfilters-hideminor-conflicts-typeofchange": "izaw ku zumaay misumad nikalahizaan la’cus matuzu’ay mala \"mikilulay\", sisa tina sakacucek nu misapili’ atu isasa’ay a sumad nikalahizaan sakacucek nu misapili’ sasula’cus: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "tina misumad nikalahizaan sakacucek nu misapili’ atu \"mikilulay mikawaway-kalumyiti\" sakacucek nu misapili’sasula’cus, uzuma misumad nikalahizaan la’cus matuzu’ay ku \"mikilulay\"",
        "rcfilters-filtergroup-lastRevision": "sabaluhay masumad",
        "wantedfiles": "maydihay a tangan",
        "wantedfiletext-cat": "isasa’ay a tangan masaungay sa, uyzasa tangan inayi’. hekal suped-sulu a tangan kanahatu mueneng, uyzasa tina piazihan-tu-sulit apasilsil tuway. tina kakuniza pacebaay a tubeli kasacacay saka <del>sipu-kenis</del> sacuzu’. zuma sa, kasabelih sipakabit ilabu' tangan inayi’ apaazih i piazihan-tu-sulit [[:$1]].",
        "wantedfiletext-cat-noforeign": "isasa’ay a tangan mapasaungay tu uyzasa inayi’ay. tina a dada’ silabas kakaiyan tu, kasabelih sipakabit ilabu’ nika inayi’ay a tangan mapalaylay i [[:$1]]",
+       "wantedfiletext-nocat": "isasa’ tangan masaungay sa, zumasatu tangan inayi’. hekal suped-sulu a tangan kanahatu, uyzasa tina piazihan-tu-sulit apasilsil tuway. uyniyan paceba’ay a  kasacacay apacuzu’ ku <del> sipu-kenis </del> sacuzu’.",
        "wantedtemplates": "maydihay a taazihan mitudung",
        "mostlinked": "masasiket sayadahay a kasabelih",
        "mostlinkedcategories": "masasiket sayadahay a kakuniza",
        "file-info-size-pages": "$1 × $2 syangsu, hacica ku tabaki nu tangan:$3,MIME kakuniza: $4, $5 {{PLURAL:$5|ku kasabelih}}",
        "file-nohires": "inay ku sangaleb takalaway a katingalaw, kapah tu nipabeli.",
        "svg-long-desc": "SVG tangan, maazihay hacica ku tabaki $1 × $2  syangsu, tangan hacica ku tabaki: $3",
-       "svg-long-desc-animated": "SVG tangan, maazihay hacica ku tabaki $1 × $2 syangsu, tangan hacica ku tabaki: $3",
+       "svg-long-desc-animated": "SVG kulit-iga tangan, maazihay hacica tabaki:$1 × $2 syangsu, tangan pinalu hacica tabaki: $3",
        "svg-long-error": "la’cusay a SVG tangan: $1",
        "show-big-image": "saayaway a tangan",
        "show-big-image-preview": "pataayaway miazih hacica ku tabaki: $1.",
index 89cd878..ed95d9d 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|لا مراجعات متوسطة|مراجعة متوسطة واحدة|مراجعتان متوسطتان|$1 مراجعات متوسطة|$1 مراجعة متوسطة}} بواسطة نفس المستخدم غير {{PLURAL:$1|معروضة|معروضة|معروضتين|معروضة}})",
        "diff-multi-otherusers": "({{PLURAL:$1|لا مراجعات|مراجعة متوسطة واحدة|مراجعتان متوسطتان|$1 مراجعات متوسطة|$1 مراجعة متوسطة}} بواسطة {{PLURAL:$2|ولا مستخدم|مستخدم واحد آخر|مستخدمين اثنين آخرين|$2 مستخدمين|$2 مستخدماً|$2 مستخدم}} غير {{PLURAL:$1|معروضة|معروضة|معروضتين|معروضة}})",
        "diff-multi-manyusers": "({{PLURAL:$1||مراجعة واحدة متوسطة غير معروضة أجراها|مراجعتان متوسطتان غير معروضتان أجراهما|$1 مراجعات متوسطة غير معروضة أجراها|$1 مراجعة متوسطة غير معروضة أجراها}} أكثر من {{PLURAL:$2||مستخدم واحد|مستخدمين|$2 مستخدمين|$2 مستخدمًا|$2 مستخدم}}.)",
+       "diff-paragraph-moved-tonew": "الفقرة تم نقلها. اضغط للذهاب للموقع الجديد.",
+       "diff-paragraph-moved-toold": "الفقرة تم نقلها. اضغط للذهاب للموقع القديم.",
        "difference-missing-revision": "{{PLURAL:$2|مراجعة واحدة|$2 مراجعات}} لهذا الفرق ($1) {{PLURAL:$2|لم|لم}} يتم إيجادها.\n\nهذا يحدث عادة عن طريق اتباع وصلة فرق قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "searchresults": "نتائج البحث",
        "searchresults-title": "نتائج البحث عن \"$1\"",
        "recentchangesdays-max": "الحد الأقصى {{PLURAL:$1|أقل من يوم|يوم واحد|يومان|$1 أيام|$1 يوما|$1 يوم}}",
        "recentchangescount": "عدد التعديلات الظاهرة مبدئيا:",
        "prefs-help-recentchangescount": "بما في ذلك أحدث التغييرات وتاريخ الصفحات والسجلات.",
-       "prefs-help-watchlist-token2": "هذا هو المفتاح السري لتغذية الويب لقائمة مراقبتك.\nيمكن لأي شخص يعرفه أن يقرأ قائمة مراقبتك، ولذا لا تتشاركه مع أحد. [[Special:ResetTokens|انقر هنا إذا أردت إعادة ضبطه]].",
        "savedprefs": "تم حفظ تفضيلاتك.",
        "savedrights": "حُفظت المجموعات الجديدة {{GENDER:$1|للمستخدم|للمستخدمة}} $1.",
        "timezonelegend": "المنطقة الزمنية:",
        "rcfilters-restore-default-filters": "استرجاع المرشحات الافتراضية",
        "rcfilters-clear-all-filters": "مسح كل المرشحات",
        "rcfilters-show-new-changes": "عرض أحدث التغييرات",
-       "rcfilters-search-placeholder": "رشح التغييرات (استخدم القائمة أو البحث لاسم المرشح)",
+       "rcfilters-search-placeholder": "رشح التغييرات (استخدم القائمة أو ابحث عن اسم المرشح)",
        "rcfilters-invalid-filter": "مرشح غير صحيح",
        "rcfilters-empty-filter": "لا مرشحات فعالة. كل المساهمات معروضة.",
        "rcfilters-filterlist-title": "مرشحات",
index d255344..19a7f7d 100644 (file)
        "diff-multi-sameuser": "(не {{PLURAL:$1|паказаная $1 прамежная вэрсія|паказаныя $1 прамежныя вэрсіі|паказаныя $1 прамежных вэрсіяў}} аўтарства таго ж удзельніка)",
        "diff-multi-otherusers": "(не {{PLURAL:$1|паказаная $1 прамежная вэрсія|паказаныя $1 прамежныя вэрсіі|паказаныя $1 прамежных вэрсіяў}} аўтарства {{PLURAL:$2|1=яшчэ аднаго ўдзельніка|$2 удзельнікаў}})",
        "diff-multi-manyusers": "($1 {{PLURAL:$1|прамежная вэрсія|прамежныя вэрсіі|прамежных вэрсіяў}} $2 {{PLURAL:$2|удзельніка|удзельнікаў|удзельнікаў}} {{PLURAL:$1|не паказаная|не паказаныя|не паказаныя}})",
+       "diff-paragraph-moved-tonew": "Параграф быў перанесены. Націсьніце, каб перайсьці ў новае месца.",
+       "diff-paragraph-moved-toold": "Параграф быў перанесены. Націсьніце, каб перайсьці ў першапачатковае месца.",
        "difference-missing-revision": "{{PLURAL:$2|$2 вэрсія|$2 вэрсіі|$2 вэрсіяў}} з гэтымі адрозьненьнямі ($1) {{PLURAL:$2|не была|не былі}} знойдзеныя.\n\nЗвычайна гэта здараецца з-за пераходу па састарэлай спасылцы на старонку, якая была выдаленая.\nПадрабязнасьці можна знайсьці ў [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленьняў].",
        "searchresults": "Вынікі пошуку",
        "searchresults-title": "Вынікі пошуку для «$1»",
        "recentchangesdays-max": "Максымальна $1 {{PLURAL:$1|дзень|дні|дзён}}",
        "recentchangescount": "Колькасьць рэдагаваньняў для паказу па змоўчаньні:",
        "prefs-help-recentchangescount": "Гэта ўключае апошнія зьмены, гісторыі старонак і журналы падзеяў.",
-       "prefs-help-watchlist-token2": "Гэта сакрэтны ключ да стужкі вашага сьпісу назіраньня.\nКожны, хто ведае яго, можа атрымаць доступ да вашага сьпісу назіраньня, таму не дзяліцеся ім.\nКалі вам трэба, [[Special:ResetTokens|вы можаце скінуць яго]].",
        "savedprefs": "Вашыя налады былі захаваныя.",
        "savedrights": "Групы {{GENDER:$1|ўдзельніка|ўдзельніцы}} $1 былі захаваныя.",
        "timezonelegend": "Часавы пояс:",
        "email-blacklist-label": "Забараніць гэтым удзельнікам дасылаць мне лісты электроннай поштай:",
        "prefs-searchoptions": "Пошук",
        "prefs-namespaces": "Прасторы назваў",
-       "default": "па Ð·Ð¼Ð¾Ñ\9eÑ\87ванÑ\8cнÑ\96",
+       "default": "па змоўчаньні",
        "prefs-files": "Файлы",
-       "prefs-custom-css": "Ð\86ндÑ\8bвÑ\96дÑ\83алÑ\8cны CSS",
-       "prefs-custom-js": "Ð\86ндÑ\8bвÑ\96дÑ\83алÑ\8cнÑ\8b JS",
+       "prefs-custom-css": "УлаÑ\81ны CSS",
+       "prefs-custom-js": "УлаÑ\81нÑ\8b JavaScript",
        "prefs-common-css-js": "Агульны CSS/JS для ўсіх афармленьняў:",
        "prefs-reset-intro": "Вы можаце выкарыстоўваць гэтую старонку для замены Вашых наладаў на налады сайта па змоўчваньні.\nГэтае дзеяньне не можа быць адмененае.",
        "prefs-emailconfirm-label": "Пацьверджаньне адрасу электроннай пошты:",
        "uploadstash-file-not-found-no-thumb": "Не атрымалася здабыць мініятуру.",
        "uploadstash-file-not-found-no-local-path": "Няма лякальнага шляху да маштабаванага элемэнту.",
        "uploadstash-file-not-found-no-object": "Не атрымалася стварыць лякальны аб’ект файлу для мініятуры.",
+       "uploadstash-file-not-found-no-remote-thumb": "Памылка атрыманьня мініятуры: $1\nURL = $2",
+       "uploadstash-file-not-found-missing-content-type": "Адсутнічае загаловак тыпу зьместу.",
        "invalid-chunk-offset": "Няслушнае зрушэньне фрагмэнту",
        "img-auth-accessdenied": "Доступ забаронены",
        "img-auth-nopathinfo": "Адсутнічае PATH_INFO.\nВаш сэрвэр не ўстаноўлены на пропуск гэтай інфармацыі.\nМагчма, ён працуе праз CGI і не падтрымлівае img_auth.\nГлядзіце https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
index 03c7ece..947b5e8 100644 (file)
        "diff-multi-sameuser": "(একই ব্যবহারকারী দ্বারা সম্পাদিত {{PLURAL:$1|একটি মধ্যবর্তী সংশোধন|$1টি মধ্যবর্তী সংশোধন}} দেখানো হচ্ছে না)",
        "diff-multi-otherusers": "({{PLURAL:$2|একজন|$2 জন}} ব্যবহারকারী দ্বারা সম্পাদিত {{PLURAL:$1|একটি|$1টি}} মধ্যবর্তী সংশোধন দেখানো হচ্ছে না)",
        "diff-multi-manyusers": "($2 জনের বেশি {{PLURAL:$2|ব্যবহারকারীর}} সম্পাদিত {{PLURAL:$1|একটি মধ্যবর্তী সংশোধন|$1টি মধ্যবর্তী সংশোধন}} প্রদর্শিত হচ্ছে না)",
+       "diff-paragraph-moved-tonew": "অনুচ্ছেদ স্থানান্তর করা হয়েছে। নতুন অবস্থানে যাওয়ার জন্য ক্লিক করুন।",
+       "diff-paragraph-moved-toold": "অনুচ্ছেদ স্থানান্তর করা হয়েছে। পুরনো অবস্থানে যাওয়ার জন্য ক্লিক করুন।",
        "difference-missing-revision": "এই পার্থক্যের ($1) অন্তর্গত {{PLURAL:$2|একটি সংশোধিত সংস্করণ|$2টি সংশোধিত সংস্করণ}} খুঁজে পাওয়া যাচ্ছে না।\n\nসাধারণত মুছে ফেলা হয়েছে এমন পাতার মেয়াদ উত্তীর্ণ ইতিহাস পাতার লিংক খোলার কারণে এটি হতে পারে। \n[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} পাতা অবলুপ্তি লগে] বিস্তারিত তথ্য জানা যাবে।",
        "searchresults": "অনুসন্ধানের ফলাফল",
        "searchresults-title": "\"$1\" অনুসন্ধানের ফলাফল",
        "recentchangesdays-max": "সর্বোচ্চ $1 {{PLURAL:$1|দিনের}}",
        "recentchangescount": "সাম্প্রতিক পরিবর্তনে প্রদর্শিত সম্পাদনার পূর্বনির্ধারিত সংখ্যা:",
        "prefs-help-recentchangescount": "এতে সাম্প্রতিক পরিবর্তনগুলি, পাতার ইতিহাসগুলি এবং লগগুলি অন্তর্ভুক্ত।",
-       "prefs-help-watchlist-token2": "এটি আপনার নজরতালিকার ওয়েব ফিডের গোপন চাবি। কেউ যদি এটি জানতে পারেন, তাহলে তিনি আপনার নজরতালিকা পড়তে সক্ষম হবেন, তাই এটি প্রকাশ করবেন না। [[Special:ResetTokens|আপনার এটি পুনঃনির্ধারণ করার প্রয়োজন হলে এখানে ক্লিক করুন]]।",
        "savedprefs": "আপনার পছন্দগুলি সংরক্ষণ করা হয়েছে।",
        "savedrights": "{{GENDER:$1|$1}}-এর ব্যবহারকারী দল সংরক্ষিত হয়েছে।",
        "timezonelegend": "সময় অঞ্চল:",
        "htmlform-selectorother-other": "অন্য",
        "htmlform-no": "না",
        "htmlform-yes": "হ্যাঁ",
-       "htmlform-chosen-placeholder": "à¦\85পশন নির্বাচন করুন",
+       "htmlform-chosen-placeholder": "বিà¦\95লà§\8dপ নির্বাচন করুন",
        "htmlform-cloner-create": "আরও যোগ করুন",
        "htmlform-cloner-delete": "অপসারণ",
        "htmlform-cloner-required": "অন্তত একটি মূল্য আবশ্যক।",
index 941f9af..9d705ae 100644 (file)
@@ -68,7 +68,6 @@
        "underline-never": "Morse",
        "underline-default": "Merdeer dre ziouer",
        "editfont-style": "Stil font an takad skridaozañ :",
-       "editfont-default": "Diouzh ar merdeer",
        "editfont-monospace": "Font unesaouennet",
        "editfont-sansserif": "Font hep-serif",
        "editfont-serif": "Font serif",
        "explainconflict": "Enrollet eo bet ar bajenn-mañ war-lerc'h m'ho pefe kroget d'he c'hemmañ.\nE-krec'h an takad aozañ emañ an destenn evel m'emañ enrollet bremañ er bank roadennoù.\nHo kemmoù deoc'h a zeu war wel en takad aozañ traoñ.\nRet e vo deoc'h degas ho kemmoù d'an destenn zo evit poent.\nN'eus '''nemet''' an destenn zo en takad krec'h a vo saveteet pa klikot war \"$1\".",
        "yourtext": "Ho testenn",
        "storedversion": "Stumm enrollet",
-       "nonunicodebrowser": "'''DIWALLIT: N'eo ket skoret an Unicode gant ho merdeer. Un diskoulm da c'hortoz zo bet kavet evit ma c'hallfec'h kemmañ pennadoù : dont a raio war wel an arouezennoù an-ASCII er prenestr skridaozañ evel kodoù eizhdekvedennel.'''",
        "editingold": "'''Diwallit : o kemm ur stumm kozh eus ar bajenn-mañ emaoc'h. Mard enrollit bremañ e vo kollet an holl gemmoù bet graet abaoe ar stumm-se.'''",
        "yourdiff": "Diforc'hioù",
        "copyrightwarning": "Sellet e vez ouzh an holl degasadennoù graet war {{SITENAME}} evel ouzh degasadennoù a zouj da dermenoù ar $2 (Sellet ouzh $1 evit gouzout hiroc'h). Mar ne fell ket deoc'h e vefe embannet ha skignet ho skridoù, arabat kas anezho.<br />\nHeñveldra, prometiñ a rit kemer perzh dre zegas skridoù savet ganeoc'h hepken pe tennet eus ur vammenn frank a wirioù.\n'''NA IMPLIJIT KET LABOURIOÙ GANT GWIRIOÙ AOZER (COPYRIGHT) HEP AOTRE D'OBER KEMENT-SE!'''",
        "recentchangesdays-max": "D'ar muiañ $1 {{PLURAL:$1|deiz|deiz}}",
        "recentchangescount": "Niver a gemmoù da ziskouez dre ziouer",
        "prefs-help-recentchangescount": "Kemer a ra an dra-mañ e kont ar c'hemmoù diwezhañ, istor ar pajennoù hag ar marilhoù.",
-       "prefs-help-watchlist-token2": "Homañ zo an alc'hwez kuzh d'ho roll-evezhiañ davit boued war ar web. Forzh piv a anavez anezhañ a c'hall lenn ho roll-evezhiañ, setu na lavarit grit diwar e benn. M'ho pez ezhomm, e c'hallit [[Special:ResetTokens|assevel anezhañ]].",
        "savedprefs": "Enrollet eo bet ar penndibaboù.",
        "savedrights": "Enrollet eo bet strolladoù implijer {{GENDER:$1|$1}}.",
        "timezonelegend": "Takad eur :",
        "rcfilters-group-results-by-page": "Strollañ an disoc'hoù dre bajenn",
        "rcfilters-grouping-title": "O strollañ",
        "rcfilters-activefilters": "Siloù oberiant",
+       "rcfilters-advancedfilters": "Siloù araokaet",
+       "rcfilters-limit-title": "Kemmoù da vezañ diskouezet",
+       "rcfilters-limit-shownum": "Diskouez {{PLURAL:$1|ar c'hemm|an $1 kemm}} diwezhañ",
        "rcfilters-days-title": "Deizioù paseet",
        "rcfilters-hours-title": "Eurioù paseet",
        "rcfilters-days-show-days": "($1 {{PLURAL:$1|deiz}})",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|eur}}",
        "rcfilters-highlighted-filters-list": "Lakaet war wel : $1",
        "rcfilters-quickfilters": "Siloù enrollet",
-       "rcfilters-quickfilters-placeholder-title": "Liamm ebet enrollet evit c'hoazh",
+       "rcfilters-quickfilters-placeholder-title": "Sil ebet enrollet evit c'hoazh",
        "rcfilters-savedqueries-defaultlabel": "Siloù enrollet",
        "rcfilters-savedqueries-rename": "Adenvel",
        "rcfilters-savedqueries-setdefault": "Gweredekaat dre ziouer",
        "rcfilters-savedqueries-remove": "Dilemel",
        "rcfilters-savedqueries-new-name-label": "Anv",
        "rcfilters-savedqueries-apply-label": "Enrollañ an arventennoù",
+       "rcfilters-savedqueries-apply-and-setdefault-label": "Krouiñ ur sil dre ziouer",
        "rcfilters-savedqueries-cancel-label": "Nullañ",
        "rcfilters-savedqueries-add-new-title": "Enrollañ arventennoù ar sil en implij",
        "rcfilters-restore-default-filters": "Assevel ar siloù dre ziouer",
        "rcfilters-clear-all-filters": "Riñsañ an holl siloù",
+       "rcfilters-show-new-changes": "Gwelet ar c'hemmoù diwezhañ",
        "rcfilters-search-placeholder": "Silañ ar c'hemmoù diwezhañ (merdeiñ pe kregiñ da skrivañ)",
        "rcfilters-invalid-filter": "Sil direizh",
        "rcfilters-empty-filter": "Sil oberiant ebet. War wel emañ an holl gemmoù.",
        "rcfilters-filterlist-title": "Siloù",
-       "rcfilters-filterlist-whatsthis": "Petra eo se ?",
+       "rcfilters-filterlist-whatsthis": "Penaos ez a en-dro ?",
        "rcfilters-filterlist-feedbacklink": "Reiñ ho soñj diwar-benn ar siloù (beta) nevez",
        "rcfilters-highlightbutton-title": "Lakaat an disoc'hoù war wel",
        "rcfilters-highlightmenu-title": "Dibabit ul liv",
        "rcfilters-filter-user-experience-level-unregistered-label": "Divarilh",
        "rcfilters-filter-user-experience-level-unregistered-description": "Aozerien n'int ket kevreet.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Tud nevez-deuet",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Nebeutoc'h eget 10 kemm ha 4 devezh obererezh.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Aozerien enrollet ganto nebeutoc'h eget 10 kemm pe 4 devezh obererezh.",
        "rcfilters-filter-user-experience-level-learner-label": "Deskarded",
-       "rcfilters-filter-user-experience-level-learner-description": "Muioc'h a skiant-prenet eget an \"deraouidi\" hogen nebeutoc'h eget an \"implijerien arroutet\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Aozerien ganto muioc'h a skiant-prenet eget an \"deraouidi\" hogen nebeutoc'h eget an \"implijerien arroutet\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Implijerien arroutet",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ouzhpenn 30 devezh oberiantiz ha 500 kemm.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Aozerien graet ganto ouzhpenn 500 kemm ha dezho 30 devezh oberiantiz .",
        "rcfilters-filtergroup-automated": "Degasadennoù emgefre",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Kemmoù graet gant ostilhoù emgefre.",
        "rcfilters-filter-watchlist-watched-label": "War ar roll evezhiañ",
        "rcfilters-filter-watchlist-watched-description": "Kemmoù graet war pajennoù ho roll evezhiañ.",
        "rcfilters-filter-watchlist-watchednew-label": "Kemmoù nevez er roll evezhiañ",
+       "rcfilters-filter-watchlistactivity-unseen-label": "Kemmoù n'int ket bet gwelet",
+       "rcfilters-filter-watchlistactivity-seen-label": "Kemmoù bet gwelet",
        "rcfilters-filtergroup-changetype": "Seurt kemm",
        "rcfilters-filter-pageedits-label": "Kemmoù pajennoù",
        "rcfilters-filter-pageedits-description": "Kemmoù da danvez ar wiki, d'ar c'haozeadennoù, da zeskrivadurioù rummadoù...",
        "block": "Stankañ an implijer",
        "unblock": "Distankañ an implijer",
        "blockip": "Stankañ an {{GENDER:$1|implijer|implijerez}}",
-       "blockip-legend": "Stankañ an implijer",
        "blockiptext": "Grit gant ar furmskrid a-is evit stankañ ar moned skrivañ ouzh ur chomlec'h IP pe un implijer bennak.\nSeurt diarbennoù n'hallont bezañ kemeret nemet evit mirout ouzh ar vandalerezh hag a-du gant ar [[{{MediaWiki:Policy-url}}|reolennoù da vezañ heuliet]].\nRoit a-is an abeg resis (o verkañ, da skouer, roll ar pajennoù bet graet gaou outo).\nGallout a rit stankañ lijorennoù chomlec'hioù IP en ur ober gant an ereadur [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; /$1 eo al lijorenn hirañ aotreet evit IPv4 ha /$2 evit IPv6.",
        "ipaddressorusername": "Chomlec'h IP pe anv implijer",
        "ipbexpiry": "Pad ar stankadenn",
        "compare-title-not-exists": "N'eus ket eus an titl spisaet ganeoc'h.",
        "compare-revision-not-exists": "N'eus ket eus an adweladenn spisaet ganeoc'h.",
        "diff-form": "ur '''furmskrid'''",
+       "permanentlink": "Peurliamm",
        "dberr-problems": "Ho tigarez ! Kudennoù teknikel zo gant al lec'hienn-mañ.",
        "dberr-again": "Gortozit un nebeud munutennoù a-raok adkargañ.",
        "dberr-info": "(Dibosupl kevreañ ouzh an diaz roadennoù : $1)",
        "gotointerwiki-invalid": "Direizh eo an titl merket",
        "gotointerwiki-external": "Emaoc'h war-nes kuitaat {{SITENAME}} evit mont da welet [[$2]] hag a zo ul lec'hienn all a-ziforc'h.\n\n[$1 Klikañ amañ evit kenderc'hel war $1].",
        "undelete-cantedit": "N'hallit ket diziverkañ ar bajenn-mañ rak n'oc'h ket aotreet da gemmañ anezhi.",
-       "undelete-cantcreate": "N'hallit ket diziverkañ ar bajenn-mañ rak n'eus pajenn ebet gant an anv-mañ ha n'oc'h ket aotreet da grouiñ ar bajenn-mañ."
+       "undelete-cantcreate": "N'hallit ket diziverkañ ar bajenn-mañ rak n'eus pajenn ebet gant an anv-mañ ha n'oc'h ket aotreet da grouiñ ar bajenn-mañ.",
+       "pagedata-title": "Roadennoù ar bajenn",
+       "pagedata-bad-title": "Titl direizh : $1."
 }
index ffa4bc0..ccd6d99 100644 (file)
        "recentchangesdays-max": "(màxim $1 {{PLURAL:$1|dia|dies}})",
        "recentchangescount": "Nombre d'edicions a mostrar per defecte:",
        "prefs-help-recentchangescount": "Inclou els canvis recents, els historials de pàgines i els registres.",
-       "prefs-help-watchlist-token2": "Aquesta és la clau secreta pel canal de continguts de la vostra llista de seguiment.\nQualsevol que la conegui podria llegir la vostra llista de seguiment, així que no la compartiu.\n[[Special:ResetTokens|Cliqueu aquí si voleu restaurar-la]].",
        "savedprefs": "S’han desat les vostres preferències.",
        "savedrights": "S'han desat els grups d'usuari de {{GENDER:$1|$1}}.",
        "timezonelegend": "Fus horari:",
index cf9bf35..d2fe335 100644 (file)
        "recentchangesdays-max": "Къезиг $1 {{PLURAL:$1|дена}}",
        "recentchangescount": "Ӏадйитаран кепаца гойтуш долу нисдарийн дукхалла",
        "prefs-help-recentchangescount": "Гойту керла нисдарш, агӀонийн истори, тептарш.",
-       "prefs-help-watchlist-token2": "Иза хьан тергаме могӀан къайла догӀа ду.\nМуьлха и хуучунна йиш ю хьан тергаме могӀам беша, цундела ма хаийта иза кхечаьрга. [[Special:ResetTokens|ТӀетаӀа йе кхуза и хьайга кхосса лууш делахь]].",
        "savedprefs": "Хьан гӀирс Ӏалашбина.",
        "savedrights": "{{GENDER:$1|$1}} декъашхочун бакъонаш Ӏалашйина.",
        "timezonelegend": "Сахьтан аса:",
        "pageinfo-robot-index": "Магийна",
        "pageinfo-robot-noindex": "Магийна дац",
        "pageinfo-watchers": "Хьоьжучера дукхалла",
+       "pageinfo-visiting-watchers": "АгӀона тидамбеш болу а, хийцамаш гуш болу а декъашхой",
        "pageinfo-few-watchers": "{{PLURAL:$1|ТӀаьхьадогӀучерал}} $1 кӀезиг",
        "pageinfo-redirects-name": "ХӀокху агӀон тӀе йолу дӀассахьажорийн дукхалла",
        "pageinfo-subpages-name": "ХӀокху агӀона бухара агӀонаш",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|цӀе хийцар|цӀе хийцарш}}; $3 {{PLURAL:$3|гуттар хуьлург|гуттар хуьлурш}})",
-       "pageinfo-firstuser": "АгӀо кхуллург",
+       "pageinfo-firstuser": "АгӀо кхоьллинарг",
        "pageinfo-firsttime": "АгӀо кхоьллина терахь",
        "pageinfo-lastuser": "ТӀеххьара тадар дийнарг",
        "pageinfo-lasttime": "ТӀеххьара нисдар дина терахь",
        "compare-invalid-title": "Ахьа язйина йолу цӀе ца магайо.",
        "compare-title-not-exists": "Иштта цӀе яц.",
        "compare-revision-not-exists": "Иштта версеш яц.",
+       "diff-form": "Башхаллаш",
+       "diff-form-oldid": "Версин шира идентификатор (тӀехь дац)",
+       "diff-form-revid": "Башхаллаш йолу версин идентификатор",
+       "diff-form-submit": "Схьагайта башхаллаш",
        "dberr-problems": "Бехк ма бил! ХӀокху сайтехь техникан халонаш хила.",
        "dberr-again": "Хьажа карла йаккха агlо массех минот йаьлча.",
        "dberr-info": "(аьтто ца хили зӀе хӀотта серверца бухара хаамашца: $1)",
index fdcb972..60a53c0 100644 (file)
        "minutes-abbrev": "$1дакъ.",
        "hours-abbrev": "$1саат",
        "bad_image_list": "Формат бойле олмалы:\n\nЭр сатыр * ишаретинен башламалы. Сатырнынъ биринджи багълантысы къошмагъа ясакълангъан файлгъа багъланмалы.\nШу сатырда илеридеки багълантылар истисна олурлар, яни шу саифелерде ишбу файл къулланмакъ мумкюн.",
+       "variantname-crh": "Lat./Кир.",
+       "variantname-crh-latn": "Latin",
+       "variantname-crh-cyrl": "Кирил",
        "metadata": "Ресим деталлери",
        "metadata-help": "Файлда (адетиндже ракъамлы камера ве сканерлернен къошулгъан) иляве малюматы бар. Эгер бу файл яратылгъандан сонъ денъиштирильсе эди, бельки де базы параметрлер эскирди.",
        "metadata-expand": "Тафсилятны косьтер",
index 75d187f..6e04de3 100644 (file)
        "minutes-abbrev": "$1daq.",
        "hours-abbrev": "$1saat",
        "bad_image_list": "Format böyle olmalı:\n\nEr satır * işaretinen başlamalı. Satırnıñ birinci bağlantısı qoşmağa yasaqlanğan faylğa bağlanmalı.\nŞu satırda ilerideki bağlantılar istisna olur, yani şu saifelerde işbu fayl qullanmaq mümkün.",
+       "variantname-crh": "Lat./Кир.",
+       "variantname-crh-latn": "Latin",
+       "variantname-crh-cyrl": "Кирил",
        "metadata": "Resim detalleri",
        "metadata-help": "Faylda (adetince raqamlı kamera ve skanerlernen qoşulğan) ilâve malümatı bar. Eger bu fayl yaratılğandan soñ deñiştirilse edi, belki de bazı parametrler eskirdi.",
        "metadata-expand": "Tafsilâtnı köster",
index c682fc9..6f97ed6 100644 (file)
        "toc": "Obsah",
        "showtoc": "zobrazit",
        "hidetoc": "skrýt",
-       "collapsible-collapse": "Sbalit",
-       "collapsible-expand": "Rozbalit",
+       "collapsible-collapse": "sbalit",
+       "collapsible-expand": "rozbalit",
        "confirmable-confirm": "Jste si {{GENDER:$1|jist|jista|jisti}}?",
        "confirmable-yes": "Ano",
        "confirmable-no": "Ne",
        "recentchangesdays-max": "Maximálně $1 {{PLURAL:$1|den|dny|dní}}",
        "recentchangescount": "Počet implicitně zobrazovaných záznamů:",
        "prefs-help-recentchangescount": "Týká se posledních změn, historie stránek a protokolovacích záznamů.",
-       "prefs-help-watchlist-token2": "Toto je tajný klíč k webovému kanálu vašich sledovaných stránek. Kdokoli, kdo bude tento klíč znát, bude moci váš seznam sledovaných stránek číst, takže ho nešiřte.\n[[Special:ResetTokens|Kliknutím sem ho můžete reinicializovat.]]",
        "savedprefs": "Nastavení byla uložena.",
        "savedrights": "Skupiny {{GENDER:$1|uživatele|uživatelky}} $1 byly uloženy.",
        "timezonelegend": "Časové pásmo:",
index 7c3544c..ff0c83e 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Eine dazwischenliegende Version desselben Benutzers wird|$1 dazwischenliegende Versionen desselben Benutzers werden}} nicht angezeigt)",
        "diff-multi-otherusers": "({{PLURAL:$1|Eine dazwischenliegende Version|$1 dazwischenliegende Versionen}} von {{PLURAL:$2|einem anderen Benutzer|$2 Benutzern}} {{PLURAL:$1|wird|werden}} nicht angezeigt)",
        "diff-multi-manyusers": "({{PLURAL:$1|$1 dazwischenliegende Versionen}} von mehr als {{PLURAL:$2|$2 Benutzern}}, die nicht angezeigt werden)",
+       "diff-paragraph-moved-tonew": "Der Absatz wurde verschoben. Klicken, um zur neuen Stelle zu springen.",
+       "diff-paragraph-moved-toold": "Der Absatz wurde verschoben. Klicken, um zur alten Stelle zu springen.",
        "difference-missing-revision": "{{PLURAL:$2|Eine Version|$2 Versionen}} dieser Unterschiedsanzeige ($1) {{PLURAL:$2|wurde|wurden}} nicht gefunden.\n\nDieser Fehler wird normalerweise von einem veralteten Link zur Versionsgeschichte einer Seite verursacht, die zwischenzeitlich gelöscht wurde.\nEinzelheiten sind im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lösch-Logbuch] vorhanden.",
        "searchresults": "Suchergebnisse",
        "searchresults-title": "Suchergebnisse für „$1“",
        "prefs-watchlist-edits": "Maximale Anzahl der angezeigten Einträge:",
        "prefs-watchlist-edits-max": "Maximal 1.000 Einträge",
        "prefs-watchlist-token": "Token der Beobachtungsliste:",
+       "prefs-watchlist-managetokens": "Token verwalten",
        "prefs-misc": "Verschiedenes",
        "prefs-resetpass": "Passwort ändern",
        "prefs-changeemail": "E-Mail-Adresse ändern oder entfernen",
        "recentchangesdays-max": "Maximal $1 {{PLURAL:$1|Tag|Tage}}",
        "recentchangescount": "Anzahl der standardmäßig angezeigten Bearbeitungen:",
        "prefs-help-recentchangescount": "Dies umfasst die Liste der letzten Änderungen, die Versionsgeschichte und die Logbücher.",
-       "prefs-help-watchlist-token2": "Dies ist der geheime Schlüssel zum Webfeed deiner Beobachtungsliste.\nJeder, der ihn kennt, kann deine Beobachtungsliste lesen. Teile ihn deshalb nicht Anderen mit.\nSofern notwendig, [[Special:ResetTokens|kannst du ihn zurücksetzen]].",
+       "prefs-help-tokenmanagement": "Du kannst den geheimen Schlüssel für dein Benutzerkonto ansehen und zurücksetzen, der auf den Webfeed deiner Beobachtungsliste zugreifen kann. Jeder, der den Schlüssel kennt, kann deine Beobachtungsliste lesen. Deshalb teile ihn nicht anderen mit.",
        "savedprefs": "Deine Einstellungen wurden gespeichert.",
        "savedrights": "Die Benutzergruppen von {{GENDER:$1|$1}} wurden gespeichert.",
        "timezonelegend": "Zeitzone:",
index 14bf37f..7ff84c5 100644 (file)
        "recentchangesdays-max": "Tewr zaf $1 {{PLURAL:$1|roc|roci}}",
        "recentchangescount": "Halê est-amardışi ra mocnayışi rê amarê vırnayışan:",
        "prefs-help-recentchangescount": "Ney de vurnayışê peyêni, tarixê pelan u cıkewteni asenê.",
-       "prefs-help-watchlist-token2": "Na pawıtış nımnayi kılta listada şımaya.\nOke kıliti zano şeno listeya tamaşann bıvino. Poğta coy ra kesiya me hesırne. \n[[Special:ResetTokens|Na kıliti reset kerdışi re tiyay bıploğne]].",
        "savedprefs": "Tecihê şıma qeyd biy.",
        "savedrights": "{{GENDER:$1|$1}}  gruba karberi qeyd  biya.",
        "timezonelegend": "Warey saete:",
index 6dd67a9..e47877e 100644 (file)
@@ -57,7 +57,6 @@
        "underline-never": "कभैई नाई",
        "underline-default": "खोल और ब्राउजर निर्धारित",
        "editfont-style": "फन्ट प्रकार क्षेत्र सम्पादन गर:",
-       "editfont-default": "ब्राउजर पूर्वस्थिति",
        "editfont-monospace": "मोनोस्पेस्ड फन्ट",
        "editfont-sansserif": "सान्स-सेरिफ फन्ट",
        "editfont-serif": "सेरिफ फन्ट",
        "nosuchusershort": " \"$1\" नाउँ भयाः कोइलै प्रयोगकर्ता नाइथिन।\n तमरो हिज्जे जाँचः।",
        "nouserspecified": "प्रयोगकर्ता नाम दिनु अनिवार्य छ।",
        "login-userblocked": "ये प्रयोगकर्तालाई रोक लगाया छ। प्रवेश गददु अनुमति छैन।",
-       "wrongpassword": "पासवरà¥\8dड à¤\97लत à¤¹à¤¾à¤²à¤¿à¤¯à¥\8b।\nà¤\95à¥\83पया à¤\86à¤\9cà¥\80 à¤ªà¥\8dरयास à¤\97रया।",
+       "wrongpassword": "à¤\97लत à¤ªà¥\8dरयà¥\8bà¤\95à¥\8dता à¤¨à¤¾à¤\89à¤\81 à¤¯à¤¾ à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¹à¤¾à¤²à¤¿à¤¯à¥\8b।\nà¤\95à¥\83पया à¤\86à¤\81à¤\9cà¥\80 à¤ªà¥\8dरयास à¤\97रà¥\8dâ\80\8dयाà¤\83।",
        "wrongpasswordempty": "हालिएया पासवर्ड खालि थ्यो।\nकृपया आजी प्रयास गरया।",
        "passwordtooshort": "पासवर्ड कम्तिमालै {{PLURAL:$1|१ अक्षर|$1 अक्षरन}}को हुनुपडन्छ।",
        "passwordtoolong": "पासवर्ड {{PLURAL:$1|१ अक्षर|$1 अक्षरन}} है लामो हुननाइसक्दो।",
        "watchlisttools-view": "आधारित फेरबदलीहरू हेर",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|कुरडी]])",
        "specialpages": "खास पन्नाअन",
-       "specialpages-note": "* साधारण खास पानाहरू।\n* <span class=\"mw-specialpagerestricted\">निषेधित खास पानाहरू।</span>",
        "specialpages-group-changes": "अल्लैका परिवर्तन लगहरू",
        "tags": "मान्य परिवर्तन ट्यागहरू",
        "tag-filter": "[[Special:Tags|पुछड]] छानिन्या",
index 52595ad..39f54e1 100644 (file)
        "recentchangesdays-max": "Mâsim $1 {{PLURAL:$1|dé}}",
        "recentchangescount": "Nómer ed mudéfichi da fêr vèder per default:",
        "prefs-help-recentchangescount": "A gh'é dèinter al j ûltmi mudéfichi, stôri, e regéster.",
-       "prefs-help-watchlist-token2": "Còsta l'é la cêva secrēta p'r al flós web di tō tgnû 'd ôc specêl. Tót quî che la cgnòsen a sràn bòun ed lēşer i tō tgnû 'd ôc specêl, per còst an spartîrla mìa cun nisûn. [[Special:ResetTokens|Cléca ché s'ét ghê bişògn ed turnêrla impustêr]].",
        "savedprefs": "Al preferèinsi în stêdi salvêdi.",
        "timezonelegend": "Fûş urâri:",
        "localtime": "Ōra lochêla:",
index 17cdcb1..ebce1ba 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Μία ενδιάμεση αναθεώρηση|$1 ενδιάμεσες αναθεωρήσεις}} από τον ίδιο χρήστη δεν εμφανίζεται)",
        "diff-multi-otherusers": "({{PLURAL:$1|Μία ενδιάμεση έκδοση|$1 ενδιάμεσες εκδόσεις}} από {{PLURAL:$2|ένα χρήστη|$2 χρήστες}} δεν εμφανίζ{{PLURAL:$1|εται|ονται}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Μία ενδιάμεση αναθεώρηση|$1 ενδιάμεσες αναθεωρήσεις}} από περισσότερο από $2 {{PLURAL:$2|χρήστη|χρήστες}} δεν εμφανίζ{{PLURAL:$1|εται|ονται}})",
+       "diff-paragraph-moved-tonew": "Η παράγραφος αφαιρέθηκε. Κάντε κλικ για να πάτε σε νέα τοποθεσία.",
+       "diff-paragraph-moved-toold": "Η παράγραφος αφαιρέθηκε. Πατήστε στο κουμπί για να πάτε σε προηγούμενη τοποθεσία.",
        "difference-missing-revision": "{{PLURAL:$2|Μία αναθεώρηση|$2 αναθεωρήσεις}} αυτής της διαφοράς ($1) δεν {{PLURAL:$2|μπόρεσε να βρεθεί|μπόρεσαν να βρεθούν}}.\n\nΑυτό συνήθως προκαλείται από παλιό σύνδεσμο διαφοράς προς σελίδα που έχει διαγραφεί.\nΛεπτομέρειες θα βρείτε στο [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ημερολόγιο καταγραφής διαγραφών].",
        "searchresults": "Αποτελέσματα αναζήτησης",
        "searchresults-title": "Αποτελέσματα αναζήτησης για \"$1\"",
        "recentchangesdays-max": "($1 {{PLURAL:$1|ημέρα|ημέρες}} το μέγιστο)",
        "recentchangescount": "Αριθμός επεξεργασιών που να εμφανίζονται για προεπιλογή.",
        "prefs-help-recentchangescount": "Αυτό περιλαμβάνει τις πρόσφατες αλλαγές, τα ιστορικά των σελίδων, και τα αρχεία διαγραφών.",
-       "prefs-help-watchlist-token2": "Αυτό είναι το μυστικό κλειδί για την web τροφοδοσία  της λίστας παρακολούθησής σας.\nΌποιος το γνωρίζει θα είναι σε θέση να διαβάσει την λίστα παρακολούθησης σας, οπότε μην τον μοιράζεστε.\n[[Special:ResetTokens|Κάνε κλικ εδώ εάν θέλετε να τον επαναφέρετε]].",
        "savedprefs": "Οι προτιμήσεις σας έχουν αποθηκευτεί.",
        "savedrights": "Οι ομάδες χρηστών {{GENDER:$1|του $1|της $1}} έχουν αποθηκευτεί.",
        "timezonelegend": "Ζώνη ώρας:",
index 5083bed..1fecca0 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by the same user not shown)",
        "diff-multi-otherusers": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by {{PLURAL:$2|one other user|$2 users}} not shown)",
        "diff-multi-manyusers": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by more than $2 {{PLURAL:$2|user|users}} not shown)",
+       "diff-paragraph-moved-tonew": "Paragraph was moved. Click to jump to new location.",
+       "diff-paragraph-moved-toold": "Paragraph was moved. Click to jump to old location.",
        "difference-missing-revision": "{{PLURAL:$2|One revision|$2 revisions}} of this difference ($1) {{PLURAL:$2|was|were}} not found.\n\nThis is usually caused by following an outdated diff link to a page that has been deleted.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "search-summary": "",
        "searchresults": "Search results",
        "prefs-watchlist-edits": "Maximum number of changes to show in watchlist:",
        "prefs-watchlist-edits-max": "Maximum number: 1000",
        "prefs-watchlist-token": "Watchlist token:",
+       "prefs-watchlist-managetokens": "Manage tokens",
        "prefs-misc": "Misc",
        "prefs-resetpass": "Change password",
        "prefs-changeemail": "Change or remove email address",
        "recentchangesdays-max": "Maximum $1 {{PLURAL:$1|day|days}}",
        "recentchangescount": "Number of edits to show by default:",
        "prefs-help-recentchangescount": "This includes recent changes, page histories, and logs.",
-       "prefs-help-watchlist-token2": "This is the secret key to the web feed of your watchlist.\nAnyone who knows it will be able to read your watchlist, so do not share it.\nIf you need to, [[Special:ResetTokens|you can reset it]].",
+       "prefs-help-tokenmanagement": "You can see and reset the secret key for your account that can access the Web feed of your watchlist. Anyone who knows the key will be able to read your watchlist, so do not share it.",
        "savedprefs": "Your preferences have been saved.",
        "savedrights": "The user groups of {{GENDER:$1|$1}} have been saved.",
        "timezonelegend": "Time zone:",
        "variantname-uz": "uz",
        "variantname-uz-latn": "uz-Latn",
        "variantname-uz-cyrl": "uz-Cyrl",
+       "variantname-crh": "crh",
+       "variantname-crh-latn": "crh-Latn",
+       "variantname-crh-cyrl": "crh-Cyrl",
        "metadata": "Metadata",
        "metadata-help": "This file contains additional information, probably added from the digital camera or scanner used to create or digitize it.\nIf the file has been modified from its original state, some details may not fully reflect the modified file.",
        "metadata-expand": "Show extended details",
index 74c2317..508d480 100644 (file)
        "recentchangesdays-max": "(maksimume $1 {{PLURAL:$1|tago|tagoj}})",
        "recentchangescount": "Nombro de redaktoj por montri defaŭlte:",
        "prefs-help-recentchangescount": "Ĉi tiu inkluzivas lastajn ŝanĝojn, paĝajn historiojn, kaj protokolojn.",
-       "prefs-help-watchlist-token2": "Tio estas la sekreta ŝlosilo al la retfluo de via atentaro.\nĈiu, kiu konas ĝin, povas legi vian atentaron. Do, ne kunhavigu ĝin.\nSe vi devas, [[Special:ResetTokens|vi povas rekomencigi ĝin]].",
        "savedprefs": "Viaj preferoj estas konservitaj.",
        "savedrights": "La uzanto-grupoj de {{GENDER:$1|$1}} estis konservitaj.",
        "timezonelegend": "Horzono:",
index 7fafe29..9fc962d 100644 (file)
        "nosuchusershort": "Ez dago \"$1\" izena duen erabiltzailerik. Egiaztatu ongi idatzi duzula.",
        "nouserspecified": "Erabiltzaile izena zehaztu beharra daukazu.",
        "login-userblocked": "Erabiltzailea blokeatua dago. Ezin du saioa hasi.",
-       "wrongpassword": "Pasahitza ez da zuzena. Saiatu berriz.",
+       "wrongpassword": "Erabiltzailea edo pasahitza txarto sartu egin da. Saiatu berriz.",
        "wrongpasswordempty": "Pasahitza hutsik dago. Saiatu berriz.",
        "passwordtooshort": "Pasahitzek {{PLURAL:$1|karaktere 1|$1 karaktere}} gutxienez eduki behar dituzte.",
        "passwordtoolong": "Pasahitzak ezin dira {{PLURAL:$1|karaktere bat|$1 karaktere}} baino luzeagoak izan.",
        "diff-multi-sameuser": "(Erabiltzaile berdinaren {{PLURAL:$1|erdiko ekarpen bat ez da|$1 erdiko ekarpen ez dira}} erakusten)",
        "diff-multi-otherusers": "({{PLURAL:$1|Tarteko berrikusketa bat|$1 tarteko berrikusketak}}  {{PLURAL:$2|beste erabiltzaile bat|$2 erabiltzaileak}} egina ez da erakusten)",
        "diff-multi-manyusers": "({{PLURAL:$1|Tarteko berrikusketa bat|$1 tarteko berrikusketak}} by more than $2 {{PLURAL:$2|erabiltzaile batek|erabiltzaile batzuek}} baino gehiagok egina ez erakutsia)",
+       "diff-paragraph-moved-tonew": "Paragrafoa mugitu egin da. Egin klik beste kokaleku batera salto egiteko.",
        "difference-missing-revision": " ($1) ezberdinatasunaren  {{PLURAL:$2|Berrikusketa bat|$2 berrikusketa}} ez {{PLURAL:$2|da|dira}} aurkitu.\n\nHau, orokorrean ezabatu egin den orri batera deskonektatua dagoen esteka desegonkor baten ondorioz gertatzen da.\n\nHemen xehetasunak aurki daitezke: [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "searchresults": "Bilaketaren emaitzak",
        "searchresults-title": "«$1» bilaketaren  emaitzak",
        "recentchangesdays-max": "(gehienez {{PLURAL:$1|egun bat|$1 egun}})",
        "recentchangescount": "Erakusteko aldaketa kopurua, lehenetsita:",
        "prefs-help-recentchangescount": "Honek azken aldaketak, orrialdeen historiak eta logak barne-biltzen ditu.",
-       "prefs-help-watchlist-token2": "Hau da zure jarraipen zerrendako web jarioaren giltza sekretua.\nEzagutzen duen orok zure jarraipen zerrenda irakurtzeko aukera izango du, ez partekatu.\n[[Special:ResetTokens|Klik egin hemen berrezarri behar baduzu]]",
        "savedprefs": "Zure hobespenak gorde egin dira.",
        "savedrights": "{{GENDER:$1|$1}} erabiltzailearen taldeak gorde dira.",
        "timezonelegend": "Ordu-eremua:",
index ee86a94..427d3c2 100644 (file)
        "recentchangesdays-max": "Enintään $1 {{PLURAL:$1|päivä|päivää}}",
        "recentchangescount": "Näytettävien muutoksien määrä oletuksena",
        "prefs-help-recentchangescount": "Tämä sisältää tuoreet muutokset, muutoshistoriat ja lokit.",
-       "prefs-help-watchlist-token2": "Tämä on salainen avain tarkkailulistasi verkkosyötteeseen.\nKuka tahansa, joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä.\n[[Special:ResetTokens|Napsauta tästä, jos sinun pitää uudistaa se]].",
        "savedprefs": "Asetuksesi on tallennettu.",
        "savedrights": "Käyttäjän {{GENDER:$1|$1}} käyttäjäryhmät on tallennettu.",
        "timezonelegend": "Aikavyöhyke",
        "rollback-success": "Käyttäjän {{GENDER:$3|$1}} tekemät muokkaukset kumottiin ja sivu palautettiin käyttäjän {{GENDER:$4|$2}} versioon.",
        "rollback-success-notify": "Kumottiin käyttäjän $1 muokkaukset; palautettiin viimeiseen käyttäjän $2 versioon. [$3 Näytä muutokset]",
        "sessionfailure-title": "Istuntovirhe",
-       "sessionfailure": "Istuntosi kanssa on ongelma. Muutosta ei toteutettu varotoimena istuntokaappauksien takia. Käytä selaimen paluutoimintoa ja päivitä sivu, jolta tulit, ja yritä uudelleen.",
+       "sessionfailure": "Näyttää siltä, että tämänhetkisessä istunnossasi on jokin ongelma. \nTämä toiminto on peruutettu varotoimena, jotta estetään istunnon kaappaaminen.\nMene aikaisemmalle sivulle ja päivitä se. Yritä sitten uudelleen.",
        "changecontentmodel": "Muuta sivun sisältömallia",
        "changecontentmodel-legend": "Muuta sisältömallia",
        "changecontentmodel-title-label": "Sivun otsikko",
index 8892be0..30bf8c8 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Une révision intermédiaire par le même utilisateur non affichée|$1 révisions intermédiaires par le même utilisateur non affichées}})",
        "diff-multi-otherusers": "({{PLURAL:$1|Une révision intermédiaire|$1 révisions intermédiaires}} par {{PLURAL:$2|un autre utilisateur|$2 utilisateurs}} non {{PLURAL:$1|affichée|affichées}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Une révision intermédiaire|$1 révisions intermédiaires}} par plus {{PLURAL:$2|d'un utilisateur|de $2 utilisateurs}} {{PLURAL:$1|est masquée|sont masquées}})",
+       "diff-paragraph-moved-tonew": "Le paragraphe a été déplacé. Cliquez pour accéder au nouvel emplacement.",
+       "diff-paragraph-moved-toold": "Le paragraphe a été déplacé. Cliquez pour accéder à l'ancien emplacement.",
        "difference-missing-revision": "{{PLURAL:$2|Une révision|$2 révisions}} de cette différence ($1) {{PLURAL:$2|n’a pas été trouvée|n’ont pas été trouvées}}.\n\nCela survient en général en suivant un lien de différence désuet vers une page qui a été supprimée.\nVous pouvez trouver des détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "searchresults": "Résultats de la recherche",
        "searchresults-title": "Résultats de recherche pour « $1 »",
        "recentchangesdays-max": "(maximum $1 jour{{PLURAL:$1||s}})",
        "recentchangescount": "Nombre de modifications à afficher par défaut :",
        "prefs-help-recentchangescount": "Ceci inclut les modifications récentes, les pages d'historiques et les journaux.",
-       "prefs-help-watchlist-token2": "Voici la clé secrète du flux Web de votre liste de suivi.\nToute personne la connaissant pourra lire votre liste de suivi, ne la communiquez donc pas.\n[[Special:ResetTokens|Cliquez ici si vous devez la réinitialiser]].",
        "savedprefs": "Les préférences ont été sauvegardées.",
        "savedrights": "Les droits utilisateur de {{GENDER:$1|$1}} ont été enregistrés.",
        "timezonelegend": "Fuseau horaire :",
index 319dc4d..9abd8ef 100644 (file)
        "tooltip-ca-edit": "આ પાનામાં ફેરફાર કરો",
        "tooltip-ca-addsection": "નવો વિભાગ ઉમેરો",
        "tooltip-ca-viewsource": "આ પાનુ સુરક્ષિત છે.\nતમે તેનો સ્રોત જોઇ શકો છો",
-       "tooltip-ca-history": "àª\86 àªªàª¾àª¨àª¾àª¨àª¾àª\82 àª\85àª\97ાàª\89નાàª\82 àª«à«\87રફારà«\8b",
+       "tooltip-ca-history": "àª\86 àªªàª¾àª¨àª¾àª¨à«\80 àª\85àª\97ાàª\89નà«\80 àª\86વà«\83તà«\8dતિàª\93",
        "tooltip-ca-protect": "આ પાનું સુરક્ષિત કરો",
        "tooltip-ca-unprotect": "આ પાનું રક્ષણ બદલો",
        "tooltip-ca-delete": "આ પાનું હટાવો",
index 963dab0..6349228 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|גרסת ביניים אחת|$1 גרסאות ביניים}} של אותו משתמש {{PLURAL:$1|אינה מוצגת|אינן מוצגות}})",
        "diff-multi-otherusers": "({{PLURAL:$1|גרסת ביניים אחת|$1 גרסאות ביניים}} של {{PLURAL:$2|משתמש אחר אחד|$2 משתמשים}} {{PLURAL:$1|אינה מוצגת|אינן מוצגות}})",
        "diff-multi-manyusers": "({{PLURAL:$1|גרסת ביניים אחת|$1 גרסאות ביניים}} של יותר {{PLURAL:$2|ממשתמש אחד|מ־$2 משתמשים}} {{PLURAL:$1|אינה מוצגת|אינן מוצגות}})",
+       "diff-paragraph-moved-tonew": "הפיסקה הועברה. ניתן ללחוץ כאן כדי לעבור למיקומה החדש.",
+       "diff-paragraph-moved-toold": "הפיסקה הועברה. ניתן ללחוץ כאן כדי לעבור למיקומה הישן.",
        "difference-missing-revision": "{{PLURAL:$2|גרסה אחת|$2 גרסאות}} מתוך הגרסאות שביקשת להשוות ($1) {{PLURAL:$2|לא נמצאה|לא נמצאו}}.\n\nזה נגרם בדרך־כלל עקב לחיצה על קישור ישן להבדלים בין גרסאות של דף שנמחק.\nאפשר למצוא פרטים ב[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} יומן המחיקות].",
        "searchresults": "תוצאות החיפוש",
        "searchresults-title": "תוצאות החיפוש \"$1\"",
        "prefs-watchlist-edits": "המספר המרבי של העריכות שמוצגות ברשימת המעקב:",
        "prefs-watchlist-edits-max": "לכל היותר: 1,000",
        "prefs-watchlist-token": "אסימון לרשימת המעקב:",
+       "prefs-watchlist-managetokens": "ניהול אסימונים",
        "prefs-misc": "שונות",
        "prefs-resetpass": "שינוי סיסמה",
        "prefs-changeemail": "שינוי או הסרת כתובת דוא\"ל",
        "recentchangesdays-max": "לכל היותר {{PLURAL:$1|יום אחד|יומיים|$1 ימים}}",
        "recentchangescount": "מספר העריכות שמוצגות כברירת מחדל:",
        "prefs-help-recentchangescount": "ההעדפה הזאת כוללת את דף השינויים האחרונים, דפי היסטוריית גרסאות ויומנים.",
-       "prefs-help-watchlist-token2": "זהו המפתח הסודי להזנה של רשימת המעקב שלך.\nכל מי שיודע אותו יכול לקרוא את רשימת המעקב שלך, לכן אין לשתף אותו.\nבמקרה הצורך, אפשר [[Special:ResetTokens|לאפס את האסימון]].",
+       "prefs-help-tokenmanagement": "באפשרותך לצפות במפתח הסודי לחשבונך, שמאפשר גישה ל־Feed האינטרנטי של רשימת המעקב שלך, ולאפס אותו. כל מי שיודע את המפתח יכול לקרוא את רשימת המעקב שלך, לכן אין לשתף אותו.",
        "savedprefs": "ההעדפות שלך נשמרו.",
        "savedrights": "קבוצות {{GENDER:$1|המשתמש|המשתמשת}} של \"$1\" נשמרו.",
        "timezonelegend": "אזור זמן:",
index 696cdf8..faf24ef 100644 (file)
        "recentchangesdays-max": "(maksimalno $1 {{PLURAL:$1|dan|dana}})",
        "recentchangescount": "Zadani broj izmjena koje se prikazuju:",
        "prefs-help-recentchangescount": "Ovo uključuje nedavne promjene, stare izmjene, i evidencije.",
-       "prefs-help-watchlist-token2": "Ovo je tajni ključ prema sažetku Vašeg popisa praćenja. Svaki suradnik kojem je poznat, moći će čitati Vaš popis praćenih stranica. Ne dijelite ga ni s kim. [[Special:ResetTokens|Kliknite ovdje ako ga želite ponovo postaviti]].",
        "savedprefs": "Vaše postavke su sačuvane.",
        "savedrights": "Suradnička su prava {{GENDER:$1|suradnika $1|suradnice $1}} spremljena.",
        "timezonelegend": "Vremenska zona:",
index a615909..a218140 100644 (file)
        "recentchangesdays-max": "($1 {{PLURAL:$1|օրից|օրից}} ոչ ավել)",
        "recentchangescount": "Խմբագրումների թիվը ըստ լռության.",
        "prefs-help-recentchangescount": "Ներառում է վերջին փոփոխությունները, էջերի պատմությունը և տեղեկամատյանները։",
-       "prefs-help-watchlist-token2": "Սա գաղտնի բանալի է հսկականկի օգնույամբ նորություն ստանալու համար:\nՈվ որ գիտի այն կարող է կարդալ ձեր հսկացանկը, ուստի մի տարածեք այն:\nԵթե ձեզ պետք է զրոյացնել հսկացանկի կտրոնը, [[Special:ResetTokens| սեղմեք այստեղ]]:",
        "savedprefs": "Ձեր նախընտրությունները հիշված են։",
        "timezonelegend": "Ժամային գոտի.",
        "localtime": "Տեղական ժամանակ.",
        "metadata": "Մետատվյալներ",
        "metadata-help": "Նիշքը պարունակում է ընդարձակ տվյալները, հավանաբար ավելացված թվային լուսանկարչական ապարատի կամ սկաների կողմից, որոնք օգտագործվել են նկարը ստեղծելու կամ թվայնացնելու համար։\nԵթե նիշքը ձևափոխվել է ստեղծումից ի վեր, ապա որոշ տվյալները կարող են չհամապատասխանել ձևափոխված նիշքին։",
        "metadata-expand": "Ցուց տալ ընդարձակ տվյալները",
-       "metadata-collapse": "Ô¹Õ¡Ö\84Ö\81Õ¶Õ¥Õ¬ Õ¨Õ¶Õ¤Õ¡Ö\80Õ±Õ¡Õ¯ Õ¿Õ¾ÕµÕ¬Õ¡ները",
+       "metadata-collapse": "Ô¹Õ¡Ö\84Ö\81Õ¶Õ¥Õ¬ Õ¬Ö\80Õ¡Ö\81Õ¸Ö\82Ö\81Õ«Õ¹ Õ¿Õ¾ÕµÕ¡Õ¬ները",
        "metadata-fields": "EXIF մետատվյալների այն դաշտերը, որոնք նշված ենք այս ուղերձի մեջ, կցուցադրվեն պատկերի էջում, երբ մետատվյալների աղյուսակը ծալված է։ Այլ տվյալները լռությամբ կթաքցվեն։\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "Լայնք",
        "exif-imagelength": "Բարձրություն",
index cb8d5ec..b3174e5 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Un version intermedie|$1 versiones intermedie}} facite per le mesme usator non es monstrate)",
        "diff-multi-otherusers": "({{PLURAL:$1|Un version intermedie|$1 versiones intermedie}} facite per {{PLURAL:$2|un altere usator|$2 usatores}} non es monstrate)",
        "diff-multi-manyusers": "({{PLURAL:$1|Un version intermedie|$1 versiones intermedie}} facite per plus de $2 {{PLURAL:$2|usator|usatores}} non es monstrate)",
+       "diff-paragraph-moved-tonew": "Le paragrapho ha essite displaciate. Clicca pro saltar al nove position.",
        "difference-missing-revision": "{{PLURAL:$2|Un version|$2 versiones}} de iste differentia ($1) non ha essite trovate.\n\nIsto es generalmente causate per sequer un ligamine de diff obsolete a un pagina que ha essite delite.\nDetalios se trova in le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de deletiones].",
        "searchresults": "Resultatos del recerca",
        "searchresults-title": "Resultatos del recerca de \"$1\"",
        "prefs-watchlist-edits": "Numero maxime de modificationes a monstrar in le observatorio:",
        "prefs-watchlist-edits-max": "Numero maxime: 1000",
        "prefs-watchlist-token": "Indicio pro le observatorio:",
+       "prefs-watchlist-managetokens": "Gerer indicios",
        "prefs-misc": "Misc",
        "prefs-resetpass": "Cambiar contrasigno",
        "prefs-changeemail": "Cambiar o remover adresse de e-mail",
        "recentchangesdays-max": "(non plus de $1 {{PLURAL:$1|die|dies}})",
        "recentchangescount": "Numero de modificationes a monstrar per predefinition:",
        "prefs-help-recentchangescount": "Isto include modificationes recente, historias de paginas, e registros.",
-       "prefs-help-watchlist-token2": "Isto es le clave secrete pro le syndication web de tu observatorio.\nOmne persona qui lo cognosce pote leger tu observatorio, dunque, non divide lo.\n[[Special:ResetTokens|Clicca hic pro reinitialisar lo]].",
+       "prefs-help-tokenmanagement": "Tu pote vider e reinitialisar le clave secrete pro tu conto que pote acceder al aggregator Web de tu observatorio. Tote persona que cognosce le clave potera leger tu observatorio, dunque non divulga lo.",
        "savedprefs": "Tu preferentias ha essite confirmate.",
        "savedrights": "Le gruppos de usator de {{GENDER:$1|$1}} ha essite salveguardate.",
        "timezonelegend": "Fuso horari:",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
        "rcfilters-exclude-button-off": "Excluder le selection",
        "rcfilters-exclude-button-on": "Selection excludite",
-       "rcfilters-view-advanced-filters-label": "Filtros avantiate",
        "rcfilters-view-tags": "Modificationes con etiquettas",
        "rcfilters-view-namespaces-tooltip": "Filtrar le resultatos per spatio de nomines",
        "rcfilters-view-tags-tooltip": "Filtrar le resultatos usante etiquettas de version",
index f7860e6..9175588 100644 (file)
@@ -56,7 +56,8 @@
                        "Rachmat04",
                        "Arifpedia",
                        "Uchup19",
-                       "Archd"
+                       "Archd",
+                       "Empu"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "recentchangesdays-max": "(maksimum $1 {{PLURAL:$1|hari|hari}})",
        "recentchangescount": "Standar jumlah suntingan yang ditampilkan:",
        "prefs-help-recentchangescount": "Opsi ini berlaku untuk perubahan terbaru, versi terdahulu halaman, dan log.",
-       "prefs-help-watchlist-token2": "Ini adalah kunci rahasia (token) ke umpan web dari daftar pantauan Anda.\nSiapa saja yang tahu akan dapat melihat daftar pantauan Anda, jadi jangan dibagikan. Jika diperlukan\n[[Special:ResetTokens|Anda dapat mengatur ulang kunci tersebut]].",
        "savedprefs": "Preferensi Anda telah disimpan",
        "savedrights": "Kelompok hak pengguna {{GENDER:$1|$1}} telah disimpan.",
        "timezonelegend": "Zona waktu:",
        "recentchanges-summary": "Temukan perubahan terbaru dalam wiki di halaman ini.<br />\n;Keterangan:(<span style=\"color:blue;\">beda</span>) perubahan, (<span style=\"color:blue;\">versi</span>) sejarah revisi, '''B''' halaman baru, '''b''' suntingan bot, '''k''' suntingan kecil, <span class=\"unpatrolled\">!</span> perubahan belum dipatroli,<br /><span style=\"color:green;\">'''(+ ''bita'')'''</span> isi konten bertambah, <span style=\"color:red;\">(- ''bita'')</span> isi konten berkurang, (← Ringkasan otomatis), (→ <span style=\"color:grey;\">Suntingan bagian</span>)",
        "recentchanges-noresult": "Tidak ada perubahan dalam rentang waktu ini yang cocok dengan kriteria.",
        "recentchanges-timeout": "Waktu pencarian ini telah habis. Anda mungkin ingin mencoba parameter pencarian lain.",
+       "recentchanges-network": "Selama terjadi kesalahan teknis, tidak ada hasil yang bisa dimuat. Silakan coba untuk menyegarkan kembali halaman.",
        "recentchanges-feed-description": "Temukan perubahan terbaru dalam wiki di umpan ini.",
        "recentchanges-label-newpage": "Suntingan ini membuat halaman baru",
        "recentchanges-label-minor": "Ini adalah suntingan kecil",
index 14f84c0..45945ec 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Una versione intermedia|$1 versioni intermedie}} di uno stesso utente non {{PLURAL:$1|è mostrata|sono mostrate}})",
        "diff-multi-otherusers": "({{PLURAL:$1|Una versione intermedia|$1 versioni intermedie}} di {{PLURAL:$2|un altro utente|$2 utenti}} non mostrate)",
        "diff-multi-manyusers": "({{PLURAL:$1|Una versione intermedia|$1 versioni intermedie}} di oltre $2 {{PLURAL:$2|utente|utenti}} non {{PLURAL:$1|mostrata|mostrate}})",
+       "diff-paragraph-moved-tonew": "Il paragrafo è stato spostato. Clicca per passare alla nuova posizione.",
+       "diff-paragraph-moved-toold": "Il paragrafo è stato spostato. Clicca per passare alla vecchia posizione.",
        "difference-missing-revision": "{{PLURAL:$2|Una versione|$2 versioni}} di questa differenza ($1) {{PLURAL:$2|non è stata trovata|non sono state trovate}}.\n\nQuesto si verifica solitamente seguendo un collegamento obsoleto di un diff a una pagina cancellata.\nI dettagli possono essere trovati nel [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro delle cancellazioni].",
        "searchresults": "Risultati della ricerca",
        "searchresults-title": "Risultati della ricerca di \"$1\"",
        "prefs-watchlist-edits": "Numero massimo di modifiche da mostrare negli osservati speciali:",
        "prefs-watchlist-edits-max": "Numero massimo: 1000",
        "prefs-watchlist-token": "Token osservati speciali:",
+       "prefs-watchlist-managetokens": "Gestisci token",
        "prefs-misc": "Varie",
        "prefs-resetpass": "Cambia password",
        "prefs-changeemail": "Modifica o rimuovi indirizzo di posta elettronica",
        "recentchangesdays-max": "(massimo $1 {{PLURAL:$1|giorno|giorni}})",
        "recentchangescount": "Numero di modifiche da mostrare per default:",
        "prefs-help-recentchangescount": "Comprende ultime modifiche, cronologie e registri.",
-       "prefs-help-watchlist-token2": "Questa è la chiave segreta per il feed web dei tuoi osservati speciali.\nChiunque la conosce sarà in grado di leggere i tuoi osservati speciali, per cui non condividerla. [[Special:ResetTokens|Clicca qui se hai bisogno di reimpostarla]].",
+       "prefs-help-tokenmanagement": "Puoi visualizzare e reimpostare la chiave segreta per la tua utenza con cui puoi accedere al feed web dei tuoi osservati speciali. Chiunque conosce la chiave sarà in grado di leggere i tuoi osservati speciali, quindi non condividerla.",
        "savedprefs": "Le preferenze sono state salvate.",
        "savedrights": "I gruppi utente di {{GENDER:$1|$1}} sono stati salvati.",
        "timezonelegend": "Fuso orario:",
index 5141158..311a989 100644 (file)
        "nosuchusershort": "「$1」という名前の利用者は存在しません。\n綴りを確認してください。",
        "nouserspecified": "利用者名を指定してください。",
        "login-userblocked": "この利用者はブロックされています。ログインは拒否されます。",
-       "wrongpassword": "パスワードが間違っています。 \nもう一度やり直してください。",
+       "wrongpassword": "利用者名またはパスワードが間違っています。 \nもう一度やり直してください。",
        "wrongpasswordempty": "パスワードを空欄にはできません。\nもう一度やり直してください。",
        "passwordtooshort": "パスワードは {{PLURAL:$1|$1 文字}}以上にしてください。",
        "passwordtoolong": "パスワードは {{PLURAL:$1|$1 文字}}以下にしてください。",
        "recentchangesdays-max": "(最大 $1 {{PLURAL:$1|日|日間}})",
        "recentchangescount": "既定で表示する件数:",
        "prefs-help-recentchangescount": "この設定は最近の更新、ページの履歴、および記録に適用されます。",
-       "prefs-help-watchlist-token2": "これはあなたのウォッチリスト フィードの秘密のコードです。\nこのトークンを知っている人は誰でもあなたのウォッチリストを読めてしまうため、他の人に教えないでください。\n[[Special:ResetTokens|トークンを再設定する必要がある場合はここをクリックしてください]]。",
        "savedprefs": "個人設定を保存しました。",
        "savedrights": "{{GENDER:$1|$1}}の利用者グループが保存されました。",
        "timezonelegend": "タイムゾーン:",
index 18aaa92..587bd3c 100644 (file)
        "recentchangesdays-max": "(maksimum $1 {{PLURAL:$1|dina|dina}})",
        "recentchangescount": "Cacahing besutan sing dituduhaké kanthi baku:",
        "prefs-help-recentchangescount": "Iki klebu owah-owahan pungkasan, kaca sajarah, lan log.",
-       "prefs-help-watchlist-token2": "Ini adalah kunci rahasia (token) ke web feed dari daftar pantauan Anda.\nSiapa saja yang tahu akan dapat melihat daftar pantauan Anda, jadi jangan dibagikan.\n[[Special:ResetTokens|Klik di sini jika Anda perlu menyetel ulang]].",
        "savedprefs": "Prèferènsi Panjenengan wis disimpen",
        "savedrights": "Golongan panganggo {{GENDER:$1|$1}} wis disimpen.",
        "timezonelegend": "Zona wektu:",
index 1d5f64f..765c418 100644 (file)
        "diff-multi-sameuser": "(같은 사용자의 {{PLURAL:$1|중간 판 하나|중간 판 $1개}}는 보이지 않습니다)",
        "diff-multi-otherusers": "({{PLURAL:$2|다른 사용자 한 명|사용자 $2명}}의 {{PLURAL:$1|중간 판 하나|중간 판 $1개}}는 보이지 않습니다)",
        "diff-multi-manyusers": "({{PLURAL:$2|사용자}} $2명 이상의 {{PLURAL:$1|중간 판 하나|중간 판 $1개}}는 보이지 않습니다)",
+       "diff-paragraph-moved-tonew": "문단이 이동되었습니다. 새로운 위치로 이동하려면 클릭하십시오.",
+       "diff-paragraph-moved-toold": "문단이 이동되었습니다. 오래된 위치로 이동하려면 클릭하십시오.",
        "difference-missing-revision": "문서 비교에서 {{PLURAL:$2|하나|$2개}}의 판($1)을 찾을 수 {{PLURAL:$2|없습니다}}.\n\n이 문제는 주로 삭제된 문서를 가리키는 오래된 문서 비교 링크로 인해 발생합니다.\n자세한 내용은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 확인할 수 있습니다.",
        "searchresults": "검색 결과",
        "searchresults-title": "\"$1\"에 대한 검색 결과",
        "recentchangesdays-max": "최대 $1{{PLURAL:$1|일}}",
        "recentchangescount": "기본으로 보여줄 편집 수:",
        "prefs-help-recentchangescount": "이 설정은 최근 바뀜, 문서 역사와 기록에 적용됩니다.",
-       "prefs-help-watchlist-token2": "내 주시문서 목록의 웹 피드의 비밀 키입니다.\n이 키를 알고 있는 사람은 내 주시문서 목록을 읽을 수 있으니 이 키를 공유하지 마세요.\n필요하다면 [[Special:ResetTokens|이 키를 재설정할 수 있습니다]].",
        "savedprefs": "설정을 저장했습니다.",
        "savedrights": "{{GENDER:$1|$1}}의 사용자 그룹이 저장되었습니다.",
        "timezonelegend": "시간대:",
index 0da01f8..10b75ba 100644 (file)
        "nosuchusershort": "De Benotzernumm \"$1\" gëtt et net.\nKuckt w.e.g. op d'Schreifweis richteg ass.",
        "nouserspecified": "Gitt w.e.g. e Benotzernumm un.",
        "login-userblocked": "Dëse Benotzer ass gespaart. Aloggen ass net erlaabt.",
-       "wrongpassword": "Dir hutt e falscht (oder kee) Passwuert aginn. Probéiert w.e.g. nach eng Kéier.",
+       "wrongpassword": "De Benotzernumm oder d'Passwuert si falsch.\nProbéiert w.e.g. nach eng Kéier.",
        "wrongpasswordempty": "D'Passwuert dat Dir aginn hutt war eidel.\nProbéiert w.e.g. nach eng Kéier.",
        "passwordtooshort": "Passwierder musse mindestens {{PLURAL:$1|1 Zeeche|$1 Zeeche}} laang sinn.",
        "passwordtoolong": "Passwierder kënnen net méi laang wéi {{PLURAL:$1|1 Zeeche|$1 Zeeche}} sinn.",
        "recentchangesdays-max": "(Maximal $1 {{PLURAL:$1|Dag|Deeg}})",
        "recentchangescount": "Zuel vun den Ännerungen déi als Standard gewise ginn:",
        "prefs-help-recentchangescount": "Inklusiv Rezent Ännerungen, Versiounshistoriquen a Logbicher.",
-       "prefs-help-watchlist-token2": "Dëst ass de geheime Schlëssel fir de Webfeed vun Ärer Iwwerwaachungslëscht. Jiddwereen deen e kennt kann Är Iwwerwaachungslëscht liesen, dofir sollt Dir en net weider ginn. [[Special:ResetTokens|Klickt hei wann Dir en zrécksetze musst]].",
        "savedprefs": "Är Astellunge goufe gespäichert.",
        "savedrights": "D'Benotzergruppe vum {{GENDER:$1|$1}} goufe gespäichert.",
        "timezonelegend": "Zäitzon:",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Standardfilter uleeën",
        "rcfilters-savedqueries-cancel-label": "Ofbriechen",
        "rcfilters-savedqueries-add-new-title": "Aktuell Filter-Astellunge späicheren",
-       "rcfilters-savedqueries-already-saved": "Dës Filtere si scho gespäichert",
+       "rcfilters-savedqueries-already-saved": "Dës Filtere si scho gespäichert. Ännert Är Astellunge fir en neie Gespäicherte Filter unzeleeën.",
        "rcfilters-restore-default-filters": "Standardfiltere restauréieren",
        "rcfilters-clear-all-filters": "All Filteren eidelmaachen",
        "rcfilters-show-new-changes": "Rezentst Ännerunge weisen",
        "rcfilters-filterlist-feedbacklink": "Sot eis wat Dir vun dësen (neien) Filterméiglechkeeten haalt",
        "rcfilters-highlightbutton-title": "Resultater ervirhiewen",
        "rcfilters-highlightmenu-title": "Eng Faarf eraussichen",
+       "rcfilters-highlightmenu-help": "Sicht eng Faarf eraus fir dës Eegenschaft ervirzehiewen.",
        "rcfilters-filterlist-noresults": "Keng Filtere fonnt",
        "rcfilters-noresults-conflict": "Näischt fonnt well d'Sichcritère sech widderspriechen",
        "rcfilters-filter-editsbyself-label": "Ännerunge vun Iech",
index 09d616d..a159b2f 100644 (file)
        "recentchangesdays-max": "Ne vairāk kā $1 {{PLURAL:$1|dienas|diena|dienas}}",
        "recentchangescount": "Izmaiņu skaits, kuru rāda pēc noklusējuma:",
        "prefs-help-recentchangescount": "Šis parametrs attiecas uz pēdējo izmaiņu un hronoloģijas lapām, kā arī uz sistēmas žurnāliem",
-       "prefs-help-watchlist-token2": "Šī ir slepena atslēga tavam uzraugāmo lapu sarakstam.\nIkvienam, kas to zinās, būs iespēja apskatīt tavu uzraugāmo lapu sarakstu, tāpēc nedalies ar to.\n[[Special:ResetTokens|Spied šeit, lai to atjaunotu]].",
        "savedprefs": "Jūsu izvēles ir saglabātas.",
        "timezonelegend": "Laika josla:",
        "localtime": "Vietējais laiks:",
        "filehist-comment": "Komentārs",
        "imagelinks": "Faila lietojums",
        "linkstoimage": "{{PLURAL:$1|Šajās $1 lapās ir saites|Šajā lapā ir saite|Šajās $1 lapās ir saites}} uz šo failu:",
+       "linkstoimage-more": "Uz šo failu ir saites vairāk nekā $1 {{PLURAL:$1|lapās|lapā|lapās}}.\nŠajā sarakstā ir tikai {{PLURAL:$1|pirmās $1 saistītās lapas|pirmā $1 saistītā lapa|pirmās $1 saistītās lapas}} uz šo failu.\nPieejams arī [[Special:WhatLinksHere/$2|pilns saraksts]].",
        "nolinkstoimage": "Nevienā lapā nav norāžu uz šo attēlu.",
        "morelinkstoimage": "Skatīt [[Special:WhatLinksHere/$1|vairāk saites]] uz šo failu.",
        "linkstoimage-redirect": "$1 (faila pāradresācija) $2",
        "unwatchthispage": "Pārtraukt uzraudzīšanu",
        "notanarticle": "Nav satura lapa",
        "notvisiblerev": "Cita lietotāja pēdējā versija ir izdzēsta",
-       "watchlist-details": "(Tu uzraugi $1 {{PLURAL:$1|lapu|lapas}}, neieskaitot diskusiju lapas.)",
+       "watchlist-details": "Tu uzraugi $1 {{PLURAL:$1|lapas|lapu|lapas}} (neieskaitot diskusiju lapas).",
        "wlheader-enotif": "E-pasta paziņojumi ir ieslēgti.",
        "wlheader-showupdated": "Lapas, kas ir tikušas izmainītas, kopš pēdējoreiz skatījies tās, tiek rādītas <strong>trekninātā</strong> rakstā.",
        "wlshowlast": "Rādīt pēdējās $1 stundas $2 dienas",
index 84b95f6..36c0cd0 100644 (file)
@@ -26,7 +26,7 @@
                        "逆襲的天邪鬼"
                ]
        },
-       "tog-underline": "鏈墊線:",
+       "tog-underline": "以底線識鏈接:",
        "tog-hideminor": "隱近校",
        "tog-hidepatrolled": "隱近巡",
        "tog-newpageshidepatrolled": "隱新巡",
@@ -48,7 +48,7 @@
        "tog-previewonfirst": "覽首修",
        "tog-enotifwatchlistpages": "哨新,遣函",
        "tog-enotifusertalkpages": "議新,遣函",
-       "tog-enotifminoredits": "æ ¡æ\96°ï¼\8cé\81£å\87½",
+       "tog-enotifminoredits": "æ¯\8fæ\9c\89ä¿®è¨\82ï¼\8cé\9b\96é\9d\9eé\87\8dè¦\81ä¹\8b屬ï¼\8c亦é\81£é\83µ",
        "tog-enotifrevealaddr": "列余址於書內",
        "tog-shownumberswatching": "放哨有",
        "tog-oldsig": "覽原署名:",
        "tog-watchlisthideown": "不哨己文",
        "tog-watchlisthidebots": "不哨僕文",
        "tog-watchlisthideminor": "不哨細纂",
-       "tog-watchlisthideliu": "不哨有簿",
-       "tog-watchlisthideanons": "不無簿",
-       "tog-watchlisthidepatrolled": "不哨已巡",
-       "tog-watchlisthidecategorization": "隱頁類",
-       "tog-ccmeonemails": "凡所遺書,請存副本。",
-       "tog-diffonly": "異下無示頁",
-       "tog-showhiddencats": "示隱類",
+       "tog-watchlisthideliu": "不監在簿",
+       "tog-watchlisthideanons": "不無簿",
+       "tog-watchlisthidepatrolled": "不監既審",
+       "tog-watchlisthidecategorization": "不示類屬",
+       "tog-ccmeonemails": "凡所遺書,亦謄錄之,存於郵篋。",
+       "tog-diffonly": "方校讎時,下端無庸具示原文",
+       "tog-showhiddencats": "隱類悉示",
        "tog-norollbackdiff": "轉後略異",
        "tog-useeditwarning": "離而未存,示吾",
        "tog-prefershttps": "入簿復用安全鏈",
        "versionrequiredtext": "惠置$1媒維基,見[[Special:Version|版]]。",
        "ok": "可",
        "retrievedfrom": "取自\"$1\"",
-       "youhavenewmessages": "有$1書至子書房也。($2)",
+       "youhavenewmessages": "{{PLURAL:$3|新接}} $1($2)。",
        "youhavenewmessagesfromusers": "子有 $1 自 {{PLURAL:$3|一簿戶也|$3 簿戶也}} ($2)。",
-       "youhavenewmessagesmanyusers": "子有 $1 自多簿戶 ( $2 )",
-       "newmessageslinkplural": "{{PLURAL:$1|一新訊|999=新訊}}",
-       "newmessagesdifflinkplural": "新",
+       "youhavenewmessagesmanyusers": "諸士音信新來。其$1,悉存乎齋($2)。",
+       "newmessageslinkplural": "{{PLURAL:$1|書簡凡一|999=二三書簡}}",
+       "newmessagesdifflinkplural": "新{{PLURAL:$1|易}}",
        "youhavenewmessagesmulti": "新訊於$1",
        "editsection": "纂",
        "editold": "纂",
        "editingcomment": "撰$1",
        "editconflict": "纂沖$1",
        "explainconflict": "子纂與他人沖,上者時也,下者子也,望子合之。\n註,'''$1'''上文儲焉",
-       "yourtext": "å­\90ä¹\9f",
+       "yourtext": "å\90\9bæ\89\80æ\92°è¿°",
        "storedversion": "時也",
        "editingold": "'''\"警示\"子纂舊然。強儲之,則新易失焉。'''",
        "yourdiff": "異",
index 2186a3b..aa348f5 100644 (file)
@@ -30,7 +30,7 @@
        "tog-watchuploads": "Sogna file anyar sing nyong unggah nang daptar sawangane nyong",
        "tog-watchrollback": "Tambahna kaca sing wis tek rollback maring daftar pangawasane inyong",
        "tog-minordefault": "Otomatis nandani kabeh suntingan dadi suntingan cilik",
-       "tog-previewontop": "Tidokna pratayang sedurunge kotak sunting",
+       "tog-previewontop": "Tidhokna pratayang sedurunge kotak sunting",
        "tog-previewonfirst": "Tidokna pratayang nang suntingan sing pertama",
        "tog-enotifwatchlistpages": "Kirimna imel maring inyong angger kaca awa berkas utsing mlebu daptar pangawasanne inyong diowaih",
        "tog-enotifusertalkpages": "Kirimna imel maring inyong angger kaca dhiskusine inyong owah",
        "createacct-yourpasswordagain-ph": "Lebokna tembung sandhi maning",
        "userlogin-remembermypassword": "Jorna ben Inyong tetep mlebu log",
        "userlogin-signwithsecure": "Gunakna koneksi aman",
+       "cannotlogin-title": "Ora bisa mlebu log",
+       "cannotlogin-text": "Ora mungkin mlebu log.",
        "cannotloginnow-title": "Ora teyeng mlebu siki",
+       "cannotcreateaccount-title": "Ora bisa gawé akun",
        "yourdomainname": "Domain Rika:",
        "password-change-forbidden": "Rika ora teyeng ngowaih tembung sandhi nang wiki kiye.",
        "externaldberror": "Ana kesalahan otentikasi basis data utawa Rika ora olih nglakokna pemutakhiran maring akun eksternale Rika.",
        "user-mail-no-body": "Njajal ngirim imel sing kosong urawa isine sithik thok.",
        "changepassword": "Ganti tembung sandhi",
        "resetpass_announce": "Kanggo ngrampungna gole mlebu log, rika kudu nglebokna tembung sandhi anyar.",
+       "resetpass_text": "<!-- Tambah teks neng kéné -->",
        "resetpass_header": "Ganti tembung sandhine akun",
        "oldpassword": "Tembung sandi lawas:",
        "newpassword": "Tembung sandi anyar:",
        "resetpass_submit": "Nata tembung sandhi lan mlebu log",
        "changepassword-success": "Sandhiné Rika uwis diganti!",
        "changepassword-throttled": "Rika wis kakehan gole njajal mlebu log.\nTulung ngenteni $1 sedurunge njajal maning.",
+       "botpasswords-label-create": "Gawé",
+       "botpasswords-label-update": "Nganyari",
+       "botpasswords-label-cancel": "Batalna",
+       "botpasswords-label-delete": "Busek",
        "resetpass_forbidden": "Tembung sandhi ora teyeng diganti",
        "resetpass-no-info": "Rika kudu mlebu log kanggo ngakses kaca kiye sacara langsung.",
        "resetpass-submit-loggedin": "Ganti tembung sandhi",
        "headline_tip": "Subbagian tingkat 1",
        "nowiki_sample": "Lebokna teks sing ora bakal diformat nang kene",
        "nowiki_tip": "Aja nganggo format wiki",
+       "image_sample": "Conto.jpg",
        "image_tip": "Ngaweh berkas",
+       "media_sample": "Conto.ogg",
        "media_tip": "Pranala berkas media",
        "sig_tip": "Tapak astane Rika nganggo tandha wektu",
        "hr_tip": "Garis horisontal",
        "minoredit": "Kiye suntingan cilik",
        "watchthis": "Awasi kaca kiyé",
        "savearticle": "Terbitna Kaca",
+       "publishpage": "Pacak kacané",
        "preview": "Pra tayang",
        "showpreview": "Pra tayang",
        "showdiff": "Ndeleng bedané",
        "editingsection": "Nyunting $1 (bagiyan)",
        "editingcomment": "Nyunting $1 (bagéyan anyar)",
        "editconflict": "Konflik panyuntingan: $1",
+       "yourtext": "Teks-é rika",
        "protectedpagewarning": "'''Pénget:  Kaca kiye wis dikunci dadi mung panganggo sing nduwé hak aksès pangurus baé sing teyeng nyunting.'''\nEntri cathetan pungkasan disadiakna nang ngisor kanggo referensi:",
        "semiprotectedpagewarning": "'''Cathetan:''' Kaca kiye lagi pinuju direksa, dadi mung panganggo kadaftar sing teyeng nyunting.\nEntri cathetan pungkasan disadiakna nang ngisor kanggo referensi:",
        "templatesused": "{{PLURAL:$1|Cithakan|Cithakan}} sing dienggo nang kaca kiye:",
        "rcshowhideliu": "$1 panganggo sing mlebu log",
        "rcshowhideanons": "$1 panganggo anonim",
        "rcshowhidepatr": "$1 suntingan sing dipatroli",
-       "rcshowhidemine": "$1 suntingane inyong",
+       "rcshowhidemine": "$1 suntingané inyong",
        "rclinks": "Tidokna $1 owahan pungkasan nang $2 dina pungkasan kiye",
        "diff": "bédane",
        "hist": "versi",
index da240ab..257235b 100644 (file)
        "nosuchusershort": "Нема корисник со името „$1“.\nПроверете дали правилно сте напишале.",
        "nouserspecified": "Мора да наведете корисничко име.",
        "login-userblocked": "Овој корисник е блокиран. Најавувањето не е дозволено.",
-       "wrongpassword": "Ð\92неÑ\81овÑ\82е Ð¿Ð¾Ð³Ñ\80еÑ\88на Ð»Ð¾Ð·Ð¸Ð½ÐºÐ°. Обидете се повторно.",
+       "wrongpassword": "Ð\92неÑ\81овÑ\82е Ð¿Ð¾Ð³Ñ\80еÑ\88но ÐºÐ¾Ñ\80иÑ\81ниÑ\87ко Ð¸Ð¼Ðµ Ð¸Ð»Ð¸ Ð»Ð¾Ð·Ð¸Ð½ÐºÐ°.\nОбидете се повторно.",
        "wrongpasswordempty": "Внесената лозинка е празна. Обидете се повторно.",
        "passwordtooshort": "Лозинката мора да има најмалку {{PLURAL:$1|1 знак|$1 знаци}}.",
        "passwordtoolong": "Лозинката не треба да има повеќе од {{PLURAL:$1|1 знак|$1 знаци}}.",
        "diff-multi-sameuser": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка|Не се прикажани $1 меѓувремени преработки}} од истиот корисник)",
        "diff-multi-otherusers": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка|Не се прикажани $1 меѓувремени преработки}} од {{PLURAL:$2|еден друг корисник|$2 корисници}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка направена|Не се прикажани $1 меѓувремени преработки направени}} од повеќе од $2 {{PLURAL:$2|корисник|корисници}})",
+       "diff-paragraph-moved-tonew": "Пасусот е преместен. Стиснете за да прејдете на новото место.",
+       "diff-paragraph-moved-toold": "Пасусот е преместен. Стиснете за да прејдете на старото место.",
        "difference-missing-revision": "Не пронајдов {{PLURAL:$2|една преработка|$2 преработки}} од оваа разлика ($1).\n\nОва обично се должи на застарена врска за разлики што води кон избришана страница.\nПовеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].",
        "searchresults": "Исход од пребарувањето",
        "searchresults-title": "Исход од пребарувањето на „$1“",
        "recentchangesdays-max": "(највеќе {{PLURAL:$1|еден ден|$1 дена}})",
        "recentchangescount": "Број на уредувања кои ќе се прикажуваат по основно:",
        "prefs-help-recentchangescount": "Подразбира скорешни промени, истории на страници и дневници.",
-       "prefs-help-watchlist-token2": "Ова е тајна шифра за вашиот канализиран список на набљудувања.\nСекој што ја знае ќе може да ја чита, па затоа ви препорачуваме да не ја кажувате никому.\n[[Special:ResetTokens|Стиснете тука ако треба да зададете нова]].",
        "savedprefs": "Вашите нагодувања се зачувани.",
        "savedrights": "Корисничките групи на {{GENDER:$1|$1}} се зачувани.",
        "timezonelegend": "Часовен појас:",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Создај стандарден филтер",
        "rcfilters-savedqueries-cancel-label": "Откажи",
        "rcfilters-savedqueries-add-new-title": "Зачувај тековни филтерски поставки",
-       "rcfilters-savedqueries-already-saved": "Овие филтри се веќе зачувани",
+       "rcfilters-savedqueries-already-saved": "Овие филтри се веќе зачувани. Сменете ги поставките за да направите нов зачуван филтер.",
        "rcfilters-restore-default-filters": "Поврати основни филтри",
        "rcfilters-clear-all-filters": "Тргни ги сите филтри",
        "rcfilters-show-new-changes": "Погл. најнови промени",
index e7527c6..f72ad63 100644 (file)
        "rcfilters-filter-excluded": "ချန်လှပ်",
        "rcfilters-exclude-button-off": "ရွေးချယ်ထားသည်များ ချန်လှပ်ရန်",
        "rcfilters-exclude-button-on": "ရွေးချယ်ထားသည်များ ချန်လှပ်နေသည်",
-       "rcfilters-view-advanced-filters-label": "အဆင့်မြင့် filter များ",
        "rcfilters-view-tags": "စာတွဲမှတ်ထားသော တည်းဖြတ်မှုများ",
        "rcfilters-view-namespaces-tooltip": "အမည်ညွှန်းအလိုက် ရလဒ်များ စစ်ထုတ်ရန်",
        "rcfilters-view-tags-tooltip": "တည်းဖြတ်စာတွဲများ အသုံးပြု၍ ရလဒ်များ စစ်ထုတ်ရန်",
        "dellogpage": "ဖျက်ထားသည်များ မှတ်တမ်း",
        "dellogpagetext": "အောက်ပါတို့သည် မကြာမီက ဖျက်ထားမှုများ စာရင်း ဖြစ်သည်။",
        "deletionlog": "ဖျက်ပစ်သည့်မှတ်တမ်း",
+       "reverted": "ယခင်တည်းဖြတ်မူသို့ နောက်ပြန်ပြင်ပြီးပြီ",
        "deletecomment": "အ​ကြောင်း​ပြ​ချက် -",
        "deleteotherreason": "အခြားသော/နောက်ထပ် အကြောင်းပြချက် -",
        "deletereasonotherlist": "အခြား အကြောင်းပြချက်",
index 7687415..ff70276 100644 (file)
        "nosuchusershort": "Det finnes ingen bruker ved navn «$1». Kontroller stavemåten.",
        "nouserspecified": "Du må oppgi et brukernavn.",
        "login-userblocked": "Brukeren er blokkert. Innlogging er ikke tillatt.",
-       "wrongpassword": "Du har oppgitt et ugyldig passord. Prøv igjen.",
+       "wrongpassword": "Galt brukernavn eller passord oppgitt.\nPrøv igjen.",
        "wrongpasswordempty": "Du oppga ikke noe passord. Prøv igjen.",
        "passwordtooshort": "Passord må ha minst {{PLURAL:$1|ett tegn|$1 tegn}}.",
        "passwordtoolong": "Passord kan ikke overskride {{PLURAL:$1|1 character|$1 characters}}.",
        "diff-multi-sameuser": "({{PLURAL:$1|Én mellomliggende revisjon|$1 mellomliggende revisjoner}} av samme bruker vises ikke)",
        "diff-multi-otherusers": "({{PLURAL:$1|En mellomliggende revisjon|$1 mellomliggende revisjoner}} av {{PLURAL:$2|en annen bruker|$2 brukere}} er ikke vist)",
        "diff-multi-manyusers": "({{PLURAL:$1|Én mellomrevisjon|$1 mellomrevisjoner}} av mer enn $2 {{PLURAL:$2|bruker|brukere}} vises ikke)",
+       "diff-paragraph-moved-tonew": "Avsnittet ble flyttet. Klikk for å hoppe til den nye plasseringen.",
+       "diff-paragraph-moved-toold": "Avsnittet ble flyttet. Klikk for å hoppe til den gamle plasseringen.",
        "difference-missing-revision": "{{PLURAL:$2|En revisjon|$2 revisjoner}} av denne forskjellen ($1) {{PLURAL:$2|ble|ble}} ikke funnet.\n\nDette skyldes som regel at en gammel forskjell-lenke er fulgt til en side som er slettet.\nDetaljer kan finnes i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} sletteloggen].",
        "searchresults": "Søkeresultater",
        "searchresults-title": "Søkeresultater for «$1»",
        "prefs-watchlist-edits": "Maksimalt antall redigeringer som skal vises i overvåkningslisten:",
        "prefs-watchlist-edits-max": "Maksimalt antall: 1000",
        "prefs-watchlist-token": "Nøkkel for overvåkningsliste",
+       "prefs-watchlist-managetokens": "Behandle nøkler",
        "prefs-misc": "Diverse",
        "prefs-resetpass": "Endre passord",
        "prefs-changeemail": "Endre eller fjerne e-postadresse",
        "recentchangesdays-max": "Maks $1 {{PLURAL:$1|dag|dager}}",
        "recentchangescount": "Antall redigeringer som skal vises som standard:",
        "prefs-help-recentchangescount": "Dette inkluderer nylige endringer, sidehistorikk og logger.",
-       "prefs-help-watchlist-token2": "Dette er den hemmelige nøkkelen til webmatingen for din overvåkningsliste.\nEnhver som kjenner nøkkelen vil kunne lese din overvåkningsliste, så ikke vis den til andre.\n[[Special:ResetTokens|Klikk her om du trenger å nullstille nøkkelen]].",
+       "prefs-help-tokenmanagement": "Du kan se og resette den hemmelige nøkkelen for kontoen din som kan få tilgang til matingen med overvåkningslisten din. Alle som har nøkkelen vil kunne lese overvåkningslisten din, så ikke del den.",
        "savedprefs": "Innstillingene ble lagret.",
        "savedrights": "Brukergruppene til {{GENDER:$1|$1}} har blitt lagret.",
        "timezonelegend": "Tidssone:",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Opprett standardfilter",
        "rcfilters-savedqueries-cancel-label": "Avbryt",
        "rcfilters-savedqueries-add-new-title": "Lagre de gjeldende filterinnstillingene",
-       "rcfilters-savedqueries-already-saved": "Disse filtrene er allerede lagret",
+       "rcfilters-savedqueries-already-saved": "Disse filtrene er allerede lagret. Endre innstillingene dine for å opprette et nytt lagret filter.",
        "rcfilters-restore-default-filters": "Gjenopprett standardfiltre",
        "rcfilters-clear-all-filters": "Nullstill alle filtre",
        "rcfilters-show-new-changes": "Vis de nyeste endringene",
index ed13a7b..710619d 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Een tussenliggende versie|$1 tussenliggende versies}} door dezelfde gebruiker niet weergegeven)",
        "diff-multi-otherusers": "({{PLURAL:$1|Een tussenliggende versie|$1 tussenliggende versies}} door {{PLURAL:$2|een andere gebruiker|$2 gebruikers}} niet weergegeven)",
        "diff-multi-manyusers": "($1 tussenliggende {{PLURAL:$1|versie|versies}} door meer dan $2 {{PLURAL:$2|gebruiker|gebruikers}}  worden niet weergegeven)",
+       "diff-paragraph-moved-tonew": "Deze paragraaf is verplaatst. Klik om naar de nieuwe locatie te springen.",
+       "diff-paragraph-moved-toold": "Deze paragraaf is verplaatst. Klik om naar de oude locatie te springen.",
        "difference-missing-revision": "{{PLURAL:$2|Eén versie|$2 versies}} van deze verschillen ($1) {{PLURAL:$2|is|zijn}} niet aangetroffen.\n\nDit wordt meestal veroorzaakt door het volgen van een verouderde koppeling verschillen voor een pagina die is verwijderd.\nMeer gegevens zijn mogelijk te vinden in het [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} verwijderingslogboek].",
        "searchresults": "Zoekresultaten",
        "searchresults-title": "Zoekresultaten voor \"$1\"",
        "prefs-watchlist-edits": "Maximaal aantal bewerkingen in de volglijst:",
        "prefs-watchlist-edits-max": "Maximale aantal: 1000",
        "prefs-watchlist-token": "Volglijstsleutel:",
+       "prefs-watchlist-managetokens": "Token beheren",
        "prefs-misc": "Diversen",
        "prefs-resetpass": "Wachtwoord wijzigen",
        "prefs-changeemail": "E-mailadres wijzigen of verwijderen",
        "recentchangesdays-max": "(maximaal $1 {{PLURAL:$1|dag|dagen}})",
        "recentchangescount": "Standaard aantal weer te geven bewerkingen:",
        "prefs-help-recentchangescount": "Dit geldt voor recente wijzigingen, paginageschiedenis en logboekpagina's.",
-       "prefs-help-watchlist-token2": "Dit is de geheime sleutel voor de webfeed van uw volglijst.\nIedereen die het token kent, kan uw volglijst bekijken, dus deel dit token niet.\nU kunt de [[Special:ResetTokens|tokens opnieuw instellen]] als u dat wilt.",
+       "prefs-help-tokenmanagement": "U kunt uw geheime sleutel voor uw account bekijken en resetten. De geheime sleutel biedt toegang tot de webfeed van uw volglijst. Iedereen die het token kent, kan uw volglijst bekijken, dus deel dit token niet.",
        "savedprefs": "Uw voorkeuren zijn opgeslagen.",
        "savedrights": "De gebruikergroepen van {{GENDER:$1|$1}} zijn opgeslagen.",
        "timezonelegend": "Tijdzone:",
index 754e21e..f5e8446 100644 (file)
        "recentchangesdays-max": "(maksymalnie $1 {{PLURAL:$1|dzień|dni}})",
        "recentchangescount": "Domyślna liczba wyświetlanych edycji:",
        "prefs-help-recentchangescount": "Uwzględnia ostatnie zmiany, historię stron i rejestry.",
-       "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli chcesz go zresetować]].",
        "savedprefs": "Twoje preferencje zostały zapisane.",
        "savedrights": "Zapisano grupy {{GENDER:$1|użytkownika $1|użytkowniczki $1}}.",
        "timezonelegend": "Strefa czasowa:",
index 9f04f32..657b912 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|Uma revisão intermediária|$1 revisões intermediárias}} pelo mesmo usuário não {{PLURAL:$1|está sendo mostrada|estão sendo mostradas}})",
        "diff-multi-otherusers": "({{PLURAL:$1|Uma revisão intermediária por {{PLURAL:$2|um outro usuário|$2 usuários}} não está sendo mostrada|$1 revisões intermediárias por {{PLURAL:$2|um outro usuário|$2 usuários}} não estão sendo mostradas}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Uma edição intermediária|$1 edições intermediárias}} de mais de {{PLURAL:$2|um usuário|$2 usuários}} não {{PLURAL:$1|está sendo mostrada|estão sendo mostradas}})",
+       "diff-paragraph-moved-tonew": "O parágrafo foi movido. Clique para saltar para a nova posição.",
+       "diff-paragraph-moved-toold": "O parágrafo foi movido. Clique para saltar para a posição anterior.",
        "difference-missing-revision": "{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.\n\nIsto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].",
        "searchresults": "Resultados da pesquisa",
        "searchresults-title": "Resultados da pesquisa por \"$1\"",
        "prefs-watchlist-edits": "Número máximo de alterações para mostrar na lista de observação:",
        "prefs-watchlist-edits-max": "Número máximo: 1000",
        "prefs-watchlist-token": "Senha para a lista de páginas vigiadas:",
+       "prefs-watchlist-managetokens": "Gerenciar chaves",
        "prefs-misc": "Diversos",
        "prefs-resetpass": "Alterar senha",
        "prefs-changeemail": "Alterar ou remover endereço de email",
        "recentchangesdays-max": "(máximo: $1 {{PLURAL:$1|dia|dias}})",
        "recentchangescount": "Número de edições a serem exibidas por padrão:",
        "prefs-help-recentchangescount": "Isto inclui mudanças recentes, histórico de páginas e registros.",
-       "prefs-help-watchlist-token2": "Esta é a senha secreta para o feed da Web com sua lista de tokens vigiados.\nQualquer pessoa que descobrir esta senha será capaz de ler sua lista, então não a compartilhe.\n[[Special:ResetTokens|Clique aqui para redefini-la]].",
+       "prefs-help-tokenmanagement": "Você pode ver e redefinir a chave secreta para sua conta que pode acessar o feed da Web da sua lista de vigilância. Qualquer pessoa que conheça a chave poderá ler sua lista de observação, então não compartilhe.",
        "savedprefs": "As suas preferências foram salvas.",
        "savedrights": "Os grupos {{GENDER:$1|do usuário|da usuária}} $1 foram gravados.",
        "timezonelegend": "Fuso horário:",
        "rcfilters-filterlist-title": "Filtros",
        "rcfilters-filterlist-whatsthis": "Como funcionam estes?",
        "rcfilters-filterlist-feedbacklink": "Diga-nos o que você pensa sobre estas (novas) ferramentas de filtragem",
-       "rcfilters-highlightbutton-title": "Realçar os resultados",
+       "rcfilters-highlightbutton-title": "Destacar resultados",
        "rcfilters-highlightmenu-title": "Selecione uma cor",
        "rcfilters-highlightmenu-help": "Selecione uma cor para realçar esta propriedade",
        "rcfilters-filterlist-noresults": "Nenhum filtro encontrado",
        "rcfilters-filtergroup-automated": "Contribuições automatizadas",
        "rcfilters-filter-bots-label": "Robô",
        "rcfilters-filter-bots-description": "Edições feitas por ferramentas automatizadas.",
-       "rcfilters-filter-humans-label": "Humano (não bot)",
+       "rcfilters-filter-humans-label": "Humano (não robô)",
        "rcfilters-filter-humans-description": "Edições feitas por editores humanos.",
        "rcfilters-filtergroup-reviewstatus": "Estado da revisão",
        "rcfilters-filter-patrolled-label": "Patrulhado",
index 2861e8f..5259dc8 100644 (file)
        "delete-hook-aborted": "A eliminação foi cancelada por um \"hook\".\nNão foi dada nenhuma explicação.",
        "no-null-revision": "Não foi possível criar uma nova revisão nula para a página \"$1\"",
        "badtitle": "Título inválido",
-       "badtitletext": "O título de página solicitado era inválido, vazio, ou um link interlínguas ou interwikis incorrecto.\nTalvez contenha um ou mais caracteres que não podem ser usados em títulos.",
-       "title-invalid-empty": "O título da página solicitada está vazio ou contém apenas o nome de um domínio.",
-       "title-invalid-utf8": "O título da página solicitada contém uma sequência UTF-8 inválida.",
-       "title-invalid-interwiki": "O título da página solicitada contém uma ligação interlíngua que não pode ser utilizada em títulos.",
-       "title-invalid-talk-namespace": "O título da página solicitada refere-se a uma página de discussão que não existe.",
-       "title-invalid-characters": "O título da página solicitada contém carateres inválidos: \"$1\".",
-       "title-invalid-relative": "O título contém um caminho relativo. Os títulos relativos (./, ../) são inválidos porque estarão inacessíveis muitas vezes ao serem carregados pelo navegador do utilizador.",
-       "title-invalid-magic-tilde": "O título da página solicitada contém uma sequência de tis inválida (<nowiki>~~~</nowiki>).",
-       "title-invalid-too-long": "O título da página solicitada é demasiado longo. Não deverá ser maior que $1 {{PLURAL:$1|byte|bytes}} na codificação UTF-8.",
-       "title-invalid-leading-colon": "O título da página solicitada contém um erro de pontuação (:) no início.",
+       "badtitletext": "O título de página solicitado era inválido, estava vazio, ou era uma hiperligação interlínguas ou interwikis incorreta.\nTalvez contenha um ou mais caracteres que não podem ser usados em títulos.",
+       "title-invalid-empty": "O título de página solicitado está vazio ou contém apenas o nome de um domínio.",
+       "title-invalid-utf8": "O título de página solicitado contém uma sequência UTF-8 inválida.",
+       "title-invalid-interwiki": "O título de página solicitado contém uma ligação interwikis que não pode ser utilizada em títulos.",
+       "title-invalid-talk-namespace": "O título de página solicitado refere-se a uma página de discussão que não pode existir.",
+       "title-invalid-characters": "O título de página solicitado contém carateres inválidos: \"$1\".",
+       "title-invalid-relative": "O título contém um caminho relativo. Os títulos relativos (./, ../) são inválidos porque normalmente são inacessíveis quando tratados pelo navegador do utilizador.",
+       "title-invalid-magic-tilde": "O título de página solicitado contém uma sequência de tis inválida (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "O título de página solicitado é demasiado longo. Não pode exceder $1 {{PLURAL:$1|byte|bytes}} em codificação UTF-8.",
+       "title-invalid-leading-colon": "O título de página solicitado contém um sinal de dois pontos (:) inválido no início.",
        "perfcached": "Os seguintes dados encontram-se armazenados na ''cache'' e podem não estar atualizados. {{PLURAL:$1|Está disponível na ''cache'' um máximo de um resultado|Estão disponíveis na ''cache'' um máximo de $1 resultados}}.",
        "perfcachedts": "Os seguintes dados encontram-se armazenados na ''cache'' e foram atualizados pela última vez a $1. {{PLURAL:$4|Está disponível na ''cache'' um máximo de um resultado|Estão disponíveis na ''cache'' um máximo de $4 resultados}}.",
        "querypage-no-updates": "As atualizações estão presentemente desativadas para esta página.\nPor enquanto, os dados aqui presentes não poderão ser atualizados.",
        "nosuchusershort": "Não existe nenhum utilizador com o nome \"$1\".\nVerifique o nome que introduziu.",
        "nouserspecified": "Tem de especificar um nome de utilizador.",
        "login-userblocked": "Este utilizador está bloqueado. Não é permitido o acesso.",
-       "wrongpassword": "A palavra-passe que introduziu é inválida. Tente novamente, por favor.",
+       "wrongpassword": "O nome de utilizador ou a palavra-passe inseridos estão incorretos.\nTente novamente, por favor.",
        "wrongpasswordempty": "A palavra-passe não foi introduzida. \nIntroduza-a, por favor.",
        "passwordtooshort": "A palavra-passe deve ter no mínimo $1 {{PLURAL:$1|carácter|caracteres}}.",
        "passwordtoolong": "A palavra-passe não pode exceder $1 {{PLURAL:$1|carácter|caracteres}}.",
        "sectioneditnotsupported-title": "Edição de secções não suportada",
        "sectioneditnotsupported-text": "A edição de secções não é suportada nesta página.",
        "permissionserrors": "Erro de permissão",
-       "permissionserrorstext": "Não possui permissão para fazer isso, {{PLURAL:$1|pelo seguinte motivo|pelos seguintes motivos}}:",
+       "permissionserrorstext": "Não tem permissão para fazer isso, {{PLURAL:$1|pelo seguinte motivo|pelos seguintes motivos}}:",
        "permissionserrorstext-withaction": "Não tem permissão para $2, {{PLURAL:$1|pelo seguinte motivo|pelos seguintes motivos}}:",
        "contentmodelediterror": "Não pode editar esta revisão porque o modelo de conteúdo é <code>$1</code>, que é diferente do modelo atual da página <code>$2</code>.",
        "recreate-moveddeleted-warn": "<strong>Aviso: Está a recriar uma página anteriormente eliminada.</strong>\n\nVerifique se é apropriado continuar a editar esta página.\nPara sua conveniência, é apresentado abaixo o registo de eliminação e movimentação da página:",
        "diff-multi-sameuser": "(Há {{PLURAL:$1|uma edição intermédia do mesmo utilizador que não está a ser apresentada|$1 edições intermédias do mesmo utilizador que não estão a ser apresentadas}})",
        "diff-multi-otherusers": "(Há {{PLURAL:$1|uma revisão intermédia|$1 revisões intermédias}} de {{PLURAL:$2|outro utilizador|$2 utilizadores}} que não {{PLURAL:$1|está a ser apresentada|estão a ser apresentadas}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de mais de {{PLURAL:$2|um utilizador|$2 utilizadores}} não {{PLURAL:$1|apresentada|apresentadas}})",
+       "diff-paragraph-moved-tonew": "O parágrafo foi movido. Clique para saltar para a nova posição.",
+       "diff-paragraph-moved-toold": "O parágrafo foi movido. Clique para saltar para a posição anterior.",
        "difference-missing-revision": "{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.\n\nIsto é geralmente causado por seguir uma ligação de histórico desatualizada para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminação].",
        "searchresults": "Resultados da pesquisa",
        "searchresults-title": "Resultados da pesquisa de \"$1\"",
        "prefs-watchlist-edits": "Número máximo de edições a mostrar na lista de vigiadas:",
        "prefs-watchlist-edits-max": "Máximo: 1000",
        "prefs-watchlist-token": "Chave secreta da lista de páginas vigiadas:",
+       "prefs-watchlist-managetokens": "Gerir chaves",
        "prefs-misc": "Diversos",
        "prefs-resetpass": "Alterar palavra-passe",
        "prefs-changeemail": "Alterar ou remover correio eletrónico",
        "recentchangesdays-max": "Máximo: $1 {{PLURAL:$1|dia|dias}}",
        "recentchangescount": "Número de edições a apresentar por omissão:",
        "prefs-help-recentchangescount": "Inclui mudanças recentes, histórico de páginas e registos.",
-       "prefs-help-watchlist-token2": "Esta é a chave secreta para o ''feed'' RSS da sua lista de páginas vigiadas.\nQualquer pessoa que conheça a chave será capaz de ler a sua lista de páginas vigiadas, por isso não a divulgue.\n[[Special:ResetTokens|Clique aqui para redefini-la]].",
+       "prefs-help-tokenmanagement": "Pode ver e repor a chave secreta da sua conta que permite aceder ao feed da sua lista de páginas vigiadas. Qualquer pessoa que conheça a chave será capaz de ler a sua lista de páginas vigiadas, por isso não a partilhe.",
        "savedprefs": "As suas preferências foram gravadas.",
        "savedrights": "Os grupos {{GENDER:$1|do utilizador|da utilizadora}} $1 foram gravados.",
        "timezonelegend": "Fuso horário:",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Criar filtro padrão",
        "rcfilters-savedqueries-cancel-label": "Cancelar",
        "rcfilters-savedqueries-add-new-title": "Gravar as configurações de filtros atuais",
-       "rcfilters-savedqueries-already-saved": "Estes filtros já foram gravados",
+       "rcfilters-savedqueries-already-saved": "Estes filtros já foram gravados. Altera as suas configurações para criar um novo filtro gravado.",
        "rcfilters-restore-default-filters": "Restaurar os filtros padrão",
        "rcfilters-clear-all-filters": "Limpar todos os filtros",
        "rcfilters-show-new-changes": "Mostrar as mudanças mais recentes",
        "upload-dialog-title": "Carregar ficheiro",
        "upload-dialog-button-cancel": "Cancelar",
        "upload-dialog-button-back": "Voltar",
-       "upload-dialog-button-done": "Feito",
+       "upload-dialog-button-done": "Concluído",
        "upload-dialog-button-save": "Gravar",
        "upload-dialog-button-upload": "Carregar",
        "upload-form-label-infoform-title": "Detalhes",
        "upload-form-label-own-work-message-generic-local": "Confirmo que estou a carregar este ficheiro segundo as condições de serviço e política de licenças da wiki {{SITENAME}}.",
        "upload-form-label-not-own-work-message-generic-local": "Se não pode carregar este ficheiro de acordo com as normas da wiki {{SITENAME}}, por favor feche esta janela e tente outro método.",
        "upload-form-label-not-own-work-local-generic-local": "Poderá querer experimentar [[Special:Upload|a página padrão de carregamento]].",
-       "upload-form-label-own-work-message-generic-foreign": "Entendo que estou a carregar este ficheiro em um repositório partilhado. Confirmo que estou a fazê-lo cumprindo com os termos de serviço e com as políticas de licenciamento.",
-       "upload-form-label-not-own-work-message-generic-foreign": "Se não é capaz de carregar este ficheiro sob as políticas do repositório partilhado, por favor feche esta janela e tente outro método.",
-       "upload-form-label-not-own-work-local-generic-foreign": "Pode querer tentar utilizar [[Special:Upload|a página de carregamento na wiki {{SITENAME}}]], se este ficheiro puder ser carregado de acordo com as normas da wiki.",
+       "upload-form-label-own-work-message-generic-foreign": "Compreendo que estou a carregar este ficheiro num repositório partilhado. Confirmo que estou a fazê-lo cumprindo as respetivas condições de serviço e normas de licenciamento.",
+       "upload-form-label-not-own-work-message-generic-foreign": "Se não pode carregar este ficheiro cumprindo as normas do repositório partilhado, feche esta janela e tente outro método, por favor.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Também pode tentar usar [[Special:Upload|a página de carregamento da wiki {{SITENAME}}]], se este ficheiro puder ser carregado lá de acordo com as respetivas normas.",
        "backend-fail-stream": "Não foi possível transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Não foi possível fazer cópia de segurança do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro $1 não existe.",
-       "backend-fail-hashes": "Não foi possível obter os hashes do ficheiro para comparação.",
+       "backend-fail-hashes": "Não foi possível obter os resumos criptográficos dos ficheiros para comparação.",
        "backend-fail-notsame": "Já existe um ficheiro não idêntico em \"$1\" .",
        "backend-fail-invalidpath": "\"$1\" não é um caminho de armazenamento válido.",
        "backend-fail-delete": "Não foi possível eliminar o ficheiro \"$1\".",
-       "backend-fail-describe": "Não foi possível mudar metadados para o ficheiro \"$1\".",
+       "backend-fail-describe": "Não foi possível alterar os metadados do ficheiro \"$1\".",
        "backend-fail-alreadyexists": "O ficheiro \"$1\" já existe.",
        "backend-fail-store": "Não foi possível armazenar o ficheiro \"$1\" em \"$2\".",
        "backend-fail-copy": "Não foi possível copiar o ficheiro \"$1\" para \"$2\".",
        "img-auth-nologinnWL": "Não tem a sessão iniciada e o ficheiro \"$1\" não está na lista branca.",
        "img-auth-nofile": "O ficheiro \"$1\" não existe.",
        "img-auth-isdir": "Está a tentar aceder ao diretório \"$1\".\nSó é permitido o acesso a ficheiros.",
-       "img-auth-streaming": "A fazer a transmissão de \"$1\".",
+       "img-auth-streaming": "A trasmitir \"$1\".",
        "img-auth-public": "A função do img_auth.php é produzir ficheiros a partir de uma wiki privada.\nEsta wiki está configurada como uma wiki pública.\nPara optimizar a segurança, o img_auth.php está impossibilitado de executar.",
        "img-auth-noread": "O utilizador não tem acesso de leitura ao ficheiro \"$1\".",
        "http-invalid-url": "URL inválido: $1",
        "doubleredirects": "Redirecionamentos duplos",
        "doubleredirectstext": "Esta página lista todas as páginas que redirecionam para outras páginas de redirecionamento.\nCada linha contém ligações para o primeiro e segundo redirecionamentos, bem como o destino do segundo redirecionamento, geralmente contendo a verdadeira página de destino, que devia ser o destino do primeiro redirecionamento.\n<del>Entradas cortadas</del> já foram solucionadas.",
        "double-redirect-fixed-move": "[[$1]] foi movida.\nEla foi atualizada automaticamente e agora redireciona para [[$2]].",
-       "double-redirect-fixed-maintenance": "A corrigir automaticamente o redirecionamento duplo de [[$1]] para [[$2]], em um trabalho de manutenção.",
+       "double-redirect-fixed-maintenance": "A corrigir automaticamente o redirecionamento duplo de [[$1]] para [[$2]] num processo de manutenção.",
        "double-redirect-fixer": "Corretor de redirecionamentos",
        "brokenredirects": "Redirecionamentos quebrados",
        "brokenredirectstext": "Os seguintes redirecionamentos contêm ligações para páginas inexistentes:",
        "import-invalid-interwiki": "Não é possível importar da wiki especificada.",
        "import-error-edit": "A página \"$1\" não foi importada porque não tem permissão para editá-la.",
        "import-error-create": "A página \"$1\" não foi importada porque não tem permissão para criá-la.",
-       "import-error-interwiki": "A página \"$1\" não pode ser importada pois o seu nome está reservado para um ligação externa (interlíngua).",
-       "import-error-special": "A página \"$1\" não pode ser importada porque pertence a um domínio especial que não permite páginas.",
-       "import-error-invalid": "A página \"$1\" não pode ser importada porque o seu nome é inválido nesta wiki.",
+       "import-error-interwiki": "A página \"$1\" não foi importada porque o nome dela está reservado para hiperligações externas (interwikis).",
+       "import-error-special": "A página \"$1\" não foi importada porque pertence a um espaço nominal especial que não permite páginas.",
+       "import-error-invalid": "A página \"$1\" não foi importada porque o nome para o qual seria importada é inválido nesta wiki.",
        "import-error-unserialize": "Não foi possível anular a seriação da revisão $2 da página \"$1\". Foi reportado que a revisão usava o modelo de conteúdo $3, seriado como $4.",
        "import-error-bad-location": "A revisão $2, que usa o modelo de conteúdo $3, não pode ser gravada em \"$1\" nesta wiki, porque o modelo não é suportado nessa página.",
        "import-options-wrong": "{{PLURAL:$2|Opção errada|Opções erradas}}: <nowiki>$1</nowiki>",
-       "import-rootpage-invalid": "A raiz da página dada é um título inválido.",
+       "import-rootpage-invalid": "A página de raiz fornecida é um título inválido.",
        "import-rootpage-nosubpage": "O domínio \"$1\" da página de raiz não permite subpáginas.",
        "importlogpage": "Registo de importações",
        "importlogpagetext": "Importações administrativas de páginas com a preservação do histórico de edição de outras wikis.",
        "tags-apply-blocked": "Não pode aplicar etiquetas de modificação nas suas alterações enquanto estiver {{GENDER:$1|bloqueado|bloqueada}}.",
        "tags-apply-not-allowed-one": "A etiqueta \"$1\" não pode ser aplicada manualmente.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser aplicada|As seguintes etiquetas não podem ser aplicadas}} manualmente: $1",
-       "tags-update-no-permission": "Não possui privilégios para adicionar ou remover etiquetas de revisões individuais ou entradas de registo.",
+       "tags-update-no-permission": "Não tem privilégios para adicionar ou remover etiquetas de revisões individuais ou entradas de registo.",
        "tags-update-blocked": "Não pode adicionar ou remover etiquetas de modificação enquanto estiver {{GENDER:$1|bloqueado|bloqueada}}.",
        "tags-update-add-not-allowed-one": "A etiqueta \"$1\" não pode ser adicionada manualmente.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser adicionada|As seguintes etiquetas não podem ser adicionadas}} manualmente: $1",
        "default-skin-not-found-no-skins": "O tema padrão da sua wiki definido em <code dir=\"ltr\">$wgDefaultSkin</code>, <code>$1</code>, não está disponível.\n\nNão tem nenhum tema instalado.\n\n; Se acabou de instalar ou atualizar o MediaWiki:\n: Provavelmente instalou-o a partir do git, ou diretamente do código fonte usando outro método. O comportamento é o esperado. O MediaWiki 1.24 e versões mais recentes não incluem qualquer tema no repositório principal. Tente instalar temas a partir do [https://www.mediawiki.org/wiki/Category:All_skins diretório de temas da mediawiki.org], assim:\n:* Descarregue o  [https://www.mediawiki.org/wiki/Download tarball de instalação], que contém vários temas e extensões. Pode copiar o diretório <code>skins/</code> nele incluído.\n:* Descarregue tarballs de temas individuais, da [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Use o Git para descarregar temas].\n: Se é programador(a) do MediaWiki, isto não deverá interferir com o seu repositório git. Consulte [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Configuração de Temas] para saber como ativar temas e escolher o tema padrão.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (ativado)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>desativado</strong>)",
-       "mediastatistics": "Estatísticas multimédia",
+       "mediastatistics": "Estatísticas de multimédia",
        "mediastatistics-summary": "Estatísticas sobre os tipos de ficheiro carregados. Inclui apenas a versão mais recente de cada ficheiro. Versões antigas ou eliminadas não estão incluídas.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
        "mediastatistics-bytespertype": "Tamanho total dos ficheiros desta secção: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
        "revid": "revisão $1",
        "pageid": "identificador de página $1",
        "rawhtml-notallowed": "As etiquetas &lt;html&gt; não podem ser utilizadas fora de páginas normais.",
-       "gotointerwiki": "A sair de {{SITENAME}}",
+       "gotointerwiki": "A sair da wiki {{SITENAME}}",
        "gotointerwiki-invalid": "O título especificado é inválido.",
        "gotointerwiki-external": "Está prestes a sair da wiki {{SITENAME}} para visitar [[$2]], que é um site externo.\n\n'''[$1 Continuar para $1]'''",
-       "undelete-cantedit": "Não pode restaurar esta página, porque não tem privilégios para editar esta página.",
-       "undelete-cantcreate": "Não pode restaurar esta página, porque não existe nenhuma página com este nome e não tem privilégios para criar esta página.",
+       "undelete-cantedit": "Não pode restaurar esta página porque não tem privilégios para a editar.",
+       "undelete-cantcreate": "Não pode restaurar esta página porque não existe nenhuma página com este nome e não tem privilégios para criar esta página.",
        "pagedata-title": "Dados de página",
        "pagedata-text": "Esta página fornece uma interface de dados para páginas. Forneça o título da página no URL, usando a sintaxe de subpáginas, por favor.\n* Aplica-se a negociação de conteúdo com base no cabeçalho Accept do seu cliente. Isto significa que os dados da página serão fornecidos no formato preferido do seu cliente.",
        "pagedata-not-acceptable": "Não foi encontrado nenhum formato correspondente. Tipos MIME suportados: $1",
index cd9df35..654ac6a 100644 (file)
        "diff-multi-sameuser": "This message appears in the revision history of a page when comparing two versions which aren't consecutive, and the intermediate revisions were all created by the same user as the new revision.\n\nParameters:\n* $1 - the number of revisions\n{{Related|Diff-multi}}",
        "diff-multi-otherusers": "This message appears in the revision history of a page when comparing two versions which aren't consecutive, and at least one of the intermediate revisions was created by a user other than the user who created the new revision.\n\nParameters:\n* $1 - the number of revisions\n* $2 - the number of distinct other users who made those revisions\n{{Related|Diff-multi}}",
        "diff-multi-manyusers": "This message appears in the revision history of a page when comparing two versions which aren't consecutive, and the intermediate revisions have been edited by more than 100 users.\n\nParameters:\n* $1 - the number of revisions, will always be 101 or more\n* $2 - the number of users that were found, which was limited at 100\n{{Related|Diff-multi}}",
+       "diff-paragraph-moved-tonew": "Explaining title tag for the indicating symbols when a paragraph was moved hinting to the new location in the Diff view.",
+       "diff-paragraph-moved-toold": "Explaining title tag for the indicating symbols when a paragraph was moved hinting to the old location in the Diff view.",
        "difference-missing-revision": "Text displayed when the requested revision does not exist using a diff link.\n\nExample: [{{canonicalurl:Project:News|diff=426850&oldid=99999999}} Diff with invalid revision#]\n\nParameters:\n* $1 - the list of missing revisions IDs\n* $2 - the number of items in $1 (one or two)",
        "search-summary": "{{doc-specialpagesummary|search}}",
        "searchresults": "This is the title of the page that contains the results of a search.\n\n{{Identical|Search results}}",
        "prefs-watchlist-edits": "Used in [[Special:Preferences]], tab \"Watchlist\".",
        "prefs-watchlist-edits-max": "Shown as hint in [[Special:Preferences]], tab \"Watchlist\"",
        "prefs-watchlist-token": "Used in [[Special:Preferences]], tab Watchlist.",
+       "prefs-watchlist-managetokens": "Label for the button to see and reset the user's private tokens",
        "prefs-misc": "Tab used on the [[Special:Preferences|user preferences]] special page.",
        "prefs-resetpass": "Button on user data tab in user preferences. When you click the button you go to the special page [[Special:ResetPass]].\n\n{{Identical|Change password}}",
        "prefs-changeemail": "Link on [[Special:Preferences]] to [[Special:ChangeEmail]]. [[Special:ChangeEmail]] also allows removing email address. \n\nSee also:\n* {{msg-mw|prefs-help-email-required|help}}\n* {{msg-mw|prefs-help-email|help}}\n* {{msg-mw|prefs-help-email-others|help}}\n* {{msg-mw|prefs-setemail|link title}}",
        "recentchangesdays-max": "Shown as hint in [[Special:Preferences]], tab \"Recent changes\". Parameters:\n* $1 - number of days\nSee also:\n* {{msg-mw|Prefs-watchlist-days-max}}",
        "recentchangescount": "Used in [[Special:Preferences]], tab \"Recent changes\".",
        "prefs-help-recentchangescount": "Used in [[Special:Preferences]], tab \"Recent changes\".",
-       "prefs-help-watchlist-token2": "Used in [[Special:Preferences]], tab Watchlist. (Formerly in {{msg-mw|prefs-help-watchlist-token}}.)",
+       "prefs-help-tokenmanagement": "Used in [[Special:Preferences]], Watchlist tab.",
        "savedprefs": "This message appears after saving changes to your user preferences.",
        "savedrights": "This message appears after saving the user groups on [[Special:UserRights]].\n* $1 - The user name of the user which groups was saved.",
        "timezonelegend": "{{Identical|Time zone}}",
        "variantname-sr-ec": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-ec</code> is not a conforming BCP47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-cyrl</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
        "variantname-sr-el": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-el</code> is not a conforming BCP47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-latn</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
        "variantname-sr": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
-       "variantname-kk-kz": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk-tr": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk-cn": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk-cyrl": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk-latn": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk-arab": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-kk": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-ku-arab": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-ku-latn": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
-       "variantname-ku": "{{optional}}\nVarient Option for wikis with variants conversion enabled.",
+       "variantname-kk-kz": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk-tr": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk-cn": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk-cyrl": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk-latn": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk-arab": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-kk": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-ku-arab": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-ku-latn": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-ku": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
        "variantname-tg-cyrl": "{{optional}}",
        "variantname-tg-latn": "{{optional}}",
        "variantname-tg": "{{optional}}",
        "variantname-uz": "{{optional}}",
        "variantname-uz-latn": "{{optional}}",
        "variantname-uz-cyrl": "{{optional}}",
+       "variantname-crh": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-crh-latn": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
+       "variantname-crh-cyrl": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
        "metadata": "The title of a section on an image description page, with information and data about the image. For example of message in use see [[commons:File:Titan-crystal_bar.JPG|Commons]].\n{{Identical|Metadata}}",
        "metadata-help": "This message is followed by a table with metadata.",
        "metadata-expand": "On an image description page, there is mostly a table containing data (metadata) about the image. The most important data are shown, but if you click on this link, you can see more data and information. For the link to hide back the less important data, see {{msg-mw|Metadata-collapse}}.",
index 8212a88..89ac217 100644 (file)
        "recentchangesdays-max": "(massime $1 {{PLURAL:$1|sciurne|sciurne}})",
        "recentchangescount": "Numere de cangiaminde da fà vedè pe default:",
        "prefs-help-recentchangescount": "Quiste 'nglude le urteme cangiaminde, le storie de le pàggene e le archivije.",
-       "prefs-help-watchlist-token2": "Queste jè 'a chiave segrete a le feed d'u web de l'elenghe de le pàggene condrollate tune.\nCengate vò ccu canosce ce pò leggere l'elenghe de le pàggene condrollate tune, accussì non g'ù pò condividere.\n[[Special:ResetTokens|Cazze aqquà ce tìne abbesogne de azzerarle]].",
        "savedprefs": "Le preferenze tue onne state aggiornete.",
        "savedrights": "Le gruppe utinde de {{GENDER:$1$1}} onne state reggistrate.",
        "timezonelegend": "Orarie d'a zone:",
index b8c6fda..a3299ed 100644 (file)
        "recentchangesdays-max": "(не более $1 {{PLURAL:$1|дня|дней}})",
        "recentchangescount": "Количество правок, отображаемое по умолчанию:",
        "prefs-help-recentchangescount": "Включает свежие правки, истории страниц, журналы.",
-       "prefs-help-watchlist-token2": "Это секретный ключ для веб-канала вашего списка наблюдений.\nЛюбой, кто знает его, сможет читать ваш список наблюдения, поэтому не сообщайте его другим. [[Special:ResetTokens|Нажмите здесь, если вам нужно сбросить его]].",
        "savedprefs": "Настройки сохранены.",
        "savedrights": "Группы {{GENDER:$1|участника|участницы}} $1 были сохранены.",
        "timezonelegend": "Часовой пояс:",
index 41fe87e..cf0eed4 100644 (file)
        "category-file-count-limited": "Latar reaḱ {{PLURAL:$1 rẽt rẽtko}} noa rokom sokomre menaḱa.",
        "listingcontinuesabbrev": "Calaḱa",
        "index-category": "Unuduḱ sakam ko do bạnuḱa",
-       "noindex-category": "Unuduḱ sakamkodo bạnuḱa",
+       "noindex-category": "ᱩᱱᱩᱫᱩᱜ ᱵᱟᱹᱱᱩᱜ-ᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "broken-file-category": "Baṅ kạmi daṛeaḱ chubi joṛao soho sakamko",
        "about": "Lạgitte, Lạgti",
        "article": "Menaḱakat́ sakam",
        "pool-errorunknown": "Bań baḍayaḱ bhul",
        "aboutsite": "babo̠tre {{SITENAME}}",
        "aboutpage": "Project: Babo̠t",
-       "copyright": "$1 re bhitrire ńamoḱa.",
+       "copyright": "$1 ᱨᱮ Content ᱧᱟᱢᱚᱜ-ᱟ ᱟᱨ ᱵᱟᱝᱠᱷᱟᱡᱽ ᱚᱞ ᱛᱟᱦᱮᱱᱟ",
        "copyrightpage": "{{ns:project}}: Eḱteạr",
        "currentevents": "Cạlit ghoṭnako",
        "currentevents-url": "Project: Nitaḱ evenṭ ko",
        "nosuchaction": "Noṅkanaḱ kạmi bạnuḱa",
        "nosuchactiontext": "Noa URL re goṭa akan kạmi do ạnlekate baṅkana.\nAm do paseć mit́ṭen vul joṛaoem emakada se URL oltem vul akada.\nNoa do noṅkanaḱ menkana je {{SITENAME}} sayeṭre beoharen sofṭower re mit́ṭen vul menaḱa.",
        "nosuchspecialpage": "Noṅkanaḱ asokay sakam do banuḱa",
-       "nospecialpagetext": "<strong>Am do mit́ṭen beạn asokae sakamem nehor akada.</strong>\n[[Special:SpecialPages {{int:specialpages}}]]-re ạn asokae sakamkore mit́ṭen tạlikam ńama.",
+       "nospecialpagetext": "<strong>ᱟᱢ ᱫᱚ ᱡᱟᱸᱦᱟ ᱥᱟᱦᱴᱟ ᱞᱟᱹᱜᱤᱛ ᱮᱢ ᱱᱮᱦᱚᱨ ᱟᱠᱟᱫᱟ ᱚᱱᱟᱫᱚ ᱵᱟᱹᱱᱩᱜ-ᱟ </strong>\nᱡᱟᱸᱦᱟ ᱥᱟᱦᱴᱟᱠᱩ ᱱᱚᱸᱰᱮ ᱢᱮᱱᱟᱜ-ᱟ ᱚᱱᱟᱨᱮᱱᱟᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱱᱚᱸᱰᱮᱢ ᱧᱟᱢᱟ [[Special:SpecialPages|{{int:specialpages}}]]᱾",
        "error": "bhul",
        "databaseerror": "Ḍaṭabase vul",
        "databaseerror-error": "ᱦᱩᱲᱟᱹᱜ: $1",
        "viewsource-title": "$1 renaḱ ńamoḱ ṭhại ńelmẽ",
        "actionthrottled": "Kạmi reaḱ dhara bại",
        "protectedpagetext": "Noa sakam do ol toṅge lạgit́te do bańcao gea.",
-       "viewsourcetext": "Noa sakam do am ńel ar ńamoḱaḱ ṭhạiem kopi hatao daṛeaḱa:",
+       "viewsourcetext": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱢ ᱧᱮᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱟᱨᱮᱢ ᱠᱚᱯᱤ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾",
        "viewyourtext": "Am do '''Amaḱ sompadon''' noa sakam ńel arem kopi hatao daṛeaḱa:",
        "protectedinterface": "Noa sakam reaḱ babotko do wiki sofṭoyer reaḱ mit́ṭen inṭarfes khobore ema, onate noa do rukhiyạ doho hoeakana.",
        "cascadeprotected": "Noa sakam do sompadon khon rukhiyạre menaḱa, karon sakam do latar reaḱ {{PLURAL:$1 gan sakam reaḱ gan sakam reaḱ}} bhitrire, oka sakam do (cascading) te rukhiyạ menaḱa:\n$2",
        "newarticle": "(Nãwa)",
        "newarticletext": "Am do oka mitṭen joṛaoem pańja akada, onaṭak do bạnuḱa.\nOna sakam tear lạgit́te, latar reaḱ baksore ol ehoṕmẽ (arhõ jạsti baḍae lạgit́te [$1 help page] pańjaemẽ).\nAm do judi nonḍe vulkatem heć akan khan, tobe amaḱ sendrakore '''back''' baṭon linmẽ.",
        "noarticletext": "Nitoḱ noa sakamre do cet́olge bạnuḱa.\n\nAm menlekhan eṭaḱ sakamkore [[Special:Search/{{PAGENAME}}|search for this page title]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.",
-       "noarticletext-nopermission": "Noa sakamre do nitoḱ o̠l banuḱa.\n\nYou can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages,\nor <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>.",
+       "noarticletext-nopermission": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱱᱤᱛᱚᱜ ᱪᱮᱫᱜᱮ ᱚᱞ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾\n\nᱟᱢ [[Special:Search/{{PAGENAME}}|ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱨᱮᱱᱟᱜ ᱧᱤᱛᱩᱢᱮᱢ ᱥᱮᱸᱫᱨᱟ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ]] ᱮᱴᱟ ᱥᱟᱦᱴᱟ ᱠᱚᱨᱮᱦᱚ,\nor <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" ńutuman jahãe beoharićaḱ ekaunṭ do baṅ resṭri hoeakana. Daya kate biḍạo katet́ ńelmẽ noa sakam do benoa/sompadonem menet́ kana se baṅ.",
        "userpage-userdoesnotexist-view": "Beoharićaḱ \"$1\" ekaunṭ do baṅ resṭire akana.",
        "blocked-notice-logextract": "Nui beoharić do nitoḱe esetgea.\nRefarens lạgit́te nahaḱ boloḱ do latare em hoena:",
+       "clearyourcache": "<strong>Note:</strong> After saving, you may have to bypass your browser's cache to see the changes.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> while clicking <em>Reload</em>, or press either <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> on a Mac)\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> on a Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> while clicking <em>Refresh</em>, or press <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Go to <em>Menu → Settings</em> (<em>Opera → Preferences</em> on a Mac) and then to <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "updated": "(Halot ruaṛ)",
        "note": "'''Noṭ:'''",
        "previewnote": "'''kheyalmẽ, noa do eken ńeloḱ lạgit.'''\nAmaḱ bodolaḱ kodo nit habićte bań rukhíạakana!",
-       "continue-editing": "Toṅge calaḱkana",
+       "continue-editing": "ᱥᱚᱞᱦᱮ ᱡᱟᱭᱜᱟ ᱪᱟᱞᱟᱜ ᱢᱮ",
        "editing": "Joṛao do purạena: $1",
        "creating": "$1 sakam doe tear akada",
        "editingsection": "Joṛao $1 (hạṭiń)",
        "nocreate-loggedin": "Nãwã sakam tear lạgit́te am do ạidạri em baṅ hoeakana.",
        "sectioneditnotsupported-title": "Pahaṭa sompadona do bae hataoeda",
        "sectioneditnotsupported-text": "Noa sompadona sakamre pahaṭa sompadona do bae hataoeda",
-       "permissionserrors": "Ạidạri vulko",
+       "permissionserrors": "á±\9fᱹᱭᱫá±\9fᱹᱨᱤ á±¦á±©á±²á±\9fá±¹á±\9c",
        "permissionserrorstext": "Noa kạmi amaḱ ạidạri do banuḱa, {{PLURAL:$1 gan karon reaḱ gan karon reaḱ}} lạgit:",
        "permissionserrorstext-withaction": "Amaḱ $2 kạmire ạydạri do bạnuḱa, Ona reaḱ {{PLURAL:$1 Karon/ Karonko}}:",
        "recreate-moveddeleted-warn": "'''Sontorokme: am do arhõ doṛhate sakamem teyareda oka do sedayre get giḍiyen.\nAm do gunạnme cet́ noa joṛao kạmi am lạgit́te ganoḱ ase bań.\nNoa get ar tala ocok sakam nonḍe em hoyena dhok lagit́te.",
-       "moveddeleted-notice": "Noa sakam do get giḍiyakana.\nGet ar ocoḱ giḍi sakam do latarre emakan reference lạgit em hoena.",
+       "moveddeleted-notice": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱜᱮᱫ ᱜᱤᱰᱤ ᱦᱩᱭ ᱟᱠᱟᱱᱟ᱾\nᱜᱮᱫ ᱥᱮ ᱵᱟᱸᱪᱟᱣ ᱥᱮ ᱚᱪᱚᱜ ᱜᱤᱰᱤᱭᱟᱠᱟᱱ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ reference ᱞᱟᱛᱟᱨᱨᱮ ᱮᱢ ᱦᱩᱭᱱᱟ᱾",
        "log-fulllog": "Joto cạbi udugmẽ",
        "edit-hook-aborted": "Huk hotete joto sompadonko bạgi hoeakana.\nNoa reaḱ jahan katha do bạnuḱa.",
        "edit-gone-missing": "Sakam do baṅ halot ruạṛlena.\nPasecc: sakam do ocoǵ hoeakana.",
        "page_first": "Pahilaḱ",
        "page_last": "Mucạt́aḱ",
        "histlegend": "Farak bachao: oka nãwã aroeko tulạoem menet́kan, onako cinhạ em kate boloḱ se latar baṭon linmẽ.<br />\nUnuduḱ: '''({{int:cur}})''' = nahaḱ nãwã aroeko saõte tulạo, '''({{int:last}})''' = laha reaḱ nãwã aroe sãote tulạo, '''{{int:minoreditletter}}''' = huḍiń sompadon.",
-       "history-fieldset-title": "Sendray jaṛ",
+       "history-fieldset-title": "revision ᱞᱟᱹᱜᱤᱛ ᱥᱮᱸᱫᱨᱟ",
        "history-show-deleted": "khạli get giḍiyaḱ koge",
-       "histfirst": "adi laha-ak'",
-       "histlast": "Nahak",
+       "histfirst": "ᱢᱟᱨᱮᱱᱟᱜ",
+       "histlast": "ᱱᱟᱣᱭᱟᱱᱟᱜ",
        "historysize": "({{PLURAL:$1 1 bayeṭ $1 bayeṭko}})",
        "historyempty": "(banuḱa)",
        "history-feed-title": "Jạṛ nãwã aroy",
        "shown-title": "Mit́ ṭen kateć sakam $1 {{PLURAL:$1|result|results}} nelmẽ",
        "viewprevnext": "Ńelme ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Noa wiki re do \"[[:$1]] ńutum sakam menaḱa",
-       "searchmenu-new": "wiki re [[:$1]]nãwã sakam tear",
+       "searchmenu-new": "ᱩᱭᱠᱤ ᱨᱮ [[:$1]] ᱱᱟᱣᱭᱟ ᱥᱟᱦᱴᱟ ᱛᱮᱭᱟᱨ",
        "searchprofile-articles": "Menaḱaḱ sakamko",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Sanamaḱ koge",
        "right-move-subpages": "Sakam saõte kạtic sakamko ocogmẽ",
        "right-movefile": "Rẽtko ocogmẽ",
        "right-upload": "Rẽtko rakabmẽ",
-       "right-writeapi": "write API ᱵᱮᱵᱦᱟᱨ",
+       "right-writeapi": "ᱚᱞ API ᱨᱮᱱᱟᱜ ᱵᱮᱵᱷᱟᱨ",
        "right-delete": "Sakamko get giḍiymẽ",
        "newuserlogpage": "Laṛcaṛićaḱ tear cạbi",
        "rightslog": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱟᱹᱭᱫᱟᱹᱨ ᱞᱚᱜᱽ",
        "recentchanges-label-plusminus": "ᱥᱟᱦᱴᱟ ᱫᱚ  ᱵᱚᱫᱚᱞᱮᱱᱟ ᱱᱤᱱᱟᱹᱜ ᱮᱞ ᱵᱟᱭᱤᱴᱥ ᱛᱮ",
        "recentchanges-legend-heading": "<strong>ᱞᱤᱡᱮᱸᱰ:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ᱟᱨᱦᱚᱸ ᱧᱮᱞᱢᱮ [[Special:NewPages|ᱱᱟᱶᱟ ᱥᱟᱦᱴᱟ ᱞᱤᱥᱴᱤ]])",
-       "rcnotefrom": "$2 habić bodolak ko do latare ńeloḱkana",
+       "rcnotefrom": "$2 ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡᱽ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱ ᱠᱚᱫᱚ ᱞᱟᱛᱟᱨᱨᱮ ᱧᱮᱞᱚᱜ-ᱟ",
        "rclistfrom": "Nãwã  bodolko uduḱme $3 $2 khon ehoṕkate",
        "rcshowhideminor": "$1 kaṭic culuń tońgeko",
        "rcshowhideminor-show": "Uduḱme",
        "emailmessage": "Mesag",
        "emailsend": "Kulmẽ",
        "usermessage-editor": "ᱥᱤᱥᱴᱚᱢ ᱨᱟᱭᱵᱟᱨ",
-       "watchlist": "Inak' n'el ko",
+       "watchlist": "ᱧᱮᱞᱟᱜ ᱞᱤᱥᱴᱤ",
        "mywatchlist": "Ńeloḱgoḱ tạlika",
        "watchlistfor2": "$1 ($2) lạ̣gitte",
        "watch": "Ńelme",
        "unwatch": "bang nelok' a",
-       "watchlist-details": "Baṅ purạo tạlikare {{PLURAL:$1ṭen sakam $1 ṭen sakam}} menaḱa (roṛ sakamko lekhare baṅ sapkate)",
+       "watchlist-details": "ᱵᱟᱝ ᱯᱩᱨᱟᱹᱣ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ{{PLURAL:$1 ᱥᱟᱦᱴᱟ $1 ᱥᱟᱦᱴᱟ}} ᱢᱮᱱᱟᱜ-ᱟ (ᱨᱚᱲ ᱥᱟᱦᱴᱟ ᱠᱚᱦᱚᱸ)",
+       "wlheader-showupdated": "ᱟᱢᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱵᱚᱞᱚᱝᱨᱮ ᱡᱟᱸᱦᱟᱸ ᱥᱟᱦᱴᱟ ᱠᱚᱢ ᱵᱚᱫᱚᱞ ᱞᱮᱫᱟ ᱚᱱᱟᱠᱩ ᱧᱮᱞᱚᱜ-ᱟ <strong>bold</strong>.",
        "wlnote": "ᱞᱟᱛᱟᱨ ᱨᱮᱱᱟᱜ {{PLURAL:$1|ᱫᱚ ᱢᱩᱪᱟᱹᱫ ᱵᱚᱫᱚᱞ ᱠᱟᱱᱟ|ᱠᱚ ᱫᱚ ᱢᱩᱪᱟᱹᱫ <strong>$1</strong> ᱵᱚᱫᱚᱞᱠᱟᱱᱟ}} ᱢᱩᱪᱟᱹᱫ ᱨᱮ {{PLURAL:$2|ᱴᱟᱲᱟᱝ|<strong>$2</strong> ᱴᱟᱲᱟᱝ}},  $3, $4 ᱞᱮᱠᱟᱛᱮ ᱾",
-       "wlshowlast": "Mucạtet́ udukmẽ $1 baje $2 maha",
+       "wlshowlast": "ᱢᱩᱪᱟᱹᱛ ᱩᱫᱩᱜᱢᱮ $1 ᱜᱷᱟᱱᱴᱟ $2 ᱢᱟᱦᱟ",
        "watchlist-options": "Ńelok tạlika reak sonketko",
        "watching": "Ńeloḱ kana...",
        "enotif_reset": "ᱱᱤᱱᱦᱟᱹᱭᱢᱮ ᱡᱚᱛᱚ ᱥᱟᱦᱴᱟ ᱦᱤᱨᱤᱭᱟᱠᱟᱱᱟ",
        "contributions-title": "$1 Beoharićaḱ kạmiko",
        "mycontris": "Ińaḱ kạmiko",
        "anoncontribs": "Ińaḱ kạmiko",
-       "contribsub2": "$1 ($2) lạgitte",
+       "contribsub2": "$1 ($2) ᱞᱟᱹᱜᱤᱛᱛᱮ",
        "nocontribs": "ᱱᱚᱶᱟ ᱮᱢᱟᱜ ᱥᱟᱶ ᱡᱚᱲᱟᱣᱟᱱ ᱵᱚᱫᱚᱞᱠᱚ ᱵᱟᱭ ᱧᱟᱢᱞᱮᱱᱟ |",
        "uctop": "(ᱱᱤᱛᱚᱜ)",
        "month": "Cando khon (ar etohopreaḱ)",
        "whatlinkshere-hideredirs": "$1 arhõ unuduḱ",
        "whatlinkshere-hidetrans": "Selet́ $1",
        "whatlinkshere-hidelinks": "$1 joṛaoko",
-       "whatlinkshere-hideimages": "$1 Chubi joṛaoko",
+       "whatlinkshere-hideimages": "$1 file ᱡᱚᱝᱲᱮᱠᱩ",
        "whatlinkshere-filters": "Sapha",
        "block": "Beoharić esedem",
        "blockip": "Beoharić esedem",
        "tooltip-t-whatlinkshere": "Sanam wiki sakam renaḱ list ar link do nonde",
        "tooltip-t-recentchangeslinked": "Noa sakam re nitaḱ bodol akan sakam renaḱ linked",
        "tooltip-feed-atom": "Noa sakam lạgit́ atom phiḍ",
-       "tooltip-t-contributions": "Beoharićak kami reaḱ tạ̣lika",
-       "tooltip-t-emailuser": "Nui beoharić mitṭen e-mail kulayme",
+       "tooltip-t-contributions": "ᱮᱱᱮᱢ ᱨᱮᱱᱟᱜ ᱛᱟᱹᱞᱠᱟᱹ {{GENDER:$1|this user}}",
+       "tooltip-t-emailuser": "ᱱᱩᱭ ᱵᱮᱵᱦᱟᱨᱤᱪ ᱢᱤᱫᱴᱮᱱ ᱤ ᱢᱮᱞ ᱠᱩᱞᱟᱭᱢᱮ",
        "tooltip-t-upload": "Phayelko aploḍ̣me",
        "tooltip-t-specialpages": "Jạruṛ patakureaḱ tạlikạ",
        "tooltip-t-print": "Printoḱ lekan sakam",
        "simpleantispam-label": "Enṭi espam ńel\nDo <strong>not</strong> noa purạome!",
        "pageinfo-title": "\"$1\" ᱞᱟᱹᱜᱤᱫ ᱥᱩᱪᱱᱟ",
        "pageinfo-header-basic": "ᱢᱩᱬ ᱥᱩᱪᱱᱟ",
-       "pageinfo-header-edits": "Toṅgeko",
+       "pageinfo-header-edits": "ᱥᱟᱯᱲᱟᱣ ᱱᱟᱜᱟᱢ",
        "pageinfo-header-restrictions": "ᱥᱟᱦᱴᱟ ᱵᱟᱧᱪᱟᱣ",
        "pageinfo-header-properties": "ᱥᱟᱦᱴᱟ ᱜᱩᱱᱠᱚ",
        "pageinfo-display-title": "ᱩᱫᱩᱜ ᱧᱩᱛᱩᱢ",
        "pageinfo-robot-policy": "ᱨᱚᱵᱚᱴ ᱫᱟᱨᱟᱭᱛᱮ ᱩᱱᱩᱫᱩᱜ",
        "pageinfo-robot-index": "ᱚᱪᱚᱣᱟᱜ",
        "pageinfo-robot-noindex": "ᱵᱟᱝᱚᱪᱚ",
-       "pageinfo-watchers": "Ńeńelkoaḱ nombor",
+       "pageinfo-watchers": "ᱥᱟᱦᱴᱟ ᱧᱮᱧᱮᱞᱤᱭᱟᱹ ᱠᱚᱣᱟᱜ ᱮᱞ",
        "pageinfo-few-watchers": "$1 ᱠᱷᱚᱱ ᱠᱚᱢ {{PLURAL:$1|ᱧᱮᱧᱮᱞᱤᱭᱟᱹ|ᱧᱮᱧᱮᱞᱤᱭᱟᱹᱠᱚ}}",
        "pageinfo-redirects-name": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱛᱮ ᱢᱚᱸᱦᱰᱟᱜᱠᱟᱱ ᱮᱞ",
        "pageinfo-subpages-name": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱪᱟᱸᱜᱟ ᱥᱟᱦᱴᱟ ᱠᱚᱣᱟᱜ ᱮᱞ",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|galmarao]])",
        "duplicate-defaultsort": "'''Sontoroḱmẽ:''' ḍifolṭ sajao reaḱ cạbi: $2 lahare ḍifolṭ sajao reaḱ sakam: ''$1'' e bae luturaḱ kana.",
        "redirect": "ᱨᱮᱫ, ᱵᱮᱵᱷᱟᱨᱩᱭᱟᱹ, ᱥᱟᱦᱴᱟ, ᱧᱮᱞ-ᱟᱹᱨᱩ, ᱵᱟᱝᱠᱷᱟᱱ ᱞᱚᱜᱽ ID ᱫᱟᱨᱟᱭᱛᱮ ᱢᱚᱦᱰᱟ",
+       "redirect-summary": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱢᱚᱦᱰᱟ ᱟ ᱢᱤᱫ ᱨᱮᱫ (ᱮᱢᱟᱠᱟᱱ ᱨᱮᱫᱧᱩᱛᱩᱢ) ᱴᱷᱮᱱ, ᱢᱤᱫ ᱥᱟᱦᱴᱟ (ᱮᱢᱮᱱ ᱟᱹᱨᱩᱣᱟᱜ ID ᱟᱨᱵᱟᱝ ᱥᱟᱦᱴᱟ ID),  ᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱩᱭᱟᱹ ᱥᱟᱦᱴᱟ (ᱮᱢᱮᱱ ᱮᱞᱩᱠ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ID ), ᱟᱨᱵᱟᱝ ᱢᱤᱫ ᱞᱚᱜᱽ ᱵᱚᱞᱚ (ᱮᱢᱮᱱ ᱞᱚᱜᱽ ID) ᱾ ᱵᱮᱵᱷᱟᱨᱟᱠᱟᱱ: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], ᱟᱨᱵᱟᱝ [[{{#Special:Redirect}}/logid/186]]",
        "redirect-submit": "ᱥᱮᱱᱚᱜ",
        "redirect-lookup": "ᱧᱮᱞᱢᱮ",
        "redirect-value": "ᱫᱟᱢ:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ᱥᱟᱛᱚᱢ|ᱥᱟᱛᱚᱢᱠᱩ}}]]: $2)",
        "tags-active-yes": "ᱦᱮᱸ",
        "tags-active-no": "ᱵᱟᱝ",
+       "tags-hitcount": "$1 {{PLURAL:$1|ᱟᱹᱨᱩ|ᱟᱹᱨᱩᱠᱚ}}",
        "logentry-delete-delete": "$3 ᱥᱟᱦᱴᱟ $1 {{GENDER:$2|ᱜᱮᱫ ᱠᱮᱜ-ᱟᱭ}}",
        "logentry-delete-restore": "$1 {{GENDER:$2|ᱨᱟᱠᱷᱟ ᱫᱚᱲᱦᱟ}} ᱠᱮᱜ-ᱟ ᱥᱟᱦᱴᱟ $3 ($4)",
        "logentry-delete-revision": "$1 {{GENDER:$2|ᱵᱚᱫᱚᱞᱠᱮᱜ-ᱟᱭ}} ᱧᱮᱞᱚᱜᱟᱜ {{PLURAL:$5|ᱫᱚᱦᱲᱟᱭᱮᱱᱟᱜ|$5 ᱫᱚᱦᱲᱟᱭᱮᱱᱟᱜ ᱠᱚ}} $3: $4 ᱥᱟᱦᱴᱟ ᱪᱮᱛᱟᱱᱨᱮ",
        "logentry-move-move": "$1 beoharić $3 sakam do $4 ńutumre {{GENDER:$2|ạcạr}} akada",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|ᱩᱪᱟᱹᱲᱠᱮᱜ-ᱟᱭ}} ᱥᱟᱦᱴᱟ $3 to $4 ᱢᱚᱦᱰᱟ ᱵᱤᱱ ᱵᱟᱹᱜᱤ ᱠᱟᱛᱮ",
        "logentry-move-move_redir": "$1 {{GENDER:$2|ᱩᱪᱟᱹᱲᱮᱱᱟ}} ᱥᱟᱦᱴᱟ $3 ᱠᱷᱚᱱ $4 ᱪᱮᱛᱟᱱ ᱢᱚᱸᱦᱰᱟ ᱦᱟᱠᱟᱱᱟ",
+       "logentry-patrol-patrol-auto": "$1 ᱟᱡᱛᱮᱜᱮ {{GENDER:$2|ᱪᱤᱱᱦᱟᱹᱭᱮᱱᱟ}} $4 ᱧᱮᱞᱟᱹᱨᱩ $3 ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱾",
        "logentry-newusers-create": "Beoharićaḱ hisạb khata $1 do jhićena",
        "logentry-newusers-autocreate": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱷᱟᱛᱟ $1 ᱫᱚ {{GENDER:$2|ᱛᱮᱭᱟᱨᱮᱱᱟ}} ᱟᱡᱛᱮᱜᱮ",
        "logentry-upload-upload": "$1 {{GENDER:$2|rakaṕ akadae}} $3",
index 37981db..be219ed 100644 (file)
        "diff-multi-sameuser": "({{PLURAL:$1|1=Vmesna redakcija|$1 vmesna redakcija|$1 vmesni redakciji|$1 vmesne redakcije|$1 vmesnih redakcij}} istega uporabnika {{PLURAL:$1|ni prikazana|nista prikazani|niso prikazane|ni prikazanih}})",
        "diff-multi-otherusers": "({{PLURAL:$1|1=Vmesna redakcija|$1 vmesna redakcija|$1 vmesni redakciji|$1 vmesne redakcije|$1 vmesnih redakcij}} {{PLURAL:$2|1=drugega uporabnika|$2 uporabnikov}} {{PLURAL:$1|ni prikazana|nista prikazani|niso prikazane|ni prikazanih}})",
        "diff-multi-manyusers": "({{PLURAL:$1|$1 vmesna redakcija|$1 vmesni redakciji|$1 vmesne redakcije|$1 vmesnih redakcij}} več kot $2 {{PLURAL:$2|uporabnika|uporabnikov}} {{PLURAL:$1|ni prikazana|nista prikazani|niso prikazane|ni prikazanih}})",
+       "diff-paragraph-moved-tonew": "Odstavek je premaknjen. Kliknite, da skočite na novo nahajališče.",
+       "diff-paragraph-moved-toold": "Odstavek je bil premaknjen. Kliknite, da skočite na staro nahajališče.",
        "difference-missing-revision": "{{PLURAL:$2|Ene redakcije|$2 redakcij}} razlike ($1) {{PLURAL:$2|nisem}} našel.\n\nPo navadi se to zgodi, ko sledite zastareli povezavi na razliko redakcij strani, ki jo je nekdo izbrisal.\nPodrobnosti lahko najdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dnevniku brisanja].",
        "searchresults": "Izid iskanja",
        "searchresults-title": "Zadetki za povpraševanje »$1«",
        "recentchangesdays-max": "Največ $1 {{PLURAL:$1|dan|dneva|dnevi|dni}}",
        "recentchangescount": "Privzeto število prikazanih urejanj:",
        "prefs-help-recentchangescount": "Vključuje zadnje spremembe, zgodovine strani in dnevniške zapise.",
-       "prefs-help-watchlist-token2": "To je skrivni ključ do spletnega vira vašega spiska nadzorov. Kdor ve zanj, lahko bere vaš spisek nadzorov, zato ključa ne delite. [[Special:ResetTokens|Kliknite tukaj, če ga želite ponastaviti]].",
        "savedprefs": "Spremembe smo uspešno shranili.",
        "savedrights": "Uporabniške skupine {{GENDER:$1|$1}} smo shranili.",
        "timezonelegend": "Časovni pas",
index 85f314e..d787f45 100644 (file)
        "recentchangesdays-max": "Највише $1 {{PLURAL:$1|дан|дана}}",
        "recentchangescount": "Број измена за приказ:",
        "prefs-help-recentchangescount": "Подразумева скорашње измене, историје страница и дневнике.",
-       "prefs-help-watchlist-token2": "Ово је тајни кључ за веб-довод Вашег списка надгледања. \nСвако ко зна овај кључ биће у могућности да види Ваша надгледања; стога, кључ немојте одавати никоме. \nАко је потребно, кључ можете [[Special:ResetTokens|ресетовати]].",
        "savedprefs": "Ваша подешавања су сачувана.",
        "savedrights": "Корисничке групе за {{GENDER:$1|$1}} су сачуване.",
        "timezonelegend": "Временска зона:",
index c26c5e0..4943f77 100644 (file)
        "recentchangesdays-max": "Maximalt $1 {{PLURAL:$1|dygn}}",
        "recentchangescount": "Antal redigeringar som visas som standard:",
        "prefs-help-recentchangescount": "Detta inkluderar senaste ändringarna, sidhistorik och loggar.",
-       "prefs-help-watchlist-token2": "Detta är den hemliga nyckeln till webbflödet i din bevakningslista.\nNågon som vet den kommer att kunna läsa din bevakningslista, så dela inte ut den.\n[[Special:ResetTokens|Klicka här om du behöver återställa den]].",
        "savedprefs": "Dina inställningar har sparats",
        "savedrights": "Användargrupperna för {{GENDER:$1|$1}} har sparats.",
        "timezonelegend": "Tidszon:",
index 22d93f5..2045c3e 100644 (file)
        "recentchangesdays-max": "மிக அதிகமாக $1 {{PLURAL:$1|நாள்|நாட்கள்}}",
        "recentchangescount": "மொத்தத் தொகுப்புக்களின் எண்ணிக்கையைத் தானாகவே காட்ட:",
        "prefs-help-recentchangescount": "அண்மைய மாற்றங்களையும், பக்கத்தின் வரலாறுகளையும் பதிவேட்டுப் பதிவுகளையும் உள்ளடக்கியதாகும்.",
-       "prefs-help-watchlist-token2": "உங்கள் கவனிப்புப்பட்டியலின் வலை ஓடைக்கு இது இரகசிய சாவி.\nஇதை அறிந்த எவரும் உங்கள் கவனிப்பு பட்டியலை வாசிக்கலாம், எனவே இதை பகிராதீர்கள்.\nதேவை ஏற்படின், [[Special:ResetTokens|நீங்கள் அதனை புதுப்பிக்கலாம்]].",
        "savedprefs": "உங்கள் விருப்பத்தேர்வுகள் சேமிக்கப்பட்டுள்ளன.",
        "savedrights": "{{GENDER:$1|$1}}-க்கான பயனர் உரிமைகள் சேமிக்கப்பட்டன.",
        "timezonelegend": "நேர வலயம்:",
index 07c79e6..e6f5a51 100644 (file)
@@ -25,7 +25,7 @@
                        "LR Guanzon"
                ]
        },
-       "tog-underline": "Pagsasalungguhit ng kawing:",
+       "tog-underline": "Pagsasalungguhit ng link:",
        "tog-hideminor": "Itago ang mga maliliit na pagbabago mula sa mga huling pagbabago",
        "tog-hidepatrolled": "Itago ang mga napatrolyang pagbabago mula sa mga huling pagbabago",
        "tog-newpageshidepatrolled": "Itago ang mga napatrolyang pahina mula talaan ng bagong pahina",
        "mytalk": "Usapan",
        "anontalk": "Usapan",
        "navigation": "Paglilibot",
-       "and": ",&#32;at",
+       "and": "&#32;at",
        "faq": "Mga malilimit itanong",
        "actions": "Mga kilos",
        "namespaces": "Mga ngalan-espasyo",
        "history_small": "kasaysayan",
        "updatedmarker": "isinapanahon mula noong huli kong pagdalaw",
        "printableversion": "Bersiyong maililimbag",
-       "permalink": "Permanenteng kawing",
+       "permalink": "Permanenteng link",
        "print": "Ilimbag",
        "view": "Tingnan",
        "view-foreign": "Tingnan sa $1",
        "edithelp": "Tulong sa pagbabago",
        "helppage-top-gethelp": "Tulong",
        "mainpage": "Unang Pahina",
-       "mainpage-description": "Unang Pahina",
+       "mainpage-description": "Unang pahina",
        "policy-url": "Project:Patakaran",
        "portal": "Puntahan ng pamayanan",
        "portal-url": "Project:Puntahan ng pamayanan",
index a4ab374..7c21904 100644 (file)
        "anontalk": "اس آئی پی پتہ کا تبادلۂ خیال",
        "navigation": "رہنمائی",
        "and": "&#32;اور",
-       "faq": "عاÙ\85 Ø·Ù\88ر Ù¾ر پوچھے جانے والے سوالات",
+       "faq": "اکثر پوچھے جانے والے سوالات",
        "actions": "اقدامات",
        "namespaces": "نام فضا",
        "variants": "متغیرات",
        "diff-multi-sameuser": "(ایک ہی صارف کا {{PLURAL: $1 |ایک درمیانی نسخہ نہیں دکھایا گیا| $1 درمیانی نسخے نہیں دکھائے گئے}})",
        "diff-multi-otherusers": "({{PLURAL:$2|ایک دوسرے صارف|$2 صارفین}} {{PLURAL:$1|کا ایک درمیانی نسخہ نہیں دکھایا گیا|$1 کے درمیانی نسخے نہیں دکھائے گئے}})",
        "diff-multi-manyusers": "($2 سے زیادہ {{PLURAL:$2|صارف|صارفین}} {{PLURAL:$1|کا ایک درمیانی نسخہ نہیں دکھایا گیا|$1 کے درمیانی نسخے نہیں دکھائے گئے}})",
+       "diff-paragraph-moved-tonew": "عبارت ہٹا دی گئی تھی۔ نئے مقام پر جانے کے لیے کلک کریں۔",
+       "diff-paragraph-moved-toold": "عبارت ہٹا دی گئی تھی۔ پرانے مقام پر واپس جانے کے لیے کلِک کریں۔",
        "difference-missing-revision": "اس فرق ($1) {{PLURAL:$2|کا ایک نسخہ نہیں ملا|$2 کے نسخے نہیں ملے}}۔\n\nعموماً ایسا اس وقت ہوتا ہے جب کسی حذف شدہ صفحہ کے نسخوں کے درمیان میں فرق تلاش کرنے کی کوشش کی جائے۔\nمزید تفصیلات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔",
        "searchresults": "تلاش کے نتائج",
        "searchresults-title": "«$1» کے نتائج تلاش",
        "recentchangesdays-max": "زیادہ سے زیادہ $1 {{PLURAL:$1|دن}}",
        "recentchangescount": "دکھائی جانے والی ترامیم کی تعداد:",
        "prefs-help-recentchangescount": "اِس میں حالیہ تبدیلیاں، تاریخچے اور نوشتہ جات شامل ہیں۔",
-       "prefs-help-watchlist-token2": "یہ آپ کی زیر نظر فہرست کے ویب فیڈ کی خفیہ کلید ہے۔\nاسے خفیہ رکھیں، تاکہ کوئی دوسرا شخص آپ کی زیر نظر فہرست نہ دیکھ سکے۔\nاگر آپ کو کلید تبدیل کرنی ہو تو [[Special:ResetTokens|یہاں کلک کریں]]۔",
        "savedprefs": "آپ کی ترجیحات محفوظ ہوگئیں۔",
        "savedrights": "{{GENDER:$1|$1}} کے اختیارات محفوظ ہو گئے۔",
        "timezonelegend": "منطقۂ وقت:",
        "statistics-header-hooks": "دیگر اعداد و شمار",
        "statistics-articles": "مندرج صفحات",
        "statistics-pages": "صفحات",
-       "statistics-pages-desc": "(ویکی اقتباسات کے کل صفحات، بشمولِ تبادلۂ خیال، رجوع مکررات وغیرہ۔)",
+       "statistics-pages-desc": "(وکی اقتباسات کے کل صفحات، بشمولِ تبادلۂ خیال، رجوع مکررات وغیرہ۔)",
        "statistics-files": "اپلوڈ کردہ فائلیں",
        "statistics-edits": "{{SITENAME}} کے آغاز سے کل صفحاتی ترامیم",
        "statistics-edits-average": "فی صفحہ اوسط ترامیم",
        "logentry-patrol-patrol": "$1 نے صفحہ $3 کے نسخہ $4 کو مراجعت شدہ {{GENDER:$2|نشان زد کیا}}",
        "logentry-patrol-patrol-auto": "$1 نے صفحہ $3 کے نسخہ $4 کو خودکار طور پر مراجعت شدہ {{GENDER:$2|نشان زد کیا}}",
        "logentry-newusers-newusers": "صارف کھاتہ $1 {{GENDER:$2|تخلیق ہو چکا ہے}}",
-       "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بنایا گیا}}",
+       "logentry-newusers-create": "$1 کے نام سے صارف کھاتہ {{GENDER:$2|بنایا گیا}}",
        "logentry-newusers-create2": "$1 نے صارف کھاتہ $3 {{GENDER:$2|تخلیق کیا}}",
        "logentry-newusers-byemail": "$1 نے صارف کھاتہ $3 {{GENDER:$2|تخلیق کیا}} اور پاس ورڈ بذریعہ برقی خط روانہ کیا گیا ہے",
        "logentry-newusers-autocreate": "صارف کھاتہ $1 خودکار طور پر {{GENDER:$2|تخلیق ہوا}}",
index 08e1f8c..9cbf68e 100644 (file)
        "diff-multi-sameuser": "(未显示同一用户的$1个中间版本)",
        "diff-multi-otherusers": "(未显示{{PLURAL:$1|另一用户|$2个用户}}的{{PLURAL:$1|$1个中间版本}})",
        "diff-multi-manyusers": "(未显示超过$2个用户的$1个中间版本)",
+       "diff-paragraph-moved-tonew": "段落已移动。点击跳到新位置。",
+       "diff-paragraph-moved-toold": "段落已移动。点击跳到旧位置。",
        "difference-missing-revision": "此差异对比的{{PLURAL:$2|$2个版本}}($1){{PLURAL:$2|没有}}找到。\n\n这通常是因为进入了一个已被删除的页面的版本差异对比链接。\n详细信息可以在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中找到。",
        "searchresults": "搜索结果",
        "searchresults-title": "“$1”的搜索结果",
        "prefs-watchlist-edits": "在监视列表中显示的更改的最大数目:",
        "prefs-watchlist-edits-max": "最大数目:1000",
        "prefs-watchlist-token": "监视列表密钥:",
+       "prefs-watchlist-managetokens": "管理令牌",
        "prefs-misc": "其他",
        "prefs-resetpass": "更改密码",
        "prefs-changeemail": "更改或移除电子邮件地址",
        "recentchangesdays-max": "最多$1天",
        "recentchangescount": "默认显示的编辑数:",
        "prefs-help-recentchangescount": "这包括最近更改、页面历史和日志。",
-       "prefs-help-watchlist-token2": "这是您的监视列表的网络feed密钥。\n任何拥有者均可以浏览您的监视列表,因此不要公开该密钥。\n如果有需要,[[Special:ResetTokens|您可以重置密钥]]。",
+       "prefs-help-tokenmanagement": "您可以查看并重置您账户的密钥,它用来访问您监视列表的Web订阅源。任何知道密钥的人都将可以阅读您的监视列表,所以不要分享它。",
        "savedprefs": "您的系统设置已保存。",
        "savedrights": "{{GENDER:$1|$1}}的用户组已被保存。",
        "timezonelegend": "时区:",
        "apisandbox": "API 沙盒",
        "apisandbox-jsonly": "需要JavaScript以使用API沙盒。",
        "apisandbox-api-disabled": "API在该网站停用。",
-       "apisandbox-intro": "使用这个页面来试验<strong>MediaWiki Web 服务应用程序接口(API)</strong>。\n欲知API使用详情,请参阅[[mw:API:Main page|API文档]]。\n例如:[https://www.mediawiki.org/wiki/API#A_simple_example 取得某个主页的内容],然后选择一个操作来看更多范例。\n\n请注意,虽然这是一个沙盒,但是您在这个页面上的改动可能会修改维基。",
+       "apisandbox-intro": "使用这个页面来试验<strong>MediaWiki Web 服务应用程序接口(API)</strong>。欲知API使用详情,请参阅[[mw:API:Main page|API文档]]。例如:[https://www.mediawiki.org/wiki/API#A_simple_example 取得某个主页的内容],然后选择一个操作来看更多范例。\n\n请注意,虽然这是一个沙盒,但是您在这个页面上的改动可能会修改维基。",
        "apisandbox-fullscreen": "展开面板",
        "apisandbox-fullscreen-tooltip": "展开沙盒面板以填充浏览器窗口。",
        "apisandbox-unfullscreen": "显示页面",
        "articleexists": "该名称的页面已存在,或者您使用的名称无效。请另选一名。",
        "cantmove-titleprotected": "您无法将页面移动到该位置,因为新标题已被保护以防止创建。",
        "movetalk": "移动关联的讨论页",
-       "move-subpages": "移动子页面(上至$1页)",
+       "move-subpages": "移动子页面(最多$1页)",
        "move-talk-subpages": "如果可能,移动子对话页面(上至$1页)",
        "movepage-page-exists": "页面$1已存在,无法自动覆盖。",
        "movepage-page-moved": "页面$1已经移动到$2。",
        "variantname-gan-hans": "赣语(简体)",
        "variantname-gan-hant": "赣语(繁体)",
        "variantname-kk-cyrl": "kk-cyrl",
+       "variantname-crh-latn": "克里米亚鞑靼文(拉丁)",
+       "variantname-crh-cyrl": "克里米亚鞑靼文(西里尔)",
        "metadata": "元数据",
        "metadata-help": "此文件中包含有额外的信息。这些信息可能是由数码相机或扫描仪在创建或数字化过程中所添加的。如果文件自初始状态已受到修改,一些详细说明可能无法反映修改后的文件。",
        "metadata-expand": "显示详细资料",
index 33f569a..0c5f2dc 100644 (file)
        "recentchangesdays-max": "最多 $1 {{PLURAL:$1|天}}",
        "recentchangescount": "預設顯示的編輯數:",
        "prefs-help-recentchangescount": "這包含近期變更、頁面歷史以及日誌。",
-       "prefs-help-watchlist-token2": "訂閱您的監視清單所需的密鑰。\n任何人只要知道密鑰就能夠讀取您的監視清單,所以請勿任意與它人共享。\n若有需要 [[Special:ResetTokens|您可重設密鑰]]。",
        "savedprefs": "已儲存您的偏好設定。",
        "savedrights": "已儲存 {{GENDER:$1|$1}} 的使用者權限。",
        "timezonelegend": "時區:",
        "rcfilters-filtergroup-authorship": "貢獻的作者",
        "rcfilters-filter-editsbyself-label": "您的編輯",
        "rcfilters-filter-editsbyself-description": "您的貢獻",
-       "rcfilters-filter-editsbyother-label": "其他人的更改",
+       "rcfilters-filter-editsbyother-label": "其他人的變更",
        "rcfilters-filter-editsbyother-description": "除了您以外的所有更改",
        "rcfilters-filtergroup-userExpLevel": "使用者註冊及經驗",
        "rcfilters-filter-user-experience-level-registered-label": "已註冊",
index f85461f..05d2aba 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Crimean Turkish (Qırımtatarca)
+/** Crimean Tatar (Qırımtatarca)
  *
  * To improve a translation please visit https://translatewiki.net
  *
index ff68ad8..1ad2b56 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Crimean Turkish (Cyrillic script) (къырымтатарджа (Кирилл)‎)
+/** Crimean Tatar (Cyrillic script) (къырымтатарджа (Кирилл)‎)
  *
  * To improve a translation please visit https://translatewiki.net
  *
index 9a993ea..7a2c97f 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Crimean Turkish (Latin script) (qırımtatarca (Latin)‎)
+/** Crimean Tatar (Latin script) (qırımtatarca (Latin)‎)
  *
  * To improve a translation please visit https://translatewiki.net
  *
index d37b990..10082e9 100644 (file)
@@ -398,19 +398,31 @@ abstract class Maintenance {
         * Throw an error to the user. Doesn't respect --quiet, so don't use
         * this for non-error output
         * @param string $err The error to display
-        * @param int $die If > 0, go ahead and die out using this int as the code
+        * @param int $die Deprecated since 1.31, use Maintenance::fatalError() instead
         */
        protected function error( $err, $die = 0 ) {
+               if ( intval( $die ) !== 0 ) {
+                       wfDeprecated( __METHOD__ . '( $err, $die )', '1.31' );
+                       $this->fatalError( $err, intval( $die ) );
+               }
                $this->outputChanneled( false );
                if ( PHP_SAPI == 'cli' ) {
                        fwrite( STDERR, $err . "\n" );
                } else {
                        print $err;
                }
-               $die = intval( $die );
-               if ( $die > 0 ) {
-                       die( $die );
-               }
+       }
+
+       /**
+        * Output a message and terminate the current script.
+        *
+        * @param string $msg Error message
+        * @param int $exitCode PHP exit status. Should be in range 1-254.
+        * @since 1.31
+        */
+       protected function fatalError( $msg, $exitCode = 1 ) {
+               $this->error( $msg );
+               exit( $exitCode );
        }
 
        private $atLineStart = true;
@@ -559,7 +571,7 @@ abstract class Maintenance {
                        $joined = implode( ', ', $missing );
                        $msg = "The following extensions are required to be installed "
                                . "for this script to run: $joined. Please enable them and then try again.";
-                       $this->error( $msg, 1 );
+                       $this->fatalError( $msg );
                }
        }
 
@@ -652,17 +664,17 @@ abstract class Maintenance {
 
                # Abort if called from a web server
                if ( isset( $_SERVER ) && isset( $_SERVER['REQUEST_METHOD'] ) ) {
-                       $this->error( 'This script must be run from the command line', true );
+                       $this->fatalError( 'This script must be run from the command line' );
                }
 
                if ( $IP === null ) {
-                       $this->error( "\$IP not set, aborting!\n" .
-                               '(Did you forget to call parent::__construct() in your maintenance script?)', 1 );
+                       $this->fatalError( "\$IP not set, aborting!\n" .
+                               '(Did you forget to call parent::__construct() in your maintenance script?)' );
                }
 
                # Make sure we can handle script parameters
                if ( !defined( 'HPHP_VERSION' ) && !ini_get( 'register_argc_argv' ) ) {
-                       $this->error( 'Cannot get command line arguments, register_argc_argv is set to false', true );
+                       $this->fatalError( 'Cannot get command line arguments, register_argc_argv is set to false' );
                }
 
                // Send PHP warnings and errors to stderr instead of stdout.
@@ -1177,9 +1189,9 @@ abstract class Maintenance {
                }
 
                if ( !is_readable( $settingsFile ) ) {
-                       $this->error( "A copy of your installation's LocalSettings.php\n" .
+                       $this->fatalError( "A copy of your installation's LocalSettings.php\n" .
                                "must exist and be readable in the source directory.\n" .
-                               "Use --conf to specify it.", true );
+                               "Use --conf to specify it." );
                }
                $wgCommandLineMode = true;
 
index 60b8a7a..341a299 100644 (file)
@@ -419,10 +419,6 @@ class BackupDumper extends Maintenance {
                        fwrite( $this->stderr, $string . "\n" );
                }
        }
-
-       function fatalError( $msg ) {
-               $this->error( "$msg\n", 1 );
-       }
 }
 
 class ExportProgressFilter extends DumpFilter {
index dc92516..fa93f23 100644 (file)
@@ -41,7 +41,7 @@ class BenchmarkJSMinPlus extends Benchmarker {
                $content = file_get_contents( $this->getOption( 'file' ) );
                MediaWiki\restoreWarnings();
                if ( $content === false ) {
-                       $this->error( 'Unable to open input file', 1 );
+                       $this->fatalError( 'Unable to open input file' );
                }
 
                $filename = basename( $this->getOption( 'file' ) );
index 1753250..a613f96 100644 (file)
@@ -106,7 +106,7 @@ class BenchmarkParse extends Maintenance {
 
                $loops = $this->getOption( 'loops', 1 );
                if ( $loops < 1 ) {
-                       $this->error( 'Invalid number of loops specified', true );
+                       $this->fatalError( 'Invalid number of loops specified' );
                }
                $startUsage = getrusage();
                $startTime = microtime( true );
index e006cf5..8566c0b 100644 (file)
@@ -37,7 +37,7 @@ class BenchmarkPurge extends Benchmarker {
        public function execute() {
                global $wgUseSquid, $wgSquidServers;
                if ( !$wgUseSquid ) {
-                       $this->error( "Squid purge benchmark doesn't do much without squid support on.", true );
+                       $this->fatalError( "Squid purge benchmark doesn't do much without squid support on." );
                } else {
                        $this->output( "There are " . count( $wgSquidServers ) . " defined squid servers:\n" );
                        if ( $this->hasOption( 'count' ) ) {
index 1479174..6698db3 100644 (file)
@@ -15,19 +15,19 @@ class BenchmarkTidy extends Maintenance {
        public function execute() {
                $html = file_get_contents( $this->getOption( 'file' ) );
                if ( $html === false ) {
-                       $this->error( "Unable to open input file", 1 );
+                       $this->fatalError( "Unable to open input file" );
                }
                if ( $this->hasOption( 'driver' ) || $this->hasOption( 'tidy-config' ) ) {
                        $config = json_decode( $this->getOption( 'tidy-config', '{}' ), true );
                        if ( !is_array( $config ) ) {
-                               $this->error( "Invalid JSON tidy config", 1 );
+                               $this->fatalError( "Invalid JSON tidy config" );
                        }
                        $config += [ 'driver' => $this->getOption( 'driver', 'RemexHtml' ) ];
                        $driver = MWTidy::factory( $config );
                } else {
                        $driver = MWTidy::singleton();
                        if ( !$driver ) {
-                               $this->error( "Tidy disabled or not installed", 1 );
+                               $this->fatalError( "Tidy disabled or not installed" );
                        }
                }
 
index 9fa6632..d7db321 100644 (file)
@@ -46,10 +46,10 @@ class ChangePassword extends Maintenance {
                } elseif ( $this->hasOption( "userid" ) ) {
                        $user = User::newFromId( $this->getOption( 'userid' ) );
                } else {
-                       $this->error( "A \"user\" or \"userid\" must be set to change the password for", true );
+                       $this->fatalError( "A \"user\" or \"userid\" must be set to change the password for" );
                }
                if ( !$user || !$user->getId() ) {
-                       $this->error( "No such user: " . $this->getOption( 'user' ), true );
+                       $this->fatalError( "No such user: " . $this->getOption( 'user' ) );
                }
                $password = $this->getOption( 'password' );
                try {
@@ -64,7 +64,7 @@ class ChangePassword extends Maintenance {
                        $user->saveSettings();
                        $this->output( "Password set for " . $user->getName() . "\n" );
                } catch ( PasswordError $pwe ) {
-                       $this->error( $pwe->getText(), true );
+                       $this->fatalError( $pwe->getText() );
                }
        }
 }
index e5b4c13..22f5969 100644 (file)
@@ -24,9 +24,8 @@ class CheckComposerLockUpToDate extends Maintenance {
                        // Maybe they're using mediawiki/vendor?
                        $lockLocation = "$IP/vendor/composer.lock";
                        if ( !file_exists( $lockLocation ) ) {
-                               $this->error(
-                                       'Could not find composer.lock file. Have you run "composer install --no-dev"?',
-                                       1
+                               $this->fatalError(
+                                       'Could not find composer.lock file. Have you run "composer install --no-dev"?'
                                );
                        }
                }
@@ -51,10 +50,9 @@ class CheckComposerLockUpToDate extends Maintenance {
                        }
                }
                if ( $found ) {
-                       $this->error(
+                       $this->fatalError(
                                'Error: your composer.lock file is not up to date. ' .
-                                       'Run "composer update --no-dev" to install newer dependencies',
-                               1
+                                       'Run "composer update --no-dev" to install newer dependencies'
                        );
                } else {
                        // We couldn't find any out-of-date dependencies, so assume everything is ok!
index fc3cc5b..3d039fa 100644 (file)
@@ -47,7 +47,7 @@ class CleanupSpam extends Maintenance {
                $username = wfMessage( 'spambot_username' )->text();
                $wgUser = User::newSystemUser( $username );
                if ( !$wgUser ) {
-                       $this->error( "Invalid username specified in 'spambot_username' message: $username", true );
+                       $this->fatalError( "Invalid username specified in 'spambot_username' message: $username" );
                }
                // Hack: Grant bot rights so we don't flood RecentChanges
                $wgUser->addGroup( 'bot' );
@@ -55,7 +55,7 @@ class CleanupSpam extends Maintenance {
                $spec = $this->getArg();
                $like = LinkFilter::makeLikeArray( $spec );
                if ( !$like ) {
-                       $this->error( "Not a valid hostname specification: $spec", true );
+                       $this->fatalError( "Not a valid hostname specification: $spec" );
                }
 
                if ( $this->hasOption( 'all' ) ) {
index 50e17d8..24d6d86 100644 (file)
@@ -165,7 +165,7 @@ class TitleCleanup extends TableCleanup {
                        $title = $verified;
                }
                if ( is_null( $title ) ) {
-                       $this->error( "Something awry; empty title.", true );
+                       $this->fatalError( "Something awry; empty title." );
                }
                $ns = $title->getNamespace();
                $dest = $title->getDBkey();
index 14c6a6b..aeaf150 100644 (file)
@@ -122,7 +122,7 @@ class UploadStashCleanup extends Maintenance {
                $iterator = $tempRepo->getBackend()->getFileList( [ 'dir' => $dir, 'adviseStat' => 1 ] );
                $this->output( "Deleting orphaned temp files...\n" );
                if ( strpos( $dir, '/local-temp' ) === false ) { // sanity check
-                       $this->error( "Temp repo is not using the temp container.", 1 ); // die
+                       $this->fatalError( "Temp repo is not using the temp container." );
                }
                $i = 0;
                $batch = []; // operation batch
index f2540c7..3b09385 100644 (file)
@@ -97,7 +97,7 @@ class CompareParsers extends DumpIterator {
                if ( $this->hasOption( 'tidy' ) ) {
                        global $wgUseTidy;
                        if ( !$wgUseTidy ) {
-                               $this->error( 'Tidy was requested but $wgUseTidy is not set in LocalSettings.php', true );
+                               $this->fatalError( 'Tidy was requested but $wgUseTidy is not set in LocalSettings.php' );
                        }
                        $this->options->setTidy( true );
                }
index 0554949..24391c1 100644 (file)
@@ -82,7 +82,7 @@ class ConvertExtensionToRegistration extends Maintenance {
                unset( $var );
                $arg = $this->getArg( 0 );
                if ( !is_file( $arg ) ) {
-                       $this->error( "$arg is not a file.", true );
+                       $this->fatalError( "$arg is not a file." );
                }
                require $arg;
                unset( $arg );
@@ -160,14 +160,14 @@ class ConvertExtensionToRegistration extends Maintenance {
        protected function handleExtensionFunctions( $realName, $value ) {
                foreach ( $value as $func ) {
                        if ( $func instanceof Closure ) {
-                               $this->error( "Error: Closures cannot be converted to JSON. " .
-                                       "Please move your extension function somewhere else.", 1
+                               $this->fatalError( "Error: Closures cannot be converted to JSON. " .
+                                       "Please move your extension function somewhere else."
                                );
                        }
                        // check if $func exists in the global scope
                        if ( function_exists( $func ) ) {
-                               $this->error( "Error: Global functions cannot be converted to JSON. " .
-                                       "Please move your extension function ($func) into a class.", 1
+                               $this->fatalError( "Error: Global functions cannot be converted to JSON. " .
+                                       "Please move your extension function ($func) into a class."
                                );
                        }
                }
@@ -239,14 +239,14 @@ class ConvertExtensionToRegistration extends Maintenance {
                        }
                        foreach ( $handlers as $func ) {
                                if ( $func instanceof Closure ) {
-                                       $this->error( "Error: Closures cannot be converted to JSON. " .
-                                               "Please move the handler for $hookName somewhere else.", 1
+                                       $this->fatalError( "Error: Closures cannot be converted to JSON. " .
+                                               "Please move the handler for $hookName somewhere else."
                                        );
                                }
                                // Check if $func exists in the global scope
                                if ( function_exists( $func ) ) {
-                                       $this->error( "Error: Global functions cannot be converted to JSON. " .
-                                               "Please move the handler for $hookName inside a class.", 1
+                                       $this->fatalError( "Error: Global functions cannot be converted to JSON. " .
+                                               "Please move the handler for $hookName inside a class."
                                        );
                                }
                        }
index ee103b8..b46cac7 100644 (file)
@@ -81,7 +81,7 @@ class CopyFileBackend extends Maintenance {
                                        'adviseStat' => true // avoid HEADs
                                ] );
                                if ( $srcPathsRel === null ) {
-                                       $this->error( "Could not list files in $container.", 1 ); // die
+                                       $this->fatalError( "Could not list files in $container." );
                                }
                        }
 
@@ -93,7 +93,7 @@ class CopyFileBackend extends Maintenance {
                                        'adviseStat' => true // avoid HEADs
                                ] );
                                if ( $dstPathsRel === null ) {
-                                       $this->error( "Could not list files in $container.", 1 ); // die
+                                       $this->fatalError( "Could not list files in $container." );
                                }
                                $this->statCache = [];
                                foreach ( $dstPathsRel as $dstPathRel ) {
@@ -174,12 +174,12 @@ class CopyFileBackend extends Maintenance {
                $srcPathsRel = $src->getFileList( [
                        'dir' => $src->getRootStoragePath() . "/$backendRel" ] );
                if ( $srcPathsRel === null ) {
-                       $this->error( "Could not list files in source container.", 1 ); // die
+                       $this->fatalError( "Could not list files in source container." );
                }
                $dstPathsRel = $dst->getFileList( [
                        'dir' => $dst->getRootStoragePath() . "/$backendRel" ] );
                if ( $dstPathsRel === null ) {
-                       $this->error( "Could not list files in destination container.", 1 ); // die
+                       $this->fatalError( "Could not list files in destination container." );
                }
                // Get the list of destination files
                $relFilesDstSha1 = [];
@@ -263,7 +263,7 @@ class CopyFileBackend extends Maintenance {
                        $status = $dst->prepare( [ 'dir' => dirname( $dstPath ), 'bypassReadOnly' => 1 ] );
                        if ( !$status->isOK() ) {
                                $this->error( print_r( $status->getErrorsArray(), true ) );
-                               $this->error( "$wikiId: Could not copy $srcPath to $dstPath.", 1 ); // die
+                               $this->fatalError( "$wikiId: Could not copy $srcPath to $dstPath." );
                        }
                        $ops[] = [ 'op' => 'store',
                                'src' => $fsFile->getPath(), 'dst' => $dstPath, 'overwrite' => 1 ];
@@ -280,7 +280,7 @@ class CopyFileBackend extends Maintenance {
                $elapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( !$status->isOK() ) {
                        $this->error( print_r( $status->getErrorsArray(), true ) );
-                       $this->error( "$wikiId: Could not copy file batch.", 1 ); // die
+                       $this->fatalError( "$wikiId: Could not copy file batch." );
                } elseif ( count( $copiedRel ) ) {
                        $this->output( "\n\tCopied these file(s) [{$elapsed_ms}ms]:\n\t" .
                                implode( "\n\t", $copiedRel ) . "\n\n" );
@@ -317,7 +317,7 @@ class CopyFileBackend extends Maintenance {
                $elapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( !$status->isOK() ) {
                        $this->error( print_r( $status->getErrorsArray(), true ) );
-                       $this->error( "$wikiId: Could not delete file batch.", 1 ); // die
+                       $this->fatalError( "$wikiId: Could not delete file batch." );
                } elseif ( count( $deletedRel ) ) {
                        $this->output( "\n\tDeleted these file(s) [{$elapsed_ms}ms]:\n\t" .
                                implode( "\n\t", $deletedRel ) . "\n\n" );
index 08e40fd..7dd40b8 100644 (file)
@@ -48,9 +48,9 @@ class CopyJobQueue extends Maintenance {
                $dstKey = $this->getOption( 'dst' );
 
                if ( !isset( $wgJobQueueMigrationConfig[$srcKey] ) ) {
-                       $this->error( "\$wgJobQueueMigrationConfig not set for '$srcKey'.", 1 );
+                       $this->fatalError( "\$wgJobQueueMigrationConfig not set for '$srcKey'." );
                } elseif ( !isset( $wgJobQueueMigrationConfig[$dstKey] ) ) {
-                       $this->error( "\$wgJobQueueMigrationConfig not set for '$dstKey'.", 1 );
+                       $this->fatalError( "\$wgJobQueueMigrationConfig not set for '$dstKey'." );
                }
 
                $types = ( $this->getOption( 'type' ) === 'all' )
index 1872716..8035c3e 100644 (file)
@@ -63,15 +63,15 @@ class CreateAndPromote extends Maintenance {
 
                $user = User::newFromName( $username );
                if ( !is_object( $user ) ) {
-                       $this->error( "invalid username.", true );
+                       $this->fatalError( "invalid username." );
                }
 
                $exists = ( 0 !== $user->idForName() );
 
                if ( $exists && !$force ) {
-                       $this->error( "Account exists. Perhaps you want the --force option?", true );
+                       $this->fatalError( "Account exists. Perhaps you want the --force option?" );
                } elseif ( !$exists && !$password ) {
-                       $this->error( "Argument <password> required!", false );
+                       $this->error( "Argument <password> required!" );
                        $this->maybeHelp( true );
                } elseif ( $exists ) {
                        $inGroups = $user->getGroups();
@@ -133,7 +133,7 @@ class CreateAndPromote extends Maintenance {
                                        $user->saveSettings();
                                }
                        } catch ( PasswordError $pwe ) {
-                               $this->error( $pwe->getText(), true );
+                               $this->fatalError( $pwe->getText() );
                        }
                }
 
index f7e0c0f..e77113a 100644 (file)
@@ -60,12 +60,12 @@ class GenerateCommonPassword extends Maintenance {
                $outfile = $this->getArg( 1 );
 
                if ( !is_readable( $infile ) && $infile !== 'php://stdin' ) {
-                       $this->error( "Cannot open input file $infile for reading", 1 );
+                       $this->fatalError( "Cannot open input file $infile for reading" );
                }
 
                $file = fopen( $infile, 'r' );
                if ( $file === false ) {
-                       $this->error( "Cannot read input file $infile", 1 );
+                       $this->fatalError( "Cannot read input file $infile" );
                }
 
                try {
@@ -109,7 +109,7 @@ class GenerateCommonPassword extends Maintenance {
                                " (out of $i) passwords to $outfile\n"
                        );
                } catch ( \Cdb\Exception $e ) {
-                       $this->error( "Error writing cdb file: " . $e->getMessage(), 2 );
+                       $this->fatalError( "Error writing cdb file: " . $e->getMessage(), 2 );
                }
        }
 }
index 0020446..eceadc1 100644 (file)
@@ -65,7 +65,7 @@ class DeleteBatch extends Maintenance {
                        $user = User::newFromName( $username );
                }
                if ( !$user ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
                $wgUser = $user;
 
@@ -77,7 +77,7 @@ class DeleteBatch extends Maintenance {
 
                # Setup
                if ( !$file ) {
-                       $this->error( "Unable to read file, exiting", true );
+                       $this->fatalError( "Unable to read file, exiting" );
                }
 
                $dbw = $this->getDB( DB_MASTER );
index ba8662a..417aa03 100644 (file)
@@ -72,7 +72,7 @@ class DeleteDefaultMessages extends Maintenance {
                // in order to hide it in RecentChanges.
                $user = User::newFromName( 'MediaWiki default' );
                if ( !$user ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
                $user->addGroup( 'bot' );
                $wgUser = $user;
index 5fc7d18..2a1fe22 100644 (file)
@@ -123,7 +123,7 @@ class DeleteEqualMessages extends Maintenance {
                                $this->fetchMessageInfo( false, $messageInfo );
                        } else {
                                if ( !isset( $langCodes[$langCode] ) ) {
-                                       $this->error( 'Invalid language code: ' . $langCode, 1 );
+                                       $this->fatalError( 'Invalid language code: ' . $langCode );
                                }
                                $this->fetchMessageInfo( $langCode, $messageInfo );
                        }
@@ -164,7 +164,7 @@ class DeleteEqualMessages extends Maintenance {
 
                $user = User::newSystemUser( 'MediaWiki default', [ 'steal' => true ] );
                if ( !$user ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
                global $wgUser;
                $wgUser = $user;
index 9bf1222..4890199 100644 (file)
@@ -87,7 +87,7 @@ TEXT
                } elseif ( $this->hasOption( 'revrange' ) ) {
                        $this->dump( WikiExporter::RANGE, $textMode );
                } else {
-                       $this->error( 'No valid action specified.', 1 );
+                       $this->fatalError( 'No valid action specified.' );
                }
        }
 
index 6dbad94..254f368 100644 (file)
@@ -48,7 +48,7 @@ abstract class DumpIterator extends Maintenance {
 
        public function execute() {
                if ( !( $this->hasOption( 'file' ) ^ $this->hasOption( 'dump' ) ) ) {
-                       $this->error( "You must provide a file or dump", true );
+                       $this->fatalError( "You must provide a file or dump" );
                }
 
                $this->checkOptions();
@@ -70,8 +70,8 @@ abstract class DumpIterator extends Maintenance {
                if ( $this->getOption( 'dump' ) == '-' ) {
                        $source = new ImportStreamSource( $this->getStdin() );
                } else {
-                       $this->error( "Sorry, I don't support dump filenames yet. "
-                               . "Use - and provide it on stdin on the meantime.", true );
+                       $this->fatalError( "Sorry, I don't support dump filenames yet. "
+                               . "Use - and provide it on stdin on the meantime." );
                }
                $importer = new WikiImporter( $source, $this->getConfig() );
 
index 4219ed0..7e50e9e 100644 (file)
@@ -59,7 +59,7 @@ class EditCLI extends Maintenance {
                        $wgUser = User::newFromName( $userName );
                }
                if ( !$wgUser ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
                if ( $wgUser->isAnon() ) {
                        $wgUser->addToDatabase();
@@ -67,13 +67,13 @@ class EditCLI extends Maintenance {
 
                $title = Title::newFromText( $this->getArg() );
                if ( !$title ) {
-                       $this->error( "Invalid title", true );
+                       $this->fatalError( "Invalid title" );
                }
 
                if ( $this->hasOption( 'nocreate' ) && !$title->exists() ) {
-                       $this->error( "Page does not exist", true );
+                       $this->fatalError( "Page does not exist" );
                } elseif ( $this->hasOption( 'createonly' ) && $title->exists() ) {
-                       $this->error( "Page already exists", true );
+                       $this->fatalError( "Page already exists" );
                }
 
                $page = WikiPage::factory( $title );
index d94d49b..24ef1ed 100644 (file)
@@ -50,7 +50,7 @@ class EraseArchivedFile extends Maintenance {
 
                if ( $filekey === '*' ) { // all versions by name
                        if ( !strlen( $filename ) ) {
-                               $this->error( "Missing --filename parameter.", 1 );
+                               $this->fatalError( "Missing --filename parameter." );
                        }
                        $afile = false;
                } else { // specified version
@@ -60,7 +60,7 @@ class EraseArchivedFile extends Maintenance {
                                [ 'fa_storage_group' => 'deleted', 'fa_storage_key' => $filekey ],
                                __METHOD__, [], $fileQuery['joins'] );
                        if ( !$row ) {
-                               $this->error( "No deleted file exists with key '$filekey'.", 1 );
+                               $this->fatalError( "No deleted file exists with key '$filekey'." );
                        }
                        $filename = $row->fa_name;
                        $afile = ArchivedFile::newFromRow( $row );
@@ -68,7 +68,7 @@ class EraseArchivedFile extends Maintenance {
 
                $file = wfLocalFile( $filename );
                if ( $file->exists() ) {
-                       $this->error( "File '$filename' is still a public file, use the delete form.\n", 1 );
+                       $this->fatalError( "File '$filename' is still a public file, use the delete form.\n" );
                }
 
                $this->output( "Purging all thumbnails for file '$filename'..." );
index b1e4fa9..542bdda 100644 (file)
@@ -37,7 +37,7 @@ class ExportSites extends Maintenance {
                $handle = fopen( $file, 'w' );
 
                if ( !$handle ) {
-                       $this->error( "Failed to open $file for writing.\n", 1 );
+                       $this->fatalError( "Failed to open $file for writing.\n" );
                }
 
                $exporter = new SiteExporter( $handle );
index fd36db1..6a21a61 100644 (file)
@@ -143,7 +143,7 @@ class FindHooks extends Maintenance {
                ) {
                        $this->output( "Looks good!\n" );
                } else {
-                       $this->error( 'The script finished with errors.', 1 );
+                       $this->fatalError( 'The script finished with errors.' );
                }
        }
 
index c4cab71..522bbc2 100644 (file)
@@ -37,7 +37,7 @@ class FindOrphanedFiles extends Maintenance {
 
                $repo = RepoGroup::singleton()->getLocalRepo();
                if ( $repo->hasSha1Storage() ) {
-                       $this->error( "Local repo uses SHA-1 file storage names; aborting.", 1 );
+                       $this->fatalError( "Local repo uses SHA-1 file storage names; aborting." );
                }
 
                $directory = $repo->getZonePath( 'public' );
@@ -51,7 +51,7 @@ class FindOrphanedFiles extends Maintenance {
 
                $list = $repo->getBackend()->getFileList( [ 'dir' => $directory ] );
                if ( $list === null ) {
-                       $this->error( "Could not get file listing.", 1 );
+                       $this->fatalError( "Could not get file listing." );
                }
 
                $pathBatch = [];
index 8c9faca..7e29f09 100644 (file)
@@ -48,7 +48,7 @@ class FixDoubleRedirects extends Maintenance {
                if ( $this->hasOption( 'title' ) ) {
                        $title = Title::newFromText( $this->getOption( 'title' ) );
                        if ( !$title || !$title->isRedirect() ) {
-                               $this->error( $title->getPrefixedText() . " is not a redirect!\n", true );
+                               $this->fatalError( $title->getPrefixedText() . " is not a redirect!\n" );
                        }
                } else {
                        $title = null;
index 796ec26..1efbc5f 100644 (file)
@@ -56,7 +56,7 @@ class FixTimestamps extends Maintenance {
                $row = $dbw->fetchObject( $res );
 
                if ( is_null( $row->minrev ) ) {
-                       $this->error( "No revisions in search period.", true );
+                       $this->fatalError( "No revisions in search period." );
                }
 
                $minRev = $row->minrev;
@@ -99,14 +99,14 @@ class FixTimestamps extends Maintenance {
 
                $numBadRevs = count( $badRevs );
                if ( $numBadRevs > $numGoodRevs ) {
-                       $this->error(
+                       $this->fatalError(
                                "The majority of revisions in the search interval are marked as bad.
 
                Are you sure the offset ($offset) has the right sign? Positive means the clock
                was incorrectly set forward, negative means the clock was incorrectly set back.
 
                If the offset is right, then increase the search interval until there are enough
-               good revisions to provide a majority reference.", true );
+               good revisions to provide a majority reference." );
                } elseif ( $numBadRevs == 0 ) {
                        $this->output( "No bad revisions found.\n" );
                        exit( 0 );
index e2b3c41..95d90c1 100644 (file)
@@ -41,8 +41,7 @@ class MaintenanceFormatInstallDoc extends Maintenance {
                        $fileName = $this->getArg( 0 );
                        $inFile = fopen( $fileName, 'r' );
                        if ( !$inFile ) {
-                               $this->error( "Unable to open input file \"$fileName\"" );
-                               exit( 1 );
+                               $this->fatalError( "Unable to open input file \"$fileName\"" );
                        }
                } else {
                        $inFile = STDIN;
@@ -52,8 +51,7 @@ class MaintenanceFormatInstallDoc extends Maintenance {
                        $fileName = $this->getOption( 'outfile' );
                        $outFile = fopen( $fileName, 'w' );
                        if ( !$outFile ) {
-                               $this->error( "Unable to open output file \"$fileName\"" );
-                               exit( 1 );
+                               $this->fatalError( "Unable to open output file \"$fileName\"" );
                        }
                } else {
                        $outFile = STDOUT;
index a84f2ae..ec32aee 100644 (file)
@@ -55,7 +55,7 @@ class GenerateJsonI18n extends Maintenance {
 
                if ( $extension ) {
                        if ( $phpfile ) {
-                               $this->error( "The phpfile is already specified, conflicts with --extension.", 1 );
+                               $this->fatalError( "The phpfile is already specified, conflicts with --extension." );
                        }
                        $phpfile = "$IP/extensions/$extension/$extension.i18n.php";
                }
@@ -101,28 +101,28 @@ class GenerateJsonI18n extends Maintenance {
                        $this->output( "Creating directory $jsondir.\n" );
                        $success = mkdir( $jsondir );
                        if ( !$success ) {
-                               $this->error( "Could not create directory $jsondir", 1 );
+                               $this->fatalError( "Could not create directory $jsondir" );
                        }
                }
 
                if ( !is_readable( $phpfile ) ) {
-                       $this->error( "Error reading $phpfile", 1 );
+                       $this->fatalError( "Error reading $phpfile" );
                }
                $messages = null;
                include $phpfile;
                $phpfileContents = file_get_contents( $phpfile );
 
                if ( !isset( $messages ) ) {
-                       $this->error( "PHP file $phpfile does not define \$messages array", 1 );
+                       $this->fatalError( "PHP file $phpfile does not define \$messages array" );
                }
 
                if ( !$messages ) {
-                       $this->error( "PHP file $phpfile contains an empty \$messages array. " .
-                               "Maybe it was already converted?", 1 );
+                       $this->fatalError( "PHP file $phpfile contains an empty \$messages array. " .
+                               "Maybe it was already converted?" );
                }
 
                if ( !isset( $messages['en'] ) || !is_array( $messages['en'] ) ) {
-                       $this->error( "PHP file $phpfile does not set language codes", 1 );
+                       $this->fatalError( "PHP file $phpfile does not set language codes" );
                }
 
                foreach ( $messages as $langcode => $langmsgs ) {
@@ -142,7 +142,7 @@ class GenerateJsonI18n extends Maintenance {
                                FormatJson::encode( $langmsgs, "\t", FormatJson::ALL_OK ) . "\n"
                        );
                        if ( $success === false ) {
-                               $this->error( "FAILED to write $jsonfile", 1 );
+                               $this->fatalError( "FAILED to write $jsonfile" );
                        }
                        $this->output( "$jsonfile\n" );
                }
index 26a9c39..bed84a8 100644 (file)
@@ -182,7 +182,7 @@ class GenerateSitemap extends Maintenance {
                # Create directory if needed
                $fspath = $this->getOption( 'fspath', getcwd() );
                if ( !wfMkdirParents( $fspath, null, __METHOD__ ) ) {
-                       $this->error( "Can not create directory $fspath.", 1 );
+                       $this->fatalError( "Can not create directory $fspath." );
                }
 
                $this->fspath = realpath( $fspath ) . DIRECTORY_SEPARATOR;
index 65ffe14..18dcc22 100644 (file)
@@ -64,7 +64,7 @@ class GetConfiguration extends Maintenance {
 
                $validFormat = in_array( $format, self::$outFormats );
                if ( !$validFormat ) {
-                       $this->error( "--format set to an unrecognized format", 0 );
+                       $this->error( "--format set to an unrecognized format" );
                        $error_out = true;
                }
 
index f519a79..21a183b 100644 (file)
@@ -42,13 +42,13 @@ class GetTextMaint extends Maintenance {
                $titleText = $this->getArg( 0 );
                $title = Title::newFromText( $titleText );
                if ( !$title ) {
-                       $this->error( "$titleText is not a valid title.\n", true );
+                       $this->fatalError( "$titleText is not a valid title.\n" );
                }
 
                $rev = Revision::newFromTitle( $title );
                if ( !$rev ) {
                        $titleText = $title->getPrefixedText();
-                       $this->error( "Page $titleText does not exist.\n", true );
+                       $this->fatalError( "Page $titleText does not exist.\n" );
                }
                $content = $rev->getContent( $this->hasOption( 'show-private' )
                        ? Revision::RAW
@@ -56,7 +56,7 @@ class GetTextMaint extends Maintenance {
 
                if ( $content === false ) {
                        $titleText = $title->getPrefixedText();
-                       $this->error( "Couldn't extract the text from $titleText.\n", true );
+                       $this->fatalError( "Couldn't extract the text from $titleText.\n" );
                }
                $this->output( $content->serialize() );
        }
index c1aa082..f77f5b9 100644 (file)
@@ -97,7 +97,7 @@ class HHVMMakeRepo extends Maintenance {
 
                $tmpDir = wfTempDir() . '/mw-make-repo' . mt_rand( 0, 1 << 31 );
                if ( !mkdir( $tmpDir ) ) {
-                       $this->error( 'Unable to create temporary directory', 1 );
+                       $this->fatalError( 'Unable to create temporary directory' );
                }
                file_put_contents( "$tmpDir/file-list", implode( "\n", $files ) );
 
@@ -119,11 +119,11 @@ class HHVMMakeRepo extends Maintenance {
                passthru( $cmd, $ret );
                if ( $ret ) {
                        $this->cleanupTemp( $tmpDir );
-                       $this->error( "Error: HHVM returned error code $ret", 1 );
+                       $this->fatalError( "Error: HHVM returned error code $ret" );
                }
                if ( !rename( "$tmpDir/hhvm.hhbc", $this->getOption( 'output' ) ) ) {
                        $this->cleanupTemp( $tmpDir );
-                       $this->error( "Error: unable to rename output file", 1 );
+                       $this->fatalError( "Error: unable to rename output file" );
                }
                $this->cleanupTemp( $tmpDir );
                return 0;
index 206c7ee..cf0e7d8 100644 (file)
@@ -87,7 +87,7 @@ TEXT
 
        public function execute() {
                if ( wfReadOnly() ) {
-                       $this->error( "Wiki is in read-only mode; you'll need to disable it for import to work.", true );
+                       $this->fatalError( "Wiki is in read-only mode; you'll need to disable it for import to work." );
                }
 
                $this->reportingInterval = intval( $this->getOption( 'report', 100 ) );
@@ -134,7 +134,7 @@ TEXT
                if ( strval( $ns ) === $namespace && $wgContLang->getNsText( $ns ) !== false ) {
                        return $ns;
                }
-               $this->error( "Unknown namespace text / index specified: $namespace", true );
+               $this->fatalError( "Unknown namespace text / index specified: $namespace" );
        }
 
        /**
@@ -299,7 +299,7 @@ TEXT
                        $statusRootPage = $importer->setTargetRootPage( $this->getOption( 'rootpage' ) );
                        if ( !$statusRootPage->isGood() ) {
                                // Die here so that it doesn't print "Done!"
-                               $this->error( $statusRootPage->getMessage()->text(), 1 );
+                               $this->fatalError( $statusRootPage->getMessage()->text() );
                                return false;
                        }
                }
index e733b9a..526561c 100644 (file)
@@ -133,11 +133,11 @@ class ImportImages extends Maintenance {
 
                # Check Protection
                if ( $this->hasOption( 'protect' ) && $this->hasOption( 'unprotect' ) ) {
-                       $this->error( "Cannot specify both protect and unprotect.  Only 1 is allowed.\n", 1 );
+                       $this->fatalError( "Cannot specify both protect and unprotect.  Only 1 is allowed.\n" );
                }
 
                if ( $this->hasOption( 'protect' ) && trim( $this->getOption( 'protect' ) ) ) {
-                       $this->error( "You must specify a protection option.\n", 1 );
+                       $this->fatalError( "You must specify a protection option.\n" );
                }
 
                # Prepare the list of allowed extensions
@@ -170,7 +170,7 @@ class ImportImages extends Maintenance {
                if ( $commentFile !== null ) {
                        $comment = file_get_contents( $commentFile );
                        if ( $comment === false || $comment === null ) {
-                               $this->error( "failed to read comment file: {$commentFile}\n", 1 );
+                               $this->fatalError( "failed to read comment file: {$commentFile}\n" );
                        }
                } else {
                        $comment = $this->getOption( 'comment', 'Importing file' );
index 816e745..4681003 100644 (file)
@@ -73,7 +73,7 @@ class ImportTextFiles extends Maintenance {
                                        $files[$filename] = file_get_contents( $filename );
                                }
                                if ( !$found ) {
-                                       $this->error( "Fatal error: The file '$arg' does not exist!", 1 );
+                                       $this->fatalError( "Fatal error: The file '$arg' does not exist!" );
                                }
                        }
                };
@@ -88,7 +88,7 @@ class ImportTextFiles extends Maintenance {
                }
 
                if ( !$user ) {
-                       $this->error( "Invalid username\n", true );
+                       $this->fatalError( "Invalid username\n" );
                }
                if ( $user->isAnon() ) {
                        $user->addToDatabase();
@@ -199,7 +199,7 @@ class ImportTextFiles extends Maintenance {
 
                $this->output( "Done! $successCount succeeded, $skipCount skipped.\n" );
                if ( $exit ) {
-                       $this->error( "Import failed with $failCount failed pages.\n", $exit );
+                       $this->fatalError( "Import failed with $failCount failed pages.\n", $exit );
                }
        }
 }
index cac3009..c996530 100644 (file)
@@ -136,7 +136,7 @@ class CommandLineInstaller extends Maintenance {
                        $dbpass = file_get_contents( $dbpassfile ); // returns false on failure
                        MediaWiki\restoreWarnings();
                        if ( $dbpass === false ) {
-                               $this->error( "Couldn't open $dbpassfile", true );
+                               $this->fatalError( "Couldn't open $dbpassfile" );
                        }
                        $this->mOptions['dbpass'] = trim( $dbpass, "\r\n" );
                }
@@ -153,11 +153,11 @@ class CommandLineInstaller extends Maintenance {
                        $pass = file_get_contents( $passfile ); // returns false on failure
                        MediaWiki\restoreWarnings();
                        if ( $pass === false ) {
-                               $this->error( "Couldn't open $passfile", true );
+                               $this->fatalError( "Couldn't open $passfile" );
                        }
                        $this->mOptions['pass'] = trim( $pass, "\r\n" );
                } elseif ( $this->getOption( 'pass' ) === null ) {
-                       $this->error( 'You need to provide the option "pass" or "passfile"', true );
+                       $this->fatalError( 'You need to provide the option "pass" or "passfile"' );
                }
        }
 
index 8f67acd..6e62cd1 100644 (file)
@@ -49,9 +49,9 @@ class InvalidateUserSesssions extends Maintenance {
                $file = $this->getOption( 'file' );
 
                if ( $username === null && $file === null ) {
-                       $this->error( 'Either --user or --file is required', 1 );
+                       $this->fatalError( 'Either --user or --file is required' );
                } elseif ( $username !== null && $file !== null ) {
-                       $this->error( 'Cannot use both --user and --file', 1 );
+                       $this->fatalError( 'Cannot use both --user and --file' );
                }
 
                if ( $username !== null ) {
@@ -60,7 +60,7 @@ class InvalidateUserSesssions extends Maintenance {
                        $usernames = is_readable( $file ) ?
                                file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ) : false;
                        if ( $usernames === false ) {
-                               $this->error( "Could not open $file", 2 );
+                               $this->fatalError( "Could not open $file", 2 );
                        }
                }
 
index ccfece0..141f1ea 100644 (file)
@@ -131,16 +131,14 @@ class GenerateCollationData extends Maintenance {
                                $error .= "* $ucdallURL\n";
                        }
 
-                       $this->error( $error );
-                       exit( 1 );
+                       $this->fatalError( $error );
                }
 
                $debugOutFileName = $this->getOption( 'debug-output' );
                if ( $debugOutFileName ) {
                        $this->debugOutFile = fopen( $debugOutFileName, 'w' );
                        if ( !$this->debugOutFile ) {
-                               $this->error( "Unable to open debug output file for writing" );
-                               exit( 1 );
+                               $this->fatalError( "Unable to open debug output file for writing" );
                        }
                }
                $this->loadUcd();
@@ -205,14 +203,12 @@ class GenerateCollationData extends Maintenance {
        function generateFirstChars() {
                $file = fopen( "{$this->dataDir}/allkeys.txt", 'r' );
                if ( !$file ) {
-                       $this->error( "Unable to open allkeys.txt" );
-                       exit( 1 );
+                       $this->fatalError( "Unable to open allkeys.txt" );
                }
                global $IP;
                $outFile = fopen( "$IP/serialized/first-letters-root.ser", 'w' );
                if ( !$outFile ) {
-                       $this->error( "Unable to open output file first-letters-root.ser" );
-                       exit( 1 );
+                       $this->fatalError( "Unable to open output file first-letters-root.ser" );
                }
 
                $goodTertiaryChars = [];
index 34903de..4338a17 100644 (file)
@@ -46,22 +46,19 @@ class GenerateNormalizerDataAr extends Maintenance {
                if ( !$this->hasOption( 'unicode-data-file' ) ) {
                        $dataFile = 'UnicodeData.txt';
                        if ( !file_exists( $dataFile ) ) {
-                               $this->error( "Unable to find UnicodeData.txt. Please specify " .
+                               $this->fatalError( "Unable to find UnicodeData.txt. Please specify " .
                                        "its location with --unicode-data-file=<FILE>" );
-                               exit( 1 );
                        }
                } else {
                        $dataFile = $this->getOption( 'unicode-data-file' );
                        if ( !file_exists( $dataFile ) ) {
-                               $this->error( 'Unable to find the specified data file.' );
-                               exit( 1 );
+                               $this->fatalError( 'Unable to find the specified data file.' );
                        }
                }
 
                $file = fopen( $dataFile, 'r' );
                if ( !$file ) {
-                       $this->error( 'Unable to open the data file.' );
-                       exit( 1 );
+                       $this->fatalError( 'Unable to open the data file.' );
                }
 
                // For the file format, see http://www.unicode.org/reports/tr44/
index 7c16602..cb989bc 100644 (file)
@@ -40,7 +40,7 @@ class LangMemUsage extends Maintenance {
 
        public function execute() {
                if ( !function_exists( 'memory_get_usage' ) ) {
-                       $this->error( "You must compile PHP with --enable-memory-limit", true );
+                       $this->fatalError( "You must compile PHP with --enable-memory-limit" );
                }
 
                $langtool = new Languages();
index 1effb61..cfd5fc2 100644 (file)
@@ -40,7 +40,7 @@ class MakeTestEdits extends Maintenance {
        public function execute() {
                $user = User::newFromName( $this->getOption( 'user' ) );
                if ( !$user->getId() ) {
-                       $this->error( "No such user exists.", 1 );
+                       $this->fatalError( "No such user exists." );
                }
 
                $count = $this->getOption( 'count' );
index 5f39a3d..c1b038c 100644 (file)
@@ -48,7 +48,7 @@ class ManageJobs extends Maintenance {
                } elseif ( $action === 'repush-abandoned' ) {
                        $this->repushAbandoned( $queue );
                } else {
-                       $this->error( "Invalid action '$action'.", 1 );
+                       $this->fatalError( "Invalid action '$action'." );
                }
        }
 
index 60f94a5..14df53c 100644 (file)
@@ -47,7 +47,7 @@ class McTest extends Maintenance {
                $iterations = $this->getOption( 'i', 100 );
                if ( $cache ) {
                        if ( !isset( $wgObjectCaches[$cache] ) ) {
-                               $this->error( "MediaWiki isn't configured with a cache named '$cache'", 1 );
+                               $this->fatalError( "MediaWiki isn't configured with a cache named '$cache'" );
                        }
                        $servers = $wgObjectCaches[$cache]['servers'];
                } elseif ( $this->hasArg() ) {
@@ -58,7 +58,7 @@ class McTest extends Maintenance {
                } elseif ( isset( $wgObjectCaches[$wgMainCacheType]['servers'] ) ) {
                        $servers = $wgObjectCaches[$wgMainCacheType]['servers'];
                } else {
-                       $this->error( "MediaWiki isn't configured for Memcached usage", 1 );
+                       $this->fatalError( "MediaWiki isn't configured for Memcached usage" );
                }
 
                # find out the longest server string to nicely align output later on
index 8d2534e..b749da4 100644 (file)
@@ -61,8 +61,8 @@ class MergeMessageFileList extends Maintenance {
                        && !$this->hasOption( 'list-file' )
                        && !$this->hasOption( 'extensions-dir' )
                ) {
-                       $this->error( "Either --list-file or --extensions-dir must be provided if " .
-                               "\$wgExtensionEntryPointListFiles is not set", 1 );
+                       $this->fatalError( "Either --list-file or --extensions-dir must be provided if " .
+                               "\$wgExtensionEntryPointListFiles is not set" );
                }
 
                $mmfl = [ 'setupFiles' => [] ];
index b2cce3e..536eddd 100644 (file)
@@ -43,11 +43,11 @@ class MigrateFileRepoLayout extends Maintenance {
        public function execute() {
                $oldLayout = $this->getOption( 'oldlayout' );
                if ( !in_array( $oldLayout, [ 'name', 'sha1' ] ) ) {
-                       $this->error( "Invalid old layout.", 1 );
+                       $this->fatalError( "Invalid old layout." );
                }
                $newLayout = $this->getOption( 'newlayout' );
                if ( !in_array( $newLayout, [ 'name', 'sha1' ] ) ) {
-                       $this->error( "Invalid new layout.", 1 );
+                       $this->fatalError( "Invalid new layout." );
                }
                $since = $this->getOption( 'since' );
 
index ad82542..81c2353 100644 (file)
@@ -48,7 +48,7 @@ class MigrateUserGroup extends Maintenance {
                $end = $dbw->selectField( 'user_groups', 'MAX(ug_user)',
                        [ 'ug_group' => $oldGroup ], __FUNCTION__ );
                if ( $start === null ) {
-                       $this->error( "Nothing to do - no users in the '$oldGroup' group", true );
+                       $this->fatalError( "Nothing to do - no users in the '$oldGroup' group" );
                }
                # Do remaining chunk
                $end += $batchSize - 1;
index 16e4d1c..540a4d9 100644 (file)
@@ -48,14 +48,12 @@ class MinifyScript extends Maintenance {
 
        public function execute() {
                if ( !count( $this->mArgs ) ) {
-                       $this->error( "minify.php: At least one input file must be specified." );
-                       exit( 1 );
+                       $this->fatalError( "minify.php: At least one input file must be specified." );
                }
 
                if ( $this->hasOption( 'outfile' ) ) {
                        if ( count( $this->mArgs ) > 1 ) {
-                               $this->error( '--outfile may only be used with a single input file.' );
-                               exit( 1 );
+                               $this->fatalError( '--outfile may only be used with a single input file.' );
                        }
 
                        // Minify one file
@@ -77,7 +75,7 @@ class MinifyScript extends Maintenance {
                        }
 
                        if ( !file_exists( $inPath ) ) {
-                               $this->error( "File does not exist: $arg", true );
+                               $this->fatalError( "File does not exist: $arg" );
                        }
 
                        $extension = $this->getExtension( $inName );
@@ -95,8 +93,7 @@ class MinifyScript extends Maintenance {
        public function getExtension( $fileName ) {
                $dotPos = strrpos( $fileName, '.' );
                if ( $dotPos === false ) {
-                       $this->error( "No file extension, cannot determine type: $fileName" );
-                       exit( 1 );
+                       $this->fatalError( "No file extension, cannot determine type: $fileName" );
                }
 
                return substr( $fileName, $dotPos + 1 );
@@ -108,13 +105,11 @@ class MinifyScript extends Maintenance {
 
                $inText = file_get_contents( $inPath );
                if ( $inText === false ) {
-                       $this->error( "Unable to open file $inPath for reading." );
-                       exit( 1 );
+                       $this->fatalError( "Unable to open file $inPath for reading." );
                }
                $outFile = fopen( $outPath, 'w' );
                if ( !$outFile ) {
-                       $this->error( "Unable to open file $outPath for writing." );
-                       exit( 1 );
+                       $this->fatalError( "Unable to open file $outPath for writing." );
                }
 
                switch ( $extension ) {
index d578a49..fa25a06 100644 (file)
@@ -73,7 +73,7 @@ class MoveBatch extends Maintenance {
 
                # Setup
                if ( !$file ) {
-                       $this->error( "Unable to read file, exiting", true );
+                       $this->fatalError( "Unable to read file, exiting" );
                }
                if ( $user === false ) {
                        $wgUser = User::newSystemUser( 'Move page script', [ 'steal' => true ] );
@@ -81,7 +81,7 @@ class MoveBatch extends Maintenance {
                        $wgUser = User::newFromName( $user );
                }
                if ( !$wgUser ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
 
                # Setup complete, now start
index 43041a4..9447268 100644 (file)
@@ -138,8 +138,7 @@ class MWDocGen extends Maintenance {
 
                $tmpFile = tempnam( wfTempDir(), 'MWDocGen-' );
                if ( file_put_contents( $tmpFile, $conf ) === false ) {
-                       $this->error( "Could not write doxygen configuration to file $tmpFile\n",
-                               /** exit code: */ 1 );
+                       $this->fatalError( "Could not write doxygen configuration to file $tmpFile\n" );
                }
 
                $command = $this->doxygen . ' ' . $tmpFile;
@@ -161,8 +160,7 @@ TEXT
                );
 
                if ( $exitcode !== 0 ) {
-                       $this->error( "Something went wrong (exit: $exitcode)\n",
-                               $exitcode );
+                       $this->fatalError( "Something went wrong (exit: $exitcode)\n", $exitcode );
                }
        }
 }
index b631005..24ec8cb 100644 (file)
@@ -45,7 +45,7 @@ class PageExists extends Maintenance {
                        $code = 1;
                }
                $this->output( $text );
-               $this->error( '', $code );
+               exit( $code );
        }
 }
 
index 7cc829d..a4fac05 100644 (file)
@@ -51,7 +51,7 @@ class PopulateContentModel extends Maintenance {
 
                $ns = $this->getOption( 'ns' );
                if ( !ctype_digit( $ns ) && $ns !== 'all' ) {
-                       $this->error( 'Invalid namespace', 1 );
+                       $this->fatalError( 'Invalid namespace' );
                }
                $ns = $ns === 'all' ? 'all' : (int)$ns;
                $table = $this->getOption( 'table' );
@@ -64,7 +64,7 @@ class PopulateContentModel extends Maintenance {
                                $this->populatePage( $dbw, $ns );
                                break;
                        default:
-                               $this->error( "Invalid table name: $table", 1 );
+                               $this->fatalError( "Invalid table name: $table" );
                }
        }
 
index 2735a1e..84b65ee 100644 (file)
@@ -76,9 +76,7 @@ class PopulateImageSha1 extends LoggedUpdateMaintenance {
                                __METHOD__
                        );
                        if ( !$res ) {
-                               $this->error( "No such file: $file", true );
-
-                               return false;
+                               $this->fatalError( "No such file: $file" );
                        }
                        $this->output( "Populating img_sha1 field for specified files\n" );
                } else {
index 5de5819..cc1a9f1 100644 (file)
@@ -44,9 +44,9 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
        public function doDBUpdates() {
                $dbw = $this->getDB( DB_MASTER );
                if ( !$dbw->tableExists( 'revision' ) ) {
-                       $this->error( "revision table does not exist", true );
+                       $this->fatalError( "revision table does not exist" );
                } elseif ( !$dbw->tableExists( 'archive' ) ) {
-                       $this->error( "archive table does not exist", true );
+                       $this->fatalError( "archive table does not exist" );
                } elseif ( !$dbw->fieldExists( 'revision', 'rev_len', __METHOD__ ) ) {
                        $this->output( "rev_len column does not exist\n\n", true );
 
index 89eff02..f3506ec 100644 (file)
@@ -45,9 +45,9 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                $db = $this->getDB( DB_MASTER );
 
                if ( !$db->tableExists( 'revision' ) ) {
-                       $this->error( "revision table does not exist", true );
+                       $this->fatalError( "revision table does not exist" );
                } elseif ( !$db->tableExists( 'archive' ) ) {
-                       $this->error( "archive table does not exist", true );
+                       $this->fatalError( "archive table does not exist" );
                } elseif ( !$db->fieldExists( 'revision', 'rev_sha1', __METHOD__ ) ) {
                        $this->output( "rev_sha1 column does not exist\n\n", true );
 
index f6bb253..eae6154 100644 (file)
@@ -59,7 +59,7 @@ class Protect extends Maintenance {
                        $user = User::newFromName( $userName );
                }
                if ( !$user ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
 
                // @todo FIXME: This is reset 7 lines down.
@@ -67,7 +67,7 @@ class Protect extends Maintenance {
 
                $t = Title::newFromText( $this->getArg() );
                if ( !$t ) {
-                       $this->error( "Invalid title", true );
+                       $this->fatalError( "Invalid title" );
                }
 
                $restrictions = [];
index 8e6978d..1035ff5 100644 (file)
@@ -43,25 +43,25 @@ class PruneFileCache extends Maintenance {
                global $wgUseFileCache, $wgFileCacheDirectory;
 
                if ( !$wgUseFileCache ) {
-                       $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true );
+                       $this->fatalError( "Nothing to do -- \$wgUseFileCache is disabled." );
                }
 
                $age = $this->getOption( 'agedays' );
                if ( !ctype_digit( $age ) ) {
-                       $this->error( "Non-integer 'age' parameter given.", true );
+                       $this->fatalError( "Non-integer 'age' parameter given." );
                }
                // Delete items with a TS older than this
                $this->minSurviveTimestamp = time() - ( 86400 * $age );
 
                $dir = $wgFileCacheDirectory;
                if ( !is_dir( $dir ) ) {
-                       $this->error( "Nothing to do -- \$wgFileCacheDirectory directory not found.", true );
+                       $this->fatalError( "Nothing to do -- \$wgFileCacheDirectory directory not found." );
                }
 
                $subDir = $this->getOption( 'subdir' );
                if ( $subDir !== null ) {
                        if ( !is_dir( "$dir/$subDir" ) ) {
-                               $this->error( "The specified subdirectory `$subDir` does not exist.", true );
+                               $this->fatalError( "The specified subdirectory `$subDir` does not exist." );
                        }
                        $this->output( "Pruning `$dir/$subDir` directory...\n" );
                        $this->prune_directory( "$dir/$subDir", 'report' );
index da2d850..716be3a 100644 (file)
@@ -60,7 +60,7 @@ class PurgeParserCache extends Maintenance {
                } elseif ( $inputAge !== null ) {
                        $date = wfTimestamp( TS_MW, time() + $wgParserCacheExpireTime - intval( $inputAge ) );
                } else {
-                       $this->error( "Must specify either --expiredate or --age", 1 );
+                       $this->fatalError( "Must specify either --expiredate or --age" );
                        return;
                }
                $this->usleep = 1e3 * $this->getOption( 'msleep', 0 );
@@ -72,7 +72,7 @@ class PurgeParserCache extends Maintenance {
                $pc = MediaWikiServices::getInstance()->getParserCache()->getCacheStorage();
                $success = $pc->deleteObjectsExpiringBefore( $date, [ $this, 'showProgressAndWait' ] );
                if ( !$success ) {
-                       $this->error( "\nCannot purge this kind of parser cache.", 1 );
+                       $this->fatalError( "\nCannot purge this kind of parser cache." );
                }
                $this->showProgressAndWait( 100 );
                $this->output( "\nDone\n" );
index 7a0e4fc..de09998 100644 (file)
@@ -186,7 +186,7 @@ class ReassignEdits extends Maintenance {
                } else {
                        $user = User::newFromName( $username );
                        if ( !$user ) {
-                               $this->error( "Invalid username", true );
+                               $this->fatalError( "Invalid username" );
                        }
                }
                $user->load();
index 19d8d06..0b5b9b0 100644 (file)
@@ -60,18 +60,18 @@ class RebuildFileCache extends Maintenance {
                global $wgRequestTime;
 
                if ( !$this->enabled ) {
-                       $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true );
+                       $this->fatalError( "Nothing to do -- \$wgUseFileCache is disabled." );
                }
 
                $start = $this->getOption( 'start', "0" );
                if ( !ctype_digit( $start ) ) {
-                       $this->error( "Invalid value for start parameter.", true );
+                       $this->fatalError( "Invalid value for start parameter." );
                }
                $start = intval( $start );
 
                $end = $this->getOption( 'end', "0" );
                if ( !ctype_digit( $end ) ) {
-                       $this->error( "Invalid value for end parameter.", true );
+                       $this->fatalError( "Invalid value for end parameter." );
                }
                $end = intval( $end );
 
@@ -87,7 +87,7 @@ class RebuildFileCache extends Maintenance {
                        ? $end
                        : $dbr->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
                if ( !$start ) {
-                       $this->error( "Nothing to do.", true );
+                       $this->fatalError( "Nothing to do." );
                }
 
                $_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client
index 48602de..8f92420 100644 (file)
@@ -92,7 +92,7 @@ class RebuildLocalisationCache extends Maintenance {
                                explode( ',', $this->getOption( 'lang' ) ) );
                        # Bailed out if nothing is left
                        if ( count( $codes ) == 0 ) {
-                               $this->error( 'None of the languages specified exists.', 1 );
+                               $this->fatalError( 'None of the languages specified exists.' );
                        }
                } else {
                        # By default get all languages
index 230e86d..93e6d47 100644 (file)
@@ -55,7 +55,7 @@ class RebuildSitesCache extends Maintenance {
                        $jsonFile = $this->getConfig()->get( 'SitesCacheFile' );
 
                        if ( $jsonFile === false ) {
-                               $this->error( 'Error: No file set in configuration for SitesCacheFile.', 1 );
+                               $this->fatalError( 'Error: No file set in configuration for SitesCacheFile.' );
                        }
                }
 
index 6d4a4bf..bbf91f5 100644 (file)
@@ -61,7 +61,7 @@ class RebuildRecentchanges extends Maintenance {
                        ( $this->hasOption( 'from' ) && !$this->hasOption( 'to' ) ) ||
                        ( !$this->hasOption( 'from' ) && $this->hasOption( 'to' ) )
                ) {
-                       $this->error( "Both 'from' and 'to' must be given, or neither", 1 );
+                       $this->fatalError( "Both 'from' and 'to' must be given, or neither" );
                }
 
                $this->rebuildRecentChangesTablePass1();
index 5971d5e..0e41ff3 100644 (file)
@@ -56,17 +56,17 @@ class RebuildTextIndex extends Maintenance {
                // Shouldn't be needed for Postgres
                $this->db = $this->getDB( DB_MASTER );
                if ( $this->db->getType() == 'postgres' ) {
-                       $this->error( "This script is not needed when using Postgres.\n", true );
+                       $this->fatalError( "This script is not needed when using Postgres.\n" );
                }
 
                if ( $this->db->getType() == 'sqlite' ) {
                        if ( !DatabaseSqlite::getFulltextSearchModule() ) {
-                               $this->error( "Your version of SQLite module for PHP doesn't "
-                                       . "support full-text search (FTS3).\n", true );
+                               $this->fatalError( "Your version of SQLite module for PHP doesn't "
+                                       . "support full-text search (FTS3).\n" );
                        }
                        if ( !$this->db->checkForEnabledSearch() ) {
-                               $this->error( "Your database schema is not configured for "
-                                       . "full-text search support. Run update.php.\n", true );
+                               $this->fatalError( "Your database schema is not configured for "
+                                       . "full-text search support. Run update.php.\n" );
                        }
                }
 
index b4d75c7..ed6a357 100644 (file)
@@ -75,7 +75,7 @@ TEXT
        public function execute() {
                $this->mode = $this->getOption( 'mode' );
                if ( !in_array( $this->mode, [ 'pages', 'subcats', 'files' ] ) ) {
-                       $this->error( 'Please specify a valid mode: one of "pages", "subcats" or "files".', 1 );
+                       $this->fatalError( 'Please specify a valid mode: one of "pages", "subcats" or "files".' );
                }
 
                $this->minimumId = intval( $this->getOption( 'begin', 0 ) );
index f6c0673..dcfed11 100644 (file)
@@ -108,7 +108,7 @@ class RefreshImageMetadata extends Maintenance {
                $dbw = $this->getDB( DB_MASTER );
                $batchSize = $this->getBatchSize();
                if ( $batchSize <= 0 ) {
-                       $this->error( "Batch size is too low...", 12 );
+                       $this->fatalError( "Batch size is too low...", 12 );
                }
 
                $repo = RepoGroup::singleton()->getLocalRepo();
@@ -255,7 +255,7 @@ class RefreshImageMetadata extends Maintenance {
                }
 
                if ( $brokenOnly && $force ) {
-                       $this->error( 'Cannot use --broken-only and --force together. ', 2 );
+                       $this->fatalError( 'Cannot use --broken-only and --force together. ', 2 );
                }
        }
 }
index cea9e0c..4fab146 100644 (file)
@@ -70,7 +70,7 @@ class RefreshLinks extends Maintenance {
                if ( ( $category = $this->getOption( 'category', false ) ) !== false ) {
                        $title = Title::makeTitleSafe( NS_CATEGORY, $category );
                        if ( !$title ) {
-                               $this->error( "'$category' is an invalid category name!\n", true );
+                               $this->fatalError( "'$category' is an invalid category name!\n" );
                        }
                        $this->refreshCategory( $title );
                } elseif ( ( $category = $this->getOption( 'tracking-category', false ) ) !== false ) {
@@ -485,7 +485,7 @@ class RefreshLinks extends Maintenance {
                if ( isset( $cats[$categoryKey] ) ) {
                        return $cats[$categoryKey]['cats'];
                }
-               $this->error( "Unknown tracking category {$categoryKey}\n", true );
+               $this->fatalError( "Unknown tracking category {$categoryKey}\n" );
        }
 }
 
index c750784..767924f 100644 (file)
@@ -53,7 +53,7 @@ class RemoveUnusedAccounts extends Maintenance {
                }
                $touched = $this->getOption( 'ignore-touched', "1" );
                if ( !ctype_digit( $touched ) ) {
-                       $this->error( "Please put a valid positive integer on the --ignore-touched parameter.", true );
+                       $this->fatalError( "Please put a valid positive integer on the --ignore-touched parameter." );
                }
                $touchedSeconds = 86400 * $touched;
                foreach ( $res as $row ) {
index 2772f04..6aefc39 100644 (file)
@@ -62,7 +62,7 @@ class RenameDbPrefix extends Maintenance {
                }
 
                if ( $old === false || $new === false ) {
-                       $this->error( "Invalid prefix!", true );
+                       $this->fatalError( "Invalid prefix!" );
                }
                if ( $old === $new ) {
                        $this->output( "Same prefix. Nothing to rename!\n", true );
index 8d0873f..f59ce6c 100644 (file)
@@ -48,12 +48,12 @@ class ResetUserEmail extends Maintenance {
                        $user = User::newFromName( $userName );
                }
                if ( !$user || !$user->getId() || !$user->loadFromId() ) {
-                       $this->error( "Error: user '$userName' does not exist\n", 1 );
+                       $this->fatalError( "Error: user '$userName' does not exist\n" );
                }
 
                $email = $this->getArg( 1 );
                if ( !Sanitizer::validateEmail( $email ) ) {
-                       $this->error( "Error: email '$email' is not valid\n", 1 );
+                       $this->fatalError( "Error: email '$email' is not valid\n" );
                }
 
                // Code from https://wikitech.wikimedia.org/wiki/Password_reset
index 5ad7d4e..ca9abb1 100644 (file)
@@ -50,7 +50,7 @@ class RollbackEdits extends Maintenance {
                $user = $this->getOption( 'user' );
                $username = User::isIP( $user ) ? $user : User::getCanonicalName( $user );
                if ( !$username ) {
-                       $this->error( 'Invalid username', true );
+                       $this->fatalError( 'Invalid username' );
                }
 
                $bot = $this->hasOption( 'bot' );
index af2a318..0874538 100644 (file)
@@ -59,7 +59,7 @@ class RunJobs extends Maintenance {
                if ( $this->hasOption( 'procs' ) ) {
                        $procs = intval( $this->getOption( 'procs' ) );
                        if ( $procs < 1 || $procs > 1000 ) {
-                               $this->error( "Invalid argument to --procs", true );
+                               $this->fatalError( "Invalid argument to --procs" );
                        } elseif ( $procs != 1 ) {
                                $fc = new ForkController( $procs );
                                if ( $fc->start() != 'child' ) {
index 65c353a..75b2e22 100644 (file)
@@ -58,7 +58,7 @@ class MediaWikiShell extends Maintenance {
 
        public function execute() {
                if ( !class_exists( \Psy\Shell::class ) ) {
-                       $this->error( 'PsySH not found. Please run composer with the --dev option.', 1 );
+                       $this->fatalError( 'PsySH not found. Please run composer with the --dev option.' );
                }
 
                $traverser = new \PhpParser\NodeTraverser();
index 36e55f3..8e276e7 100644 (file)
@@ -72,7 +72,7 @@ class MwSql extends Maintenance {
                                }
                        }
                        if ( $index === null ) {
-                               $this->error( "No replica DB server configured with the name '$replicaDB'.", 1 );
+                               $this->fatalError( "No replica DB server configured with the name '$replicaDB'." );
                        }
                } else {
                        $index = DB_MASTER;
@@ -81,7 +81,7 @@ class MwSql extends Maintenance {
                /** @var IDatabase $db DB handle for the appropriate cluster/wiki */
                $db = $lb->getConnection( $index, [], $wiki );
                if ( $replicaDB != '' && $db->getLBInfo( 'master' ) !== null ) {
-                       $this->error( "The server selected ({$db->getServer()}) is not a replica DB.", 1 );
+                       $this->fatalError( "The server selected ({$db->getServer()}) is not a replica DB." );
                }
 
                if ( $index === DB_MASTER ) {
@@ -92,12 +92,12 @@ class MwSql extends Maintenance {
                if ( $this->hasArg( 0 ) ) {
                        $file = fopen( $this->getArg( 0 ), 'r' );
                        if ( !$file ) {
-                               $this->error( "Unable to open input file", true );
+                               $this->fatalError( "Unable to open input file" );
                        }
 
                        $error = $db->sourceStream( $file, null, [ $this, 'sqlPrintResult' ] );
                        if ( $error !== true ) {
-                               $this->error( $error, true );
+                               $this->fatalError( $error );
                        } else {
                                exit( 0 );
                        }
@@ -157,7 +157,11 @@ class MwSql extends Maintenance {
                        $res = $db->query( $line );
                        $this->sqlPrintResult( $res, $db );
                } catch ( DBQueryError $e ) {
-                       $this->error( $e, $dieOnError );
+                       if ( $dieOnError ) {
+                               $this->fatalError( $e );
+                       } else {
+                               $this->error( $e );
+                       }
                }
        }
 
index e74a86c..fbde417 100644 (file)
@@ -83,7 +83,7 @@ class SqliteMaintenance extends Maintenance {
        private function vacuum() {
                $prevSize = filesize( $this->db->getDbFilePath() );
                if ( $prevSize == 0 ) {
-                       $this->error( "Can't vacuum an empty database.\n", true );
+                       $this->fatalError( "Can't vacuum an empty database.\n" );
                }
 
                $this->output( 'VACUUM: ' );
index 9045870..acf0103 100644 (file)
@@ -134,24 +134,24 @@ class CheckStorage {
                                        // This is a known bug from 2004
                                        // It's safe to just erase the old_flags field
                                        if ( $fix ) {
-                                               $this->error( 'fixed', "Warning: old_flags set to 0", $id );
+                                               $this->addError( 'fixed', "Warning: old_flags set to 0", $id );
                                                $dbw = wfGetDB( DB_MASTER );
                                                $dbw->ping();
                                                $dbw->update( 'text', [ 'old_flags' => '' ],
                                                        [ 'old_id' => $id ], __METHOD__ );
                                                echo "Fixed\n";
                                        } else {
-                                               $this->error( 'fixable', "Warning: old_flags set to 0", $id );
+                                               $this->addError( 'fixable', "Warning: old_flags set to 0", $id );
                                        }
                                } elseif ( count( array_diff( $flagArray, $knownFlags ) ) ) {
-                                       $this->error( 'unfixable', "Error: invalid flags field \"$flags\"", $id );
+                                       $this->addError( 'unfixable', "Error: invalid flags field \"$flags\"", $id );
                                }
                        }
                        $dbr->freeResult( $res );
 
                        // Output errors for any missing text rows
                        foreach ( $missingTextRows as $oldId => $revId ) {
-                               $this->error( 'restore revision', "Error: missing text row", $oldId );
+                               $this->addError( 'restore revision', "Error: missing text row", $oldId );
                        }
 
                        // Verify external revisions
@@ -163,12 +163,15 @@ class CheckStorage {
                                foreach ( $res as $row ) {
                                        $urlParts = explode( '://', $row->old_text, 2 );
                                        if ( count( $urlParts ) !== 2 || $urlParts[1] == '' ) {
-                                               $this->error( 'restore text', "Error: invalid URL \"{$row->old_text}\"", $row->old_id );
+                                               $this->addError( 'restore text', "Error: invalid URL \"{$row->old_text}\"", $row->old_id );
                                                continue;
                                        }
                                        list( $proto, ) = $urlParts;
                                        if ( $proto != 'DB' ) {
-                                               $this->error( 'restore text', "Error: invalid external protocol \"$proto\"", $row->old_id );
+                                               $this->addError(
+                                                       'restore text',
+                                                       "Error: invalid external protocol \"$proto\"",
+                                                       $row->old_id );
                                                continue;
                                        }
                                        $path = explode( '/', $row->old_text );
@@ -204,7 +207,10 @@ class CheckStorage {
                                        $extDb->freeResult( $res );
                                        // Print errors for missing blobs rows
                                        foreach ( $xBlobIds as $blobId => $oldId ) {
-                                               $this->error( 'restore text', "Error: missing target $blobId for one-part ES URL", $oldId );
+                                               $this->addError(
+                                                       'restore text',
+                                                       "Error: missing target $blobId for one-part ES URL",
+                                                       $oldId );
                                        }
                                }
                        }
@@ -225,13 +231,13 @@ class CheckStorage {
                                        $oldId = $row->old_id;
                                        $matches = [];
                                        if ( !preg_match( '/^O:(\d+):"(\w+)"/', $row->header, $matches ) ) {
-                                               $this->error( 'restore text', "Error: invalid object header", $oldId );
+                                               $this->addError( 'restore text', "Error: invalid object header", $oldId );
                                                continue;
                                        }
 
                                        $className = strtolower( $matches[2] );
                                        if ( strlen( $className ) != $matches[1] ) {
-                                               $this->error(
+                                               $this->addError(
                                                        'restore text',
                                                        "Error: invalid object header, wrong class name length",
                                                        $oldId
@@ -249,12 +255,12 @@ class CheckStorage {
                                                case 'historyblobstub':
                                                case 'historyblobcurstub':
                                                        if ( strlen( $row->header ) == $headerLength ) {
-                                                               $this->error( 'unfixable', "Error: overlong stub header", $oldId );
+                                                               $this->addError( 'unfixable', "Error: overlong stub header", $oldId );
                                                                continue;
                                                        }
                                                        $stubObj = unserialize( $row->header );
                                                        if ( !is_object( $stubObj ) ) {
-                                                               $this->error( 'restore text', "Error: unable to unserialize stub object", $oldId );
+                                                               $this->addError( 'restore text', "Error: unable to unserialize stub object", $oldId );
                                                                continue;
                                                        }
                                                        if ( $className == 'historyblobstub' ) {
@@ -264,7 +270,7 @@ class CheckStorage {
                                                        }
                                                        break;
                                                default:
-                                                       $this->error( 'unfixable', "Error: unrecognised object class \"$className\"", $oldId );
+                                                       $this->addError( 'unfixable', "Error: unrecognised object class \"$className\"", $oldId );
                                        }
                                }
                                $dbr->freeResult( $res );
@@ -287,7 +293,7 @@ class CheckStorage {
                                                if ( in_array( 'object', $flags ) ) {
                                                        $urlParts = explode( '/', $row->header );
                                                        if ( $urlParts[0] != 'DB:' ) {
-                                                               $this->error(
+                                                               $this->addError(
                                                                        'unfixable',
                                                                        "Error: unrecognised external storage type \"{$urlParts[0]}",
                                                                        $row->old_id
@@ -303,7 +309,7 @@ class CheckStorage {
                                                                );
                                                        }
                                                } else {
-                                                       $this->error(
+                                                       $this->addError(
                                                                'unfixable',
                                                                "Error: invalid flags \"{$row->old_flags}\" on concat bulk row {$row->old_id}",
                                                                $concatBlobs[$row->old_id] );
@@ -312,7 +318,7 @@ class CheckStorage {
                                                substr( $row->header, 0, strlen( self::CONCAT_HEADER ) ),
                                                self::CONCAT_HEADER
                                        ) ) {
-                                               $this->error(
+                                               $this->addError(
                                                        'restore text',
                                                        "Error: Incorrect object header for concat bulk row {$row->old_id}",
                                                        $concatBlobs[$row->old_id]
@@ -357,7 +363,7 @@ class CheckStorage {
                }
        }
 
-       function error( $type, $msg, $ids ) {
+       function addError( $type, $msg, $ids ) {
                if ( is_array( $ids ) && count( $ids ) == 1 ) {
                        $ids = reset( $ids );
                }
@@ -399,7 +405,7 @@ class CheckStorage {
                                [ 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ], __METHOD__ );
                        foreach ( $res as $row ) {
                                if ( strcasecmp( $row->header, self::CONCAT_HEADER ) ) {
-                                       $this->error(
+                                       $this->addError(
                                                'restore text',
                                                "Error: invalid header on target $cluster/{$row->blob_id} of two-part ES URL",
                                                $oldIds[$row->blob_id]
@@ -411,7 +417,7 @@ class CheckStorage {
 
                        // Print errors for missing blobs rows
                        foreach ( $oldIds as $blobId => $oldIds2 ) {
-                               $this->error(
+                               $this->addError(
                                        'restore text',
                                        "Error: missing target $cluster/$blobId for two-part ES URL",
                                        $oldIds2
index c17ce99..0ae46ae 100644 (file)
@@ -103,8 +103,8 @@ class CompressOld extends Maintenance {
        public function execute() {
                global $wgDBname;
                if ( !function_exists( "gzdeflate" ) ) {
-                       $this->error( "You must enable zlib support in PHP to compress old revisions!\n" .
-                               "Please see http://www.php.net/manual/en/ref.zlib.php\n", true );
+                       $this->fatalError( "You must enable zlib support in PHP to compress old revisions!\n" .
+                               "Please see http://www.php.net/manual/en/ref.zlib.php\n" );
                }
 
                $type = $this->getOption( 'type', 'concat' );
index 437bfcd..d39acd4 100644 (file)
@@ -43,7 +43,7 @@ class DumpRev extends Maintenance {
                        [ 'old_id=rev_text_id', 'rev_id' => $this->getArg() ]
                );
                if ( !$row ) {
-                       $this->error( "Row not found", true );
+                       $this->fatalError( "Row not found" );
                }
 
                $flags = explode( ',', $row->old_flags );
index d7d0b84..eb72915 100644 (file)
@@ -45,7 +45,7 @@ class OrphanStats extends Maintenance {
        public function execute() {
                $dbr = $this->getDB( DB_REPLICA );
                if ( !$dbr->tableExists( 'blob_orphans' ) ) {
-                       $this->error( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first", true );
+                       $this->fatalError( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first" );
                }
                $res = $dbr->select( 'blob_orphans', '*', false, __METHOD__ );
 
index 154f54e..68afde7 100644 (file)
@@ -54,7 +54,7 @@ class SyncFileBackend extends Maintenance {
                if ( $this->hasOption( 'posdump' ) ) {
                        // Just dump the current position into the specified position dir
                        if ( !$this->hasOption( 'posdir' ) ) {
-                               $this->error( "Param posdir required!", 1 );
+                               $this->fatalError( "Param posdir required!" );
                        }
                        if ( $this->hasOption( 'postime' ) ) {
                                $id = (int)$src->getJournal()->getPositionAtTime( $this->getOption( 'postime' ) );
@@ -76,7 +76,7 @@ class SyncFileBackend extends Maintenance {
                }
 
                if ( !$this->hasOption( 'dst' ) ) {
-                       $this->error( "Param dst required!", 1 );
+                       $this->fatalError( "Param dst required!" );
                }
                $dst = FileBackendGroup::singleton()->get( $this->getOption( 'dst' ) );
 
@@ -156,7 +156,7 @@ class SyncFileBackend extends Maintenance {
                $first = true; // first batch
 
                if ( $start > $end ) { // sanity
-                       $this->error( "Error: given starting ID greater than ending ID.", 1 );
+                       $this->fatalError( "Error: given starting ID greater than ending ID." );
                }
 
                $next = null;
index c2d5c2c..278b68d 100644 (file)
@@ -41,7 +41,7 @@ class Undelete extends Maintenance {
 
                $title = Title::newFromText( $pageName );
                if ( !$title ) {
-                       $this->error( "Invalid title", true );
+                       $this->fatalError( "Invalid title" );
                }
                if ( $user === false ) {
                        $wgUser = User::newSystemUser( 'Command line script', [ 'steal' => true ] );
@@ -49,7 +49,7 @@ class Undelete extends Maintenance {
                        $wgUser = User::newFromName( $user );
                }
                if ( !$wgUser ) {
-                       $this->error( "Invalid username", true );
+                       $this->fatalError( "Invalid username" );
                }
                $archive = new PageArchive( $title, RequestContext::getMain()->getConfig() );
                $this->output( "Undeleting " . $title->getPrefixedDBkey() . '...' );
index f8f5dcd..529c069 100755 (executable)
@@ -66,23 +66,21 @@ class UpdateMediaWiki extends Maintenance {
 
                list( $pcreVersion ) = explode( ' ', PCRE_VERSION, 2 );
                if ( version_compare( $pcreVersion, $minimumPcreVersion, '<' ) ) {
-                       $this->error(
+                       $this->fatalError(
                                "PCRE $minimumPcreVersion or later is required.\n" .
                                "Your PHP binary is linked with PCRE $pcreVersion.\n\n" .
                                "More information:\n" .
                                "https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE\n\n" .
-                               "ABORTING.\n",
-                               true );
+                               "ABORTING.\n" );
                }
 
                $test = new PhpXmlBugTester();
                if ( !$test->ok ) {
-                       $this->error(
+                       $this->fatalError(
                                "Your system has a combination of PHP and libxml2 versions that is buggy\n" .
                                "and can cause hidden data corruption in MediaWiki and other web apps.\n" .
                                "Upgrade to libxml2 2.7.3 or later.\n" .
-                               "ABORTING (see https://bugs.php.net/bug.php?id=45996).\n",
-                               true );
+                               "ABORTING (see https://bugs.php.net/bug.php?id=45996).\n" );
                }
        }
 
@@ -94,22 +92,22 @@ class UpdateMediaWiki extends Maintenance {
                                || $this->hasOption( 'schema' )
                                || $this->hasOption( 'noschema' ) )
                ) {
-                       $this->error( "Do not run update.php on this wiki. If you're seeing this you should\n"
+                       $this->fatalError( "Do not run update.php on this wiki. If you're seeing this you should\n"
                                . "probably ask for some help in performing your schema updates or use\n"
                                . "the --noschema and --schema options to get an SQL file for someone\n"
                                . "else to inspect and run.\n\n"
-                               . "If you know what you are doing, you can continue with --force\n", true );
+                               . "If you know what you are doing, you can continue with --force\n" );
                }
 
                $this->fileHandle = null;
                if ( substr( $this->getOption( 'schema' ), 0, 2 ) === "--" ) {
-                       $this->error( "The --schema option requires a file as an argument.\n", true );
+                       $this->fatalError( "The --schema option requires a file as an argument.\n" );
                } elseif ( $this->hasOption( 'schema' ) ) {
                        $file = $this->getOption( 'schema' );
                        $this->fileHandle = fopen( $file, "w" );
                        if ( $this->fileHandle === false ) {
                                $err = error_get_last();
-                               $this->error( "Problem opening the schema file for writing: $file\n\t{$err['message']}", true );
+                               $this->fatalError( "Problem opening the schema file for writing: $file\n\t{$err['message']}" );
                        }
                }
 
@@ -152,7 +150,7 @@ class UpdateMediaWiki extends Maintenance {
                if ( !$status->isOK() ) {
                        // This might output some wikitext like <strong> but it should be comprehensible
                        $text = $status->getWikiText();
-                       $this->error( $text, 1 );
+                       $this->fatalError( $text );
                }
 
                $this->output( "Going to run database updates for " . wfWikiID() . "\n" );
index cb2f125..12056c8 100644 (file)
@@ -53,7 +53,7 @@ class UpdateDoubleWidthSearch extends Maintenance {
 
                $dbw = $this->getDB( DB_MASTER );
                if ( $dbw->getType() !== 'mysql' ) {
-                       $this->error( "This change is only needed on MySQL, quitting.\n", true );
+                       $this->fatalError( "This change is only needed on MySQL, quitting.\n" );
                }
 
                $res = $this->findRows( $dbw );
index 427769f..264f4be 100644 (file)
@@ -14,12 +14,12 @@ class UpdateExtensionJsonSchema extends Maintenance {
        public function execute() {
                $filename = $this->getArg( 0 );
                if ( !is_readable( $filename ) ) {
-                       $this->error( "Error: Unable to read $filename", 1 );
+                       $this->fatalError( "Error: Unable to read $filename" );
                }
 
                $json = FormatJson::decode( file_get_contents( $filename ), true );
                if ( $json === null ) {
-                       $this->error( "Error: Invalid JSON", 1 );
+                       $this->fatalError( "Error: Invalid JSON" );
                }
 
                if ( !isset( $json['manifest_version'] ) ) {
index 334ed27..c0b7b10 100644 (file)
@@ -43,12 +43,12 @@ class UpdateRestrictions extends Maintenance {
                $db = $this->getDB( DB_MASTER );
                $batchSize = $this->getBatchSize();
                if ( !$db->tableExists( 'page_restrictions' ) ) {
-                       $this->error( "page_restrictions table does not exist", true );
+                       $this->fatalError( "page_restrictions table does not exist" );
                }
 
                $start = $db->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
                if ( !$start ) {
-                       $this->error( "Nothing to do.", true );
+                       $this->fatalError( "Nothing to do." );
                }
                $end = $db->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
 
index e2c0c61..58f23df 100644 (file)
@@ -72,7 +72,7 @@ class UpdateSpecialPages extends Maintenance {
                                $queryPage = $specialObj;
                        } else {
                                $class = get_class( $specialObj );
-                               $this->error( "$class is not an instance of QueryPage.\n", 1 );
+                               $this->fatalError( "$class is not an instance of QueryPage.\n" );
                                die;
                        }
 
index 7cf16b6..5692f11 100644 (file)
@@ -102,7 +102,7 @@ The new option is NOT validated.' );
                        // Get the options and update stats
                        if ( $option ) {
                                if ( !array_key_exists( $option, $defaultOptions ) ) {
-                                       $this->error( "Invalid user option. Use --list to see valid choices\n", 1 );
+                                       $this->fatalError( "Invalid user option. Use --list to see valid choices\n" );
                                }
 
                                $userValue = $user->getOption( $option );
index aa1f668..ea27a7e 100644 (file)
@@ -9,7 +9,7 @@ class ValidateRegistrationFile extends Maintenance {
        }
        public function execute() {
                $validator = new ExtensionJsonValidator( function ( $msg ) {
-                       $this->error( $msg, 1 );
+                       $this->fatalError( $msg );
                } );
                $validator->checkDependencies();
                $path = $this->getArg( 0 );
@@ -17,7 +17,7 @@ class ValidateRegistrationFile extends Maintenance {
                        $validator->validate( $path );
                        $this->output( "$path validates against the schema!\n" );
                } catch ( ExtensionJsonValidationError $e ) {
-                       $this->error( $e->getMessage(), 1 );
+                       $this->fatalError( $e->getMessage() );
                }
        }
 }
index af7eb2d..8c0237f 100644 (file)
@@ -38,17 +38,17 @@ class ViewCLI extends Maintenance {
        public function execute() {
                $title = Title::newFromText( $this->getArg() );
                if ( !$title ) {
-                       $this->error( "Invalid title", true );
+                       $this->fatalError( "Invalid title" );
                }
 
                $page = WikiPage::factory( $title );
 
                $content = $page->getContent( Revision::RAW );
                if ( !$content ) {
-                       $this->error( "Page has no content", true );
+                       $this->fatalError( "Page has no content" );
                }
                if ( !$content instanceof TextContent ) {
-                       $this->error( "Non-text content models not supported", true );
+                       $this->fatalError( "Non-text content models not supported" );
                }
 
                $this->output( $content->getNativeData() );
index 6a601ad..94bd3cb 100644 (file)
@@ -51,12 +51,12 @@ class WrapOldPasswords extends Maintenance {
 
                // Check that type exists and is a layered type
                if ( !isset( $typeInfo[$layeredType] ) ) {
-                       $this->error( 'Undefined password type', true );
+                       $this->fatalError( 'Undefined password type' );
                }
 
                $passObj = $passwordFactory->newFromType( $layeredType );
                if ( !$passObj instanceof LayeredParameterizedPassword ) {
-                       $this->error( 'Layered parameterized password type must be used.', true );
+                       $this->fatalError( 'Layered parameterized password type must be used.' );
                }
 
                // Extract the first layer type
index faca87d..2878eca 100644 (file)
@@ -2,6 +2,7 @@
   "private": true,
   "scripts": {
     "test": "grunt test",
+    "qunit": "grunt qunit",
     "doc": "jsduck",
     "postdoc": "grunt copy:jsduck",
     "selenium": "killall -0 chromedriver 2>/dev/null || chromedriver --url-base=/wd/hub --port=4444 & grunt webdriver:test; killall chromedriver"
index d9fa8e0..a5bfbc5 100644 (file)
@@ -1922,7 +1922,10 @@ return [
                ],
        ],
        'mediawiki.special' => [
-               'styles' => 'resources/src/mediawiki.special/mediawiki.special.css',
+               'styles' => [
+                       'resources/src/mediawiki.special/mediawiki.special.css',
+                       'resources/src/mediawiki.special/mediawiki.special.userrights.css',
+               ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.special.apisandbox.styles' => [
@@ -1972,6 +1975,8 @@ return [
                        'api-help-param-integer-minmax',
                        'api-help-param-multi-separate',
                        'api-help-param-multi-max',
+                       'api-help-param-maxbytes',
+                       'api-help-param-maxchars',
                        'apisandbox-submit-invalid-fields-title',
                        'apisandbox-submit-invalid-fields-message',
                        'apisandbox-results',
@@ -2235,9 +2240,6 @@ return [
                        'mediawiki.notification.convertmessagebox',
                ],
        ],
-       'mediawiki.special.userrights.styles' => [
-               'styles' => 'resources/src/mediawiki.special/mediawiki.special.userrights.css',
-       ],
        'mediawiki.special.watchlist' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.watchlist.js',
                'messages' => [
@@ -2747,6 +2749,7 @@ return [
                        'oojs-ui.styles.icons-moderation',
                ],
                'messages' => [
+                       'ooui-item-remove',
                        'ooui-outline-control-move-down',
                        'ooui-outline-control-move-up',
                        'ooui-outline-control-remove',
index 5386291..0cec3ff 100644 (file)
         * Reset to default filters
         */
        mw.rcfilters.Controller.prototype.resetToDefaults = function () {
-               if ( this.applyParamChange( this._getDefaultParams() ) ) {
+               var params = this._getDefaultParams();
+               if ( this.applyParamChange( params ) ) {
                        // Only update the changes list if there was a change to actual filters
                        this.updateChangesList();
+               } else {
+                       this.uriProcessor.updateURL( params );
                }
        };
 
                if ( this.applyParamChange( {} ) ) {
                        // Only update the changes list if there was a change to actual filters
                        this.updateChangesList();
+               } else {
+                       this.uriProcessor.updateURL();
                }
 
                if ( highlightedFilterNames ) {
                if ( this.applyParamChange( params ) ) {
                        // Update changes list only if there was a difference in filter selection
                        this.updateChangesList();
+               } else {
+                       this.uriProcessor.updateURL( params );
                }
 
                // Log filter grouping
index 621c200..fe806ed 100644 (file)
@@ -63,7 +63,8 @@
         * @return {mw.Uri} Updated Uri
         */
        mw.rcfilters.UriProcessor.prototype.getUpdatedUri = function ( uriQuery ) {
-               var uri = new mw.Uri(),
+               var titlePieces,
+                       uri = new mw.Uri(),
                        unrecognizedParams = this.getUnrecognizedParams( uriQuery || uri.query );
 
                if ( uriQuery ) {
                        uri.query = uriQuery;
                }
 
+               // Normalize subpage to use &target= so we are always
+               // consistent in Special:RecentChangesLinked between the
+               // ?title=Special:RecentChangesLinked/TargetPage and
+               // ?title=Special:RecentChangesLinked&target=TargetPage
+               if ( uri.query.title && uri.query.title.indexOf( '/' ) !== -1 ) {
+                       titlePieces = uri.query.title.split( '/' );
+
+                       unrecognizedParams.title = titlePieces.shift();
+                       unrecognizedParams.target = titlePieces.join( '/' );
+               }
+
                uri.query = this.filtersModel.getMinimizedParamRepresentation(
                        $.extend(
                                true,
@@ -87,7 +99,6 @@
 
                // Reapply unrecognized params and url version
                uri.query = $.extend( true, {}, uri.query, unrecognizedParams, { urlversion: '2' } );
-
                return uri;
        };
 
index c62685a..ed51c34 100644 (file)
                                                                } ) );
                                                        }
                                                }
+                                               if ( 'maxbytes' in pi.parameters[ i ] ) {
+                                                       descriptionContainer.append( $( '<div>', {
+                                                               addClass: 'info',
+                                                               append: Util.parseMsg( 'api-help-param-maxbytes', pi.parameters[ i ].maxbytes )
+                                                       } ) );
+                                               }
+                                               if ( 'maxchars' in pi.parameters[ i ] ) {
+                                                       descriptionContainer.append( $( '<div>', {
+                                                               addClass: 'info',
+                                                               append: Util.parseMsg( 'api-help-param-maxchars', pi.parameters[ i ].maxchars )
+                                                       } ) );
+                                               }
                                                helpField = new OO.ui.FieldLayout(
                                                        new OO.ui.Widget( {
                                                                $content: '\xa0',
index acfdb56..1ffdf70 100644 (file)
 .mw-userrights-groups * th {
        text-align: left;
 }
+
+/* Dynamically show/hide the expiry selection underneath each checkbox */
+input.mw-userrights-groupcheckbox:not( :checked ) ~ .mw-userrights-nested {
+       display: none;
+}
+
+/* Initial hide the expiry fields to prevent a FOUC on loading */
+/* The input fields gets unhidden by JavaScript when needed */
+.client-js .mw-userrights-expiryfield {
+       display: none;
+}
index 3f864dd..d3494f7 100644 (file)
@@ -6,13 +6,8 @@
        // Replace successbox with notifications
        convertmessagebox();
 
-       // Dynamically show/hide the expiry selection underneath each checkbox
-       $( '#mw-userrights-form2 input[type=checkbox]' ).on( 'change', function ( e ) {
-               $( '#mw-userrights-nested-' + e.target.id ).toggle( e.target.checked );
-       } ).trigger( 'change' );
-
-       // Also dynamically show/hide the "other time" input under each dropdown
+       // Dynamically show/hide the "other time" input under each dropdown
        $( '.mw-userrights-nested select' ).on( 'change', function ( e ) {
                $( e.target.parentNode ).find( 'input' ).toggle( $( e.target ).val() === 'other' );
-       } ).trigger( 'change' );
+       } );
 }( jQuery ) );
index a505cde..7af3a36 100644 (file)
@@ -15181,7 +15181,7 @@ parsoid=wt2html,wt2wt,html2html
 <div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="135" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"></a></div>lang=invalid:language:code</div></div></div>
 
 !! html/parsoid
-<figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/f/ff/Foobar.svg" data-file-width="240" data-file-height="180" data-file-type="drawing" height="165" width="220"/></a><figcaption>lang=invalid.language.code</figcaption></figure>
+<figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/thumb/f/ff/Foobar.svg/220px-Foobar.svg.png" data-file-width="240" data-file-height="180" data-file-type="drawing" height="165" width="220"/></a><figcaption>lang=invalid:language:code</figcaption></figure>
 !! end
 
 !! test
@@ -17774,7 +17774,7 @@ T4304: HTML attribute safety (link)
 !! wikitext
 <div title="[[Main Page]]"></div>
 !! html
-<div title="&#91;&#91;Main Page]]"></div>
+<div title="&#91;&#91;Main Page&#93;&#93;"></div>
 
 !! end
 
@@ -17837,7 +17837,7 @@ T4304: HTML attribute safety (named web link)
 !! wikitext
 <div title="[http://example.com/ link]"></div>
 !! html
-<div title="&#91;http&#58;//example.com/ link]"></div>
+<div title="&#91;http&#58;//example.com/ link&#93;"></div>
 
 !! end
 
@@ -18549,10 +18549,14 @@ language=sr variant=sr-el
 -{H|foAjrjvi=>sr-el:" onload="alert(1)" data-foo="}-
 
 [[File:Foobar.jpg|alt=-{}-foAjrjvi-{}-]]
-!! html
+!! html/php
 <p>
 </p><p><a href="/wiki/%D0%94%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0:Foobar.jpg" class="image"><img alt="&quot; onload=&quot;alert(1)&quot; data-foo=&quot;" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
 </p>
+!! html/parsoid
+<p><meta typeof="mw:LanguageVariant" data-mw-variant='{"add":true,"oneway":[{"f":"foAjrjvi","l":"sr-el","t":"\" onload=\"alert(1)\" data-foo=\""}]}'/></p>
+
+<p><span class="mw-default-size" typeof="mw:Image"><a href="./Датотека:Foobar.jpg"><img alt="foAjrjvi" resource="./Датотека:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"alt":"foAjrjvi","resource":"./Датотека:Foobar.jpg","height":"220","width":"1941"},"sa":{"alt":"alt=-{}-foAjrjvi-{}-","resource":"File:Foobar.jpg"}}'/></a></span></p>
 !! end
 
 !! test
@@ -29705,3 +29709,438 @@ wgFragmentMode=[ 'html5', 'legacy' ]
 <p><a href="#Foo_bar">#Foo&#160;bar</a>
 </p>
 !! end
+
+!! test
+T51672: Test for brackets in attributes of elements in external link texts
+!! wikitext
+[http://example.com/ link <span title="title with [brackets]">span</span>]
+[http://example.com/ link <span title="title with &#91;brackets&#93;">span</span>]
+
+!! html/php
+<p><a rel="nofollow" class="external text" href="http://example.com/">link <span title="title with &#91;brackets&#93;">span</span></a>
+<a rel="nofollow" class="external text" href="http://example.com/">link <span title="title with &#91;brackets&#93;">span</span></a>
+</p>
+!! end
+
+!! test
+T72875: Test for brackets in attributes of elements in internal link texts
+!! wikitext
+[[Foo|link <span title="title with [[double brackets]]">span</span>]]
+[[Foo|link <span title="title with &#91;&#91;double brackets&#93;&#93;">span</span>]]
+
+!! html/php
+<p><a href="/wiki/Foo" title="Foo">link <span title="title with &#91;&#91;double brackets&#93;&#93;">span</span></a>
+<a href="/wiki/Foo" title="Foo">link <span title="title with &#91;&#91;double brackets&#93;&#93;">span</span></a>
+</p>
+!! end
+
+!! test
+T179544: {{anchorencode:}} output should be always usable in links
+!! config
+wgFragmentMode=[ 'html5' ]
+!! wikitext
+<span id="{{anchorencode:[foo]}}"></span>[[#{{anchorencode:[foo]}}]]
+!! html/php
+<p><span id="&#91;foo&#93;"></span><a href="#[foo]">#&#91;foo&#93;</a>
+</p>
+!! end
+
+## ------------------------------
+## Parsoid section-wrapping tests
+## ------------------------------
+!! test
+Section wrapping for well-nested sections (no leading content)
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+= 1 =
+a
+
+= 2 =
+b
+
+== 2.1 ==
+c
+
+== 2.2 ==
+d
+
+=== 2.2.1 ===
+e
+
+= 3 =
+f
+!! html/parsoid
+<section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+</section><section data-mw-section-id="2"><h1 id="2"> 2 </h1>
+<p>b</p>
+
+<section data-mw-section-id="3"><h2 id="2.1"> 2.1 </h2>
+<p>c</p>
+
+</section><section data-mw-section-id="4"><h2 id="2.2"> 2.2 </h2>
+<p>d</p>
+
+<section data-mw-section-id="5"><h3 id="2.2.1"> 2.2.1 </h3>
+<p>e</p>
+
+</section></section></section><section data-mw-section-id="6"><h1 id="3"> 3 </h1>
+<p>f</p>
+
+</section>
+!! end
+
+!! test
+Section wrapping for well-nested sections (with leading content)
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+Para 1.
+
+Para 2 with a <div>nested in it</div>
+
+Para 3.
+
+= 1 =
+a
+
+= 2 =
+b
+
+== 2.1 ==
+c
+!! html/parsoid
+<section data-mw-section-id="0"><p>Para 1.</p>
+
+<p>Para 2 with a </p><div>nested in it</div>
+
+<p>Para 3.</p>
+
+</section><section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+</section><section data-mw-section-id="2"><h1 id="2"> 2 </h1>
+<p>b</p>
+
+<section data-mw-section-id="3"><h2 id="2.1"> 2.1 </h2>
+<p>c</p>
+
+</section></section>
+!! end
+
+!! test
+Section wrapping with template-generated sections (good nesting 1)
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+= 1 =
+a
+
+{{echo|1=
+== 1.1 ==
+b
+}}
+
+== 1.2 ==
+c
+
+= 2 =
+d
+!! html/parsoid
+<section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+<section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.1" data-parsoid='{"dsr":[9,33,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.1 ==\nb"}},"i":0}}]}'> 1.1 </h2><span about="#mwt1">
+</span><p about="#mwt1">b</p>
+</section><section data-mw-section-id="3"><h2 id="1.2"> 1.2 </h2>
+<p>c</p>
+
+</section></section><section data-mw-section-id="4"><h1 id="2"> 2 </h1>
+<p>d</p></section>
+!! end
+
+# In this example, the template scope is mildly expanded to incorporate the
+# trailing newline after the transclusion since that is part of section 1.1.1
+!! test
+Section wrapping with template-generated sections (good nesting 2)
+!! options
+parsoid={
+  "wrapSections": true,
+  "modes": ["wt2html", "wt2wt"]
+}
+!! wikitext
+= 1 =
+a
+
+{{echo|1=
+== 1.1 ==
+b
+=== 1.1.1 ===
+d
+}}
+= 2 =
+e
+!! html/parsoid
+<section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+<section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.1" data-parsoid='{"dsr":[9,50,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.1 ==\nb\n=== 1.1.1 ===\nd"}},"i":0}},"\n"]}'> 1.1 </h2><span about="#mwt1">
+</span><p about="#mwt1">b</p><span about="#mwt1">
+</span><section data-mw-section-id="-1" about="#mwt1"><h3 about="#mwt1" id="1.1.1"> 1.1.1 </h3><span about="#mwt1">
+</span><p about="#mwt1">d</p><span about="#mwt1">
+</span></section></section></section><section data-mw-section-id="4" data-parsoid="{}"><h1 id="2"> 2 </h1>
+<p>e</p></section>
+!! end
+
+# In this example, the template scope is mildly expanded to incorporate the
+# trailing newline after the transclusion since that is part of section 1.2.1
+!! test
+Section wrapping with template-generated sections (good nesting 3)
+!! options
+parsoid={
+  "wrapSections": true,
+  "modes": ["wt2html", "wt2wt"]
+}
+!! wikitext
+= 1 =
+a
+
+{{echo|1=
+x
+== 1.1 ==
+b
+==1.2==
+c
+===1.2.1===
+d
+}}
+= 2 =
+e
+!! html/parsoid
+<section data-mw-section-id="1" data-parsoid="{}"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+<p about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[9,60,0,0],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"x\n== 1.1 ==\nb\n==1.2==\nc\n===1.2.1===\nd"}},"i":0}},"\n"]}'>x</p><span about="#mwt1">
+</span><section data-mw-section-id="-1" about="#mwt1"><h2 about="#mwt1" id="1.1"> 1.1 </h2><span about="#mwt1">
+</span><p about="#mwt1">b</p><span about="#mwt1">
+</span></section><section data-mw-section-id="-1" about="#mwt1"><h2 about="#mwt1" id="1.2">1.2</h2><span about="#mwt1">
+</span><p about="#mwt1">c</p><span about="#mwt1">
+</span><section data-mw-section-id="-1" about="#mwt1"><h3 about="#mwt1" id="1.2.1">1.2.1</h3><span about="#mwt1">
+</span><p about="#mwt1">d</p><span about="#mwt1">
+</span></section></section></section><section data-mw-section-id="5"><h1 id="2"> 2 </h1>
+<p>e</p></section>
+!! end
+
+# Because of section-wrapping and template-wrapping interactions,
+# the scope of the template is expanded so that the template markup
+# is valid in the presence of <section> tags.
+!! test
+Section wrapping with template-generated sections (bad nesting 1)
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+= 1 =
+a
+
+{{echo|1=
+= 2 =
+b
+== 2.1 ==
+c
+}}
+
+d
+
+= 3 =
+e
+!! html/parsoid
+<section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+</section><section data-mw-section-id="-1"><h1 about="#mwt1" typeof="mw:Transclusion" id="2" data-parsoid='{"dsr":[9,45,null,null],"pi":[[{"k":"1","named":true,"spc":["","","\n","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"= 2 =\nb\n== 2.1 ==\nc"}},"i":0}},"\n\nd\n\n"]}'> 2 </h1><span about="#mwt1">
+</span><p about="#mwt1">b</p><span about="#mwt1">
+</span><section data-mw-section-id="-1" about="#mwt1"><h2 about="#mwt1" id="2.1"> 2.1 </h2><span about="#mwt1">
+</span><p about="#mwt1">c</p><span about="#mwt1">
+
+</span><p about="#mwt1">d</p><span about="#mwt1">
+
+</span></section></section><section data-mw-section-id="4"><h1 id="3"> 3 </h1>
+<p>e</p></section>
+!! end
+
+# Because of section-wrapping and template-wrapping interactions,
+# additional template wrappers are added to <section> tags
+# so that template wrapping semantics are valid whether section
+# tags are retained or stripped. But, the template scope can expand
+# greatly when accounting for section tags.
+!! test
+Section wrapping with template-generated sections (bad nesting 2)
+!! options
+parsoid={
+  "wrapSections": true,
+  "modes": ["wt2html", "wt2wt"]
+}
+!! wikitext
+= 1 =
+a
+
+{{echo|1=
+== 1.2 ==
+b
+= 2 =
+c
+}}
+
+d
+
+= 3 =
+e
+!! html/parsoid
+<section data-mw-section-id="1" about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":["= 1 =\na\n\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.2 ==\nb\n= 2 =\nc"}},"i":0}},"\n\nd\n\n"]}'><h1 id="1"> 1 </h1>
+<p>a</p>
+
+<section data-mw-section-id="-1"><h2 about="#mwt1" typeof="mw:Transclusion" id="1.2" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"== 1.2 ==\nb\n= 2 =\nc"}},"i":0}}]}'> 1.2 </h2><span about="#mwt1">
+</span><p about="#mwt1">b</p><span about="#mwt1">
+</span></section></section><section data-mw-section-id="-1" about="#mwt1"><h1 about="#mwt1" id="2"> 2 </h1><span about="#mwt1">
+</span><p about="#mwt1">c</p>
+
+<p>d</p>
+</section><section data-mw-section-id="4" data-parsoid="{}"><h1 id="3"> 3 </h1>
+<p>e</p></section>
+!! end
+
+!! test
+Section wrapping with uneditable lead section + div wrapping multiple sections
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+foo
+
+<div style="border:1px solid red;">
+= 1 =
+a
+
+== 1.1 ==
+b
+
+= 2 =
+c
+</div>
+
+= 3 =
+d
+
+== 3.1 ==
+e
+!! html/parsoid
+<section data-mw-section-id="-1"><p>foo</p>
+
+</section><section data-mw-section-id="-2"><div style="border:1px solid red;">
+<section data-mw-section-id="1"><h1 id="1"> 1 </h1>
+<p>a</p>
+
+<section data-mw-section-id="2"><h2 id="1.1"> 1.1 </h2>
+<p>b</p>
+
+</section></section><section data-mw-section-id="-1"><h1 id="2"> 2 </h1>
+<p>c</p>
+</section></div>
+
+</section><section data-mw-section-id="4"><h1 id="3"> 3 </h1>
+<p>d</p>
+
+<section data-mw-section-id="5"><h2 id="3.1"> 3.1 </h2>
+<p>e</p>
+</section></section>
+!! end
+
+!! test
+Section wrapping with editable lead section + div overlapping multiple sections
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+foo
+
+= 1 =
+a
+<div style="border:1px solid red;">
+b
+
+== 1.1 ==
+c
+
+= 2 =
+d
+</div>
+e
+
+= 3 =
+f
+
+== 3.1 ==
+g
+!! html/parsoid
+<section data-mw-section-id="0"><p>foo</p>
+
+</section><section data-mw-section-id="-1"><h1 id="1"> 1 </h1>
+<p>a</p>
+</section><section data-mw-section-id="-2"><div style="border:1px solid red;">
+<p>b</p>
+
+<section data-mw-section-id="2"><h2 id="1.1"> 1.1 </h2>
+<p>c</p>
+
+</section><section data-mw-section-id="-1"><h1 id="2"> 2 </h1>
+<p>d</p>
+</section></div>
+<p>e</p>
+
+</section><section data-mw-section-id="4"><h1 id="3"> 3 </h1>
+<p>f</p>
+
+<section data-mw-section-id="5"><h2 id="3.1"> 3.1 </h2>
+<p>g</p>
+</section></section>
+!! end
+
+!! test
+HTML header tags should not be wrapped in section tags
+!! options
+parsoid={
+  "wrapSections": true
+}
+!! wikitext
+foo
+
+<h1>a</h1>
+
+= b =
+
+<h1>c</h1>
+
+= d =
+!! html/parsoid
+<section data-mw-section-id="0"><p>foo</p>
+
+<h1 id="a" data-parsoid='{"stx":"html"}'>a</h1>
+
+</section><section data-mw-section-id="1"><h1 id="b"> b </h1>
+
+<h1 id="c" data-parsoid='{"stx":"html"}'>c</h1>
+
+</section><section data-mw-section-id="2"><h1 id="d"> d </h1></section>
+!! end
index f04eec7..4d3c37b 100644 (file)
@@ -968,12 +968,13 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         * @since 1.18
         */
        public function needsDB() {
-               # if the test says it uses database tables, it needs the database
+               // If the test says it uses database tables, it needs the database
                if ( $this->tablesUsed ) {
                        return true;
                }
 
-               # if the test says it belongs to the Database group, it needs the database
+               // If the test class says it belongs to the Database group, it needs the database.
+               // NOTE: This ONLY checks for the group in the class level doc comment.
                $rc = new ReflectionClass( $this );
                if ( preg_match( '/@group +Database/im', $rc->getDocComment() ) ) {
                        return true;
index a4e3bb9..aaa135d 100644 (file)
@@ -193,7 +193,6 @@ class ExtraParserTest extends MediaWikiTestCase {
        }
 
        /**
-        * @group Database
         * @covers Parser::parse
         */
        public function testTrackingCategory() {
@@ -207,7 +206,6 @@ class ExtraParserTest extends MediaWikiTestCase {
        }
 
        /**
-        * @group Database
         * @covers Parser::parse
         */
        public function testTrackingCategorySpecial() {
index f3d4916..e867f5e 100644 (file)
@@ -447,6 +447,47 @@ class HtmlTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers Html::warningBox
+        * @covers Html::messageBox
+        */
+       public function testWarningBox() {
+               $this->assertEquals(
+                       Html::warningBox( 'warn' ),
+                       '<div class="warningbox">warn</div>'
+               );
+       }
+
+       /**
+        * @covers Html::errorBox
+        * @covers Html::messageBox
+        */
+       public function testErrorBox() {
+               $this->assertEquals(
+                       Html::errorBox( 'err' ),
+                       '<div class="errorbox">err</div>'
+               );
+               $this->assertEquals(
+                       Html::errorBox( 'err', 'heading' ),
+                       '<div class="errorbox"><h2>heading</h2>err</div>'
+               );
+       }
+
+       /**
+        * @covers Html::successBox
+        * @covers Html::messageBox
+        */
+       public function testSuccessBox() {
+               $this->assertEquals(
+                       Html::successBox( 'great' ),
+                       '<div class="successbox">great</div>'
+               );
+               $this->assertEquals(
+                       Html::successBox( '<script>beware no escaping!</script>' ),
+                       '<div class="successbox"><script>beware no escaping!</script></div>'
+               );
+       }
+
        /**
         * List of input element types values introduced by HTML5
         * Full list at https://www.w3.org/TR/html-markup/input.html
index 912bffe..f99cccd 100644 (file)
@@ -2,6 +2,9 @@
 
 use Wikimedia\TestingAccessWrapper;
 
+/**
+ * @group Database
+ */
 class MessageTest extends MediaWikiLangTestCase {
 
        protected function setUp() {
@@ -467,7 +470,6 @@ class MessageTest extends MediaWikiLangTestCase {
 
        /**
         * FIXME: This should not need database, but Language#formatExpiry does (T57912)
-        * @group Database
         * @covers Message::expiryParam
         * @covers Message::expiryParams
         */
index b0febe8..5a92b99 100644 (file)
@@ -282,7 +282,6 @@ class TitleTest extends MediaWikiTestCase {
        /**
         * Auth-less test of Title::isValidMoveOperation
         *
-        * @group Database
         * @param string $source
         * @param string $target
         * @param array|string|bool $expected Required error
index b290f8f..1601493 100644 (file)
@@ -212,7 +212,6 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataIsCountable
-        * @group Database
         * @covers TextContent::isCountable
         */
        public function testIsCountable( $text, $hasLinks, $mode, $expected ) {
index 3a8f4db..57aeb20 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Wikimedia\TestingAccessWrapper;
+
 /**
  * @covers DifferenceEngine
  *
@@ -117,4 +119,30 @@ class DifferenceEngineTest extends MediaWikiTestCase {
                $this->assertEquals( $revs[2], $diffEngine->getNewid(), 'diff get new id' );
        }
 
+       public function provideLocaliseTitleTooltipsTestData() {
+               return [
+                       'moved paragraph left shoud get new location title' => [
+                               '<a class="mw-diff-movedpara-left">⚫</a>',
+                               '<a class="mw-diff-movedpara-left" title="(diff-paragraph-moved-tonew)">⚫</a>',
+                       ],
+                       'moved paragraph right shoud get old location title' => [
+                               '<a class="mw-diff-movedpara-right">⚫</a>',
+                               '<a class="mw-diff-movedpara-right" title="(diff-paragraph-moved-toold)">⚫</a>',
+                       ],
+                       'nothing changed when key not hit' => [
+                               '<a class="mw-diff-movedpara-rightis">⚫</a>',
+                               '<a class="mw-diff-movedpara-rightis">⚫</a>',
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideLocaliseTitleTooltipsTestData
+        */
+       public function testAddLocalisedTitleTooltips( $input, $expected ) {
+               $this->setContentLang( 'qqx' );
+               $diffEngine = TestingAccessWrapper::newFromObject( new DifferenceEngine() );
+               $this->assertEquals( $expected, $diffEngine->addLocalisedTitleTooltips( $input ) );
+       }
+
 }
index 36b6d64..9584d4b 100644 (file)
@@ -1,12 +1,8 @@
 <?php
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
 
 class InstallDocFormatterTest extends MediaWikiTestCase {
        /**
-        * @covers InstallDocFormatter::format
+        * @covers InstallDocFormatter
         * @dataProvider provideDocFormattingTests
         */
        public function testFormat( $expected, $unformattedText, $message = '' ) {
index 934d49a..592f72a 100644 (file)
@@ -275,6 +275,66 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                ];
        }
 
+       public function testPreemtiveRefresh() {
+               $value = 'KatCafe';
+               $wasSet = 0;
+               $func = function ( $old, &$ttl, &$opts, $asOf ) use ( &$wasSet, $value )
+               {
+                       ++$wasSet;
+                       return $value;
+               };
+
+               $cache = new NearExpiringWANObjectCache( [
+                       'cache'   => new HashBagOStuff(),
+                       'pool'    => 'empty'
+               ] );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'lowTTL' => 30 ];
+               $v = $cache->getWithSetCallback( $key, 20, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $v = $cache->getWithSetCallback( $key, 20, $func, $opts );
+               $this->assertEquals( 2, $wasSet, "Value re-calculated" );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'lowTTL' => 1 ];
+               $v = $cache->getWithSetCallback( $key, 30, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $v = $cache->getWithSetCallback( $key, 30, $func, $opts );
+               $this->assertEquals( 1, $wasSet, "Value cached" );
+
+               $cache = new PopularityRefreshingWANObjectCache( [
+                       'cache'   => new HashBagOStuff(),
+                       'pool'    => 'empty'
+               ] );
+
+               $now = microtime( true ); // reference time
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'hotTTR' => 900 ];
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $cache->setTime( $now + 30 );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( 1, $wasSet, "Value cached" );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'hotTTR' => 10 ];
+               $cache->setTime( $now );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $cache->setTime( $now + 30 );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( 2, $wasSet, "Value re-calculated" );
+       }
+
        /**
         * @covers WANObjectCache::getWithSetCallback()
         * @covers WANObjectCache::doGetWithSetCallback()
@@ -1346,3 +1406,44 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $class, $wanCache->determineKeyClass( $key ) );
        }
 }
+
+class TimeAdjustableHashBagOStuff extends HashBagOStuff {
+       private $timeOverride = 0;
+
+       public function setTime( $time ) {
+               $this->timeOverride = $time;
+       }
+
+       protected function getCurrentTime() {
+               return $this->timeOverride ?: parent::getCurrentTime();
+       }
+}
+
+class TimeAdjustableWANObjectCache extends WANObjectCache {
+       private $timeOverride = 0;
+
+       public function setTime( $time ) {
+               $this->timeOverride = $time;
+               if ( $this->cache instanceof TimeAdjustableHashBagOStuff ) {
+                       $this->cache->setTime( $time );
+               }
+       }
+
+       protected function getCurrentTime() {
+               return $this->timeOverride ?: parent::getCurrentTime();
+       }
+}
+
+class NearExpiringWANObjectCache extends TimeAdjustableWANObjectCache {
+       const CLOCK_SKEW = 1;
+
+       protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
+               return ( ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
+       }
+}
+
+class PopularityRefreshingWANObjectCache extends TimeAdjustableWANObjectCache {
+       protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now ) {
+               return ( ( $now - $asOf ) > $timeTillRefresh );
+       }
+}
index 7814b83..9cb2f94 100644 (file)
@@ -42,7 +42,7 @@ class MemcachedBagOStuffTest extends MediaWikiTestCase {
                );
 
                $this->assertEquals(
-                       'test:##dc89dcb43b28614da27660240af478b5',
+                       'test:BagOStuff-long-key:##dc89dcb43b28614da27660240af478b5',
                        $this->cache->makeKey( '𝕖𝕧𝕖𝕟', '𝕚𝕗', '𝕨𝕖', '𝕄𝔻𝟝', '𝕖𝕒𝕔𝕙',
                                '𝕒𝕣𝕘𝕦𝕞𝕖𝕟𝕥', '𝕥𝕙𝕚𝕤', '𝕜𝕖𝕪', '𝕨𝕠𝕦𝕝𝕕', '𝕤𝕥𝕚𝕝𝕝', '𝕓𝕖', '𝕥𝕠𝕠', '𝕝𝕠𝕟𝕘' )
                );
diff --git a/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php b/tests/phpunit/includes/page/WikiPageTestContentHandlerUseDB.php
deleted file mode 100644 (file)
index 3db7628..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- */
-class WikiPageTestContentHandlerUseDB extends WikiPageTest {
-
-       protected function setUp() {
-               parent::setUp();
-               $this->setMwGlobals( 'wgContentHandlerUseDB', false );
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $page_table = $dbw->tableName( 'page' );
-               $revision_table = $dbw->tableName( 'revision' );
-               $archive_table = $dbw->tableName( 'archive' );
-
-               if ( $dbw->fieldExists( $page_table, 'page_content_model' ) ) {
-                       $dbw->query( "alter table $page_table drop column page_content_model" );
-                       $dbw->query( "alter table $revision_table drop column rev_content_model" );
-                       $dbw->query( "alter table $revision_table drop column rev_content_format" );
-                       $dbw->query( "alter table $archive_table drop column ar_content_model" );
-                       $dbw->query( "alter table $archive_table drop column ar_content_format" );
-               }
-       }
-
-       /**
-        * @covers WikiPage::getContentModel
-        */
-       public function testGetContentModel() {
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentModel",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               $page = new WikiPage( $page->getTitle() );
-
-               // NOTE: since the content model is not recorded in the database,
-               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
-               $this->assertEquals( CONTENT_MODEL_WIKITEXT, $page->getContentModel() );
-       }
-
-       /**
-        * @covers WikiPage::getContentHandler
-        */
-       public function testGetContentHandler() {
-               $page = $this->createPage(
-                       "WikiPageTest_testGetContentHandler",
-                       "some text",
-                       CONTENT_MODEL_JAVASCRIPT
-               );
-
-               // NOTE: since the content model is not recorded in the database,
-               //       we expect to get the default, namely CONTENT_MODEL_WIKITEXT
-               $page = new WikiPage( $page->getTitle() );
-               $this->assertEquals( 'WikitextContentHandler', get_class( $page->getContentHandler() ) );
-       }
-}
diff --git a/tests/phpunit/languages/classes/LanguageCrhTest.php b/tests/phpunit/languages/classes/LanguageCrhTest.php
new file mode 100644 (file)
index 0000000..f34288c
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+class LanguageCrhTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return [
+                       [ // general words, covering more of the alphabet
+                               [
+                                       'crh'      => 'рузгярнынъ ruzgârnıñ Париж Parij',
+                                       'crh-cyrl' => 'рузгярнынъ рузгярнынъ Париж Париж',
+                                       'crh-latn' => 'ruzgârnıñ ruzgârnıñ Parij Parij',
+                               ],
+                               'рузгярнынъ ruzgârnıñ Париж Parij'
+                       ],
+                       [ // general words, covering more of the alphabet
+                               [
+                                       'crh'      => 'чёкюч çöküç элифбени elifbeni полициясы politsiyası',
+                                       'crh-cyrl' => 'чёкюч чёкюч элифбени элифбени полициясы полициясы',
+                                       'crh-latn' => 'çöküç çöküç elifbeni elifbeni politsiyası politsiyası',
+                               ],
+                               'чёкюч çöküç элифбени elifbeni полициясы politsiyası'
+                       ],
+                       [ // general words, covering more of the alphabet
+                               [
+                                       'crh'      => 'хусусында hususında акъшамларны aqşamlarnı опькеленюв öpkelenüv',
+                                       'crh-cyrl' => 'хусусында хусусында акъшамларны акъшамларны опькеленюв опькеленюв',
+                                       'crh-latn' => 'hususında hususında aqşamlarnı aqşamlarnı öpkelenüv öpkelenüv',
+                               ],
+                               'хусусында hususında акъшамларны aqşamlarnı опькеленюв öpkelenüv'
+                       ],
+                       [ // general words, covering more of the alphabet
+                               [
+                                       'crh'      => 'кулюмсиреди külümsiredi айтмайджагъым aytmaycağım козьяшсыз közyaşsız',
+                                       'crh-cyrl' => 'кулюмсиреди кулюмсиреди айтмайджагъым айтмайджагъым козьяшсыз козьяшсыз',
+                                       'crh-latn' => 'külümsiredi külümsiredi aytmaycağım aytmaycağım közyaşsız közyaşsız',
+                               ],
+                               'кулюмсиреди külümsiredi айтмайджагъым aytmaycağım козьяшсыз közyaşsız'
+                       ],
+                       [ // exception words
+                               [
+                                       'crh'      => 'инструменталь instrumental гургуль gürgül тюшюнмемек tüşünmemek',
+                                       'crh-cyrl' => 'инструменталь инструменталь гургуль гургуль тюшюнмемек тюшюнмемек',
+                                       'crh-latn' => 'instrumental instrumental gürgül gürgül tüşünmemek tüşünmemek',
+                               ],
+                               'инструменталь instrumental гургуль gürgül тюшюнмемек tüşünmemek'
+                       ],
+                       [ // multi part words
+                               [
+                                       'crh'      => 'эки юз eki yüz',
+                                       'crh-cyrl' => 'эки юз эки юз',
+                                       'crh-latn' => 'eki yüz eki yüz',
+                               ],
+                               'эки юз eki yüz'
+                       ],
+                       [ // ALL CAPS, made up acronyms
+                               [
+                                       'crh'      => 'ÑAB QIC ĞUK COT НЪАБ КЪЫДж ГЪУК ДЖОТ CA ДЖА',
+                                       'crh-cyrl' => 'НЪАБ КЪЫДж ГЪУК ДЖОТ НЪАБ КЪЫДж ГЪУК ДЖОТ ДЖА ДЖА',
+                                       'crh-latn' => 'ÑAB QIC ĞUK COT ÑAB QIC ĞUK COT CA CA',
+                               ],
+                               'ÑAB QIC ĞUK COT НЪАБ КЪЫДж ГЪУК ДЖОТ CA ДЖА'
+                       ],
+               ];
+       }
+}
index 7912f97..cbc74f4 100644 (file)
@@ -182,8 +182,7 @@ class ApiStructureTest extends MediaWikiTestCase {
 
                foreach ( [ $paramsPlain, $paramsForHelp ] as $params ) {
                        foreach ( $params as $param => $config ) {
-                               if (
-                                       isset( $config[ApiBase::PARAM_ISMULTI_LIMIT1] )
+                               if ( isset( $config[ApiBase::PARAM_ISMULTI_LIMIT1] )
                                        || isset( $config[ApiBase::PARAM_ISMULTI_LIMIT2] )
                                ) {
                                        $this->assertTrue( !empty( $config[ApiBase::PARAM_ISMULTI] ), $param
@@ -199,6 +198,15 @@ class ApiStructureTest extends MediaWikiTestCase {
                                                $config[ApiBase::PARAM_ISMULTI_LIMIT2], $param
                                                . 'PARAM_ISMULTI limit cannot be smaller for users with apihighlimits rights' );
                                }
+                               if ( isset( $config[ApiBase::PARAM_MAX_BYTES] )
+                                       || isset( $config[ApiBase::PARAM_MAX_CHARS] )
+                               ) {
+                                       $default = isset( $config[ApiBase::PARAM_DFLT] ) ? $config[ApiBase::PARAM_DFLT] : null;
+                                       $type = isset( $config[ApiBase::PARAM_TYPE] ) ? $config[ApiBase::PARAM_TYPE]
+                                               : gettype( $default );
+                                       $this->assertContains( $type, [ 'NULL', 'string', 'text', 'password' ],
+                                               'PARAM_MAX_BYTES/CHARS is only supported for string-like types' );
+                               }
                        }
                }
        }