Merge "objectcache: allow for callbacks to mask SYNC_WRITE latency"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 1 Sep 2016 21:38:21 +0000 (21:38 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 1 Sep 2016 21:38:21 +0000 (21:38 +0000)
71 files changed:
RELEASE-NOTES-1.28
includes/Linker.php
includes/MediaWiki.php
includes/MediaWikiServices.php
includes/Setup.php
includes/api/ApiParamInfo.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/ja.json
includes/api/i18n/oc.json
includes/api/i18n/qqq.json
includes/api/i18n/sv.json
includes/api/i18n/uk.json
includes/content/WikiTextStructure.php
includes/content/WikitextContentHandler.php
includes/db/DBConnRef.php
includes/db/Database.php
includes/db/IDatabase.php
includes/db/loadbalancer/LBFactory.php
includes/db/loadbalancer/LBFactoryMulti.php
includes/db/loadbalancer/LBFactorySimple.php
includes/db/loadbalancer/LoadBalancer.php
includes/deferred/DeferredUpdates.php
includes/deferred/LinksDeletionUpdate.php
includes/deferred/LinksUpdate.php
includes/deferred/SqlDataUpdate.php
includes/filerepo/file/LocalFile.php
includes/installer/i18n/diq.json
includes/installer/i18n/ne.json
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/search/SearchIndexField.php
includes/specials/SpecialEmailuser.php
languages/i18n/ar.json
languages/i18n/be-tarask.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dty.json
languages/i18n/eo.json
languages/i18n/eu.json
languages/i18n/gu.json
languages/i18n/io.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/kiu.json
languages/i18n/lv.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/oc.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vo.json
languages/i18n/wa.json
languages/i18n/yi.json
maintenance/Maintenance.php
maintenance/doMaintenance.php
resources/Resources.php
resources/src/mediawiki/mediawiki.util.js
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/content/WikitextStructureTest.php
tests/phpunit/includes/db/DatabaseTest.php
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index 6639a95..fa7d9ca 100644 (file)
@@ -96,6 +96,8 @@ production.
   ApiPageSet may contain entries where the 'from' value is percent-encoded as
   the raw value cannot be represented in a valid API response. These are
   indicated by a 'fromencoded' boolean alongside the existing 'from' parameter.
+* (T28680) action=paraminfo can now return info about all submodules of a
+  module without listing them all explicitly.
 
 === Action API internal changes in 1.28 ===
 * Added a new hook, 'ApiMakeParserOptions', to allow extensions to better
@@ -126,6 +128,8 @@ changes to languages because of Phabricator reports.
   login and visiting the login page while already logged in.
 * ResourceLoader::makeLoaderURL() was removed (deprecated since 1.24).
 * $.fn.liveAndTestAtStart was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyPrefix was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyRegexp was removed (deprecated since 1.24).
 * Linker::link() and Linker::linkKnown() were deprecated; please instead use
   MediaWiki\Linker\LinkRenderer. In addition, the LinkBegin and LinkEnd hooks
   were replaced by HtmlPageLinkRendererBegin and HtmlPageLinkRendererEnd
index 2b38a96..f8b1bae 100644 (file)
@@ -993,9 +993,10 @@ class Linker {
                        $page = Title::makeTitle( NS_USER, $userName );
                }
 
+               // Wrap the output with <bdi> tags for directionality isolation
                return self::link(
                        $page,
-                       htmlspecialchars( $altUserName !== false ? $altUserName : $userName ),
+                       '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>',
                        [ 'class' => $classes ]
                );
        }
index cded064..1add24a 100644 (file)
@@ -822,16 +822,18 @@ class MediaWiki {
 
                $runJobsLogger = LoggerFactory::getInstance( 'runJobs' );
 
+               // Fall back to running the job(s) while the user waits if needed
                if ( !$this->config->get( 'RunJobsAsync' ) ) {
-                       // Fall back to running the job here while the user waits
                        $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
+                       $runner->run( [ 'maxJobs' => $n ] );
                        return;
                }
 
+               // Do not send request if there are probably no jobs
                try {
-                       if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
-                               return; // do not send request if there are probably no jobs
+                       $group = JobQueueGroup::singleton();
+                       if ( !$group->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
+                               return;
                        }
                } catch ( JobQueueError $e ) {
                        MWExceptionHandler::logException( $e );
@@ -845,8 +847,7 @@ class MediaWiki {
 
                $errno = $errstr = null;
                $info = wfParseUrl( $this->config->get( 'CanonicalServer' ) );
-               MediaWiki\suppressWarnings();
-               $host = $info['host'];
+               $host = $info ? $info['host'] : null;
                $port = 80;
                if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
                        $host = "tls://" . $host;
@@ -855,48 +856,60 @@ class MediaWiki {
                if ( isset( $info['port'] ) ) {
                        $port = $info['port'];
                }
-               $sock = fsockopen(
+
+               MediaWiki\suppressWarnings();
+               $sock = $host ? fsockopen(
                        $host,
                        $port,
                        $errno,
                        $errstr,
-                       // If it takes more than 100ms to connect to ourselves there
-                       // is a problem elsewhere.
-                       0.1
-               );
+                       // If it takes more than 100ms to connect to ourselves there is a problem...
+                       0.100
+               ) : false;
                MediaWiki\restoreWarnings();
-               if ( !$sock ) {
+
+               $invokedWithSuccess = true;
+               if ( $sock ) {
+                       $special = SpecialPageFactory::getPage( 'RunJobs' );
+                       $url = $special->getPageTitle()->getCanonicalURL( $query );
+                       $req = (
+                               "POST $url HTTP/1.1\r\n" .
+                               "Host: {$info['host']}\r\n" .
+                               "Connection: Close\r\n" .
+                               "Content-Length: 0\r\n\r\n"
+                       );
+
+                       $runJobsLogger->info( "Running $n job(s) via '$url'" );
+                       // Send a cron API request to be performed in the background.
+                       // Give up if this takes too long to send (which should be rare).
+                       stream_set_timeout( $sock, 2 );
+                       $bytes = fwrite( $sock, $req );
+                       if ( $bytes !== strlen( $req ) ) {
+                               $invokedWithSuccess = false;
+                               $runJobsLogger->error( "Failed to start cron API (socket write error)" );
+                       } else {
+                               // Do not wait for the response (the script should handle client aborts).
+                               // Make sure that we don't close before that script reaches ignore_user_abort().
+                               $start = microtime( true );
+                               $status = fgets( $sock );
+                               $sec = microtime( true ) - $start;
+                               if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
+                                       $invokedWithSuccess = false;
+                                       $runJobsLogger->error( "Failed to start cron API: received '$status' ($sec)" );
+                               }
+                       }
+                       fclose( $sock );
+               } else {
+                       $invokedWithSuccess = false;
                        $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
-                       // Fall back to running the job here while the user waits
-                       $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
-                       return;
                }
 
-               $special = SpecialPageFactory::getPage( 'RunJobs' );
-               $url = $special->getPageTitle()->getCanonicalURL( $query );
-               $req = (
-                       "POST $url HTTP/1.1\r\n" .
-                       "Host: {$info['host']}\r\n" .
-                       "Connection: Close\r\n" .
-                       "Content-Length: 0\r\n\r\n"
-               );
+               // Fall back to running the job(s) while the user waits if needed
+               if ( !$invokedWithSuccess ) {
+                       $runJobsLogger->warning( "Jobs switched to blocking; Special:RunJobs disabled" );
 
-               $runJobsLogger->info( "Running $n job(s) via '$url'" );
-               // Send a cron API request to be performed in the background.
-               // Give up if this takes too long to send (which should be rare).
-               stream_set_timeout( $sock, 2 );
-               $bytes = fwrite( $sock, $req );
-               if ( $bytes !== strlen( $req ) ) {
-                       $runJobsLogger->error( "Failed to start cron API (socket write error)" );
-               } else {
-                       // Do not wait for the response (the script should handle client aborts).
-                       // Make sure that we don't close before that script reaches ignore_user_abort().
-                       $status = fgets( $sock );
-                       if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
-                               $runJobsLogger->error( "Failed to start cron API: received '$status'" );
-                       }
+                       $runner = new JobRunner( $runJobsLogger );
+                       $runner->run( [ 'maxJobs'  => $n ] );
                }
-               fclose( $sock );
        }
 }
index 037e96f..f621867 100644 (file)
@@ -16,6 +16,7 @@ use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Services\SalvageableService;
 use MediaWiki\Services\ServiceContainer;
+use MediaWiki\Services\NoSuchServiceException;
 use MWException;
 use ObjectCache;
 use SearchEngine;
index cbe4e2e..97cba25 100644 (file)
@@ -638,6 +638,9 @@ date_default_timezone_set( $wgLocaltimezone );
 if ( is_null( $wgLocalTZoffset ) ) {
        $wgLocalTZoffset = date( 'Z' ) / 60;
 }
+// The part after the System| is ignored, but rest of MW fills it
+// out as the local offset.
+$wgDefaultUserOptions['timecorrection'] = "System|$wgLocalTZoffset";
 
 if ( !$wgDBerrorLogTZ ) {
        $wgDBerrorLogTZ = $wgLocaltimezone;
index 25e1a7f..caf0cd7 100644 (file)
@@ -46,7 +46,39 @@ class ApiParamInfo extends ApiBase {
                $this->context->setLanguage( $this->getMain()->getLanguage() );
 
                if ( is_array( $params['modules'] ) ) {
-                       $modules = $params['modules'];
+                       $modules = [];
+                       foreach ( $params['modules'] as $path ) {
+                               if ( $path === '*' || $path === '**' ) {
+                                       $path = "main+$path";
+                               }
+                               if ( substr( $path, -2 ) === '+*' || substr( $path, -2 ) === ' *' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -2 );
+                                       $recursive = false;
+                               } elseif ( substr( $path, -3 ) === '+**' || substr( $path, -3 ) === ' **' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -3 );
+                                       $recursive = true;
+                               } else {
+                                       $submodules = false;
+                               }
+
+                               if ( $submodules ) {
+                                       try {
+                                               $module = $this->getModuleFromPath( $path );
+                                       } catch ( UsageException $ex ) {
+                                               $this->setWarning( $ex->getMessage() );
+                                       }
+                                       $submodules = $this->listAllSubmodules( $module, $recursive );
+                                       if ( $submodules ) {
+                                               $modules = array_merge( $modules, $submodules );
+                                       } else {
+                                               $this->setWarning( "Module $path has no submodules" );
+                                       }
+                               } else {
+                                       $modules[] = $path;
+                               }
+                       }
                } else {
                        $modules = [];
                }
@@ -69,6 +101,8 @@ class ApiParamInfo extends ApiBase {
                        $formatModules = [];
                }
 
+               $modules = array_unique( $modules );
+
                $res = [];
 
                foreach ( $modules as $m ) {
@@ -121,6 +155,29 @@ class ApiParamInfo extends ApiBase {
                $result->addValue( null, $this->getModuleName(), $res );
        }
 
+       /**
+        * List all submodules of a module
+        * @param ApiBase $module
+        * @param boolean $recursive
+        * @return string[]
+        */
+       private function listAllSubmodules( ApiBase $module, $recursive ) {
+               $manager = $module->getModuleManager();
+               if ( $manager ) {
+                       $paths = [];
+                       $names = $manager->getNames();
+                       sort( $names );
+                       foreach ( $names as $name ) {
+                               $submodule = $manager->getModule( $name );
+                               $paths[] = $submodule->getModulePath();
+                               if ( $recursive && $submodule->getModuleManager() ) {
+                                       $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) );
+                               }
+                       }
+               }
+               return $paths;
+       }
+
        /**
         * @param array $res Result array
         * @param string $key Result key
@@ -449,8 +506,10 @@ class ApiParamInfo extends ApiBase {
 
        protected function getExamplesMessages() {
                return [
-                       'action=paraminfo&modules=parse|phpfm|query+allpages|query+siteinfo'
+                       'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo'
                                => 'apihelp-paraminfo-example-1',
+                       'action=paraminfo&modules=query%2B*'
+                               => 'apihelp-paraminfo-example-2',
                ];
        }
 
index a559683..3073a95 100644 (file)
@@ -55,6 +55,14 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                $result = $this->getResult();
 
+               // If the user wants no namespaces, they get no pages.
+               if ( $params['namespace'] === [] ) {
+                       if ( $resultPageSet === null ) {
+                               $result->addValue( 'query', $this->getModuleName(), [] );
+                       }
+                       return;
+               }
+
                // This module operates in two modes:
                // 'user': List deleted revs by a certain user
                // 'all': List all deleted revs in NS
@@ -153,10 +161,10 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                if ( $mode == 'all' ) {
                        if ( $params['namespace'] !== null ) {
                                $namespaces = $params['namespace'];
-                               $this->addWhereFld( 'ar_namespace', $namespaces );
                        } else {
                                $namespaces = MWNamespace::getValidNamespaces();
                        }
+                       $this->addWhereFld( 'ar_namespace', $namespaces );
 
                        // For from/to/prefix, we have to consider the potential
                        // transformations of the title in all specified namespaces.
@@ -447,7 +455,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                return [
                        'action=query&list=alldeletedrevisions&adruser=Example&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-user',
-                       'action=query&list=alldeletedrevisions&adrdir=newer&adrlimit=50'
+                       'action=query&list=alldeletedrevisions&adrdir=newer&adrnamespace=0&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-ns-main',
                ];
        }
index a3ff076..06311f9 100644 (file)
        "apihelp-options-example-change": "Ändert die Einstellungen <kbd>skin</kbd> und <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Setzt alle Einstellungen zurück, dann <kbd>skin</kbd> und <kbd>nickname</kbd> festlegen.",
        "apihelp-paraminfo-description": "Ruft Informationen über API-Module ab.",
-       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der <var>action</var>- und <var>format</var>-Parameters, oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> bestimmen.",
+       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der Parameter <var>action</var> und <var>format</var> oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> oder alle Untermodule mit <kbd>+*</kbd> oder alle Untermodule rekursiv mit <kbd>+**</kbd> bestimmen.",
        "apihelp-paraminfo-param-helpformat": "Format der Hilfe-Zeichenfolgen.",
        "apihelp-paraminfo-param-querymodules": "Liste von Abfragemodulnamen (Werte von <var>prop</var>-, <var>meta</var>- oder <var>list</var>-Parameter). Benutze <kbd>$1modules=query+foo</kbd> anstatt <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Auch Informationen über die Hauptmodule (top-level) erhalten. Benutze <kbd>$1modules=main</kbd> stattdessen.",
        "apihelp-query+exturlusage-param-query": "Suchbegriff ohne Protokoll. Siehe [[Special:LinkSearch]]. Leer lassen, um alle externen Verknüpfungen aufzulisten.",
        "apihelp-query+exturlusage-param-namespace": "Die aufzulistenden Seiten-Namensräume.",
        "apihelp-query+exturlusage-param-limit": "Wie viele Seiten zurückgegeben werden sollen.",
+       "apihelp-query+exturlusage-example-simple": "Zeigt Seiten, die auf <kbd>http://www.mediawiki.org</kbd> verlinken.",
        "apihelp-query+filearchive-description": "Alle gelöschten Dateien der Reihe nach auflisten.",
        "apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
index a68a87f..974e0aa 100644 (file)
        "apihelp-options-example-complex": "Reset all preferences, then set <kbd>skin</kbd> and <kbd>nickname</kbd>.",
 
        "apihelp-paraminfo-description": "Obtain information about API modules.",
-       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>, or all submodules with <kbd>+*</kbd>, or all submodules recursively with <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format of help strings.",
        "apihelp-paraminfo-param-querymodules": "List of query module names (value of <var>prop</var>, <var>meta</var> or <var>list</var> parameter). Use <kbd>$1modules=query+foo</kbd> instead of <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Get information about the main (top-level) module as well. Use <kbd>$1modules=main</kbd> instead.",
        "apihelp-paraminfo-param-pagesetmodule": "Get information about the pageset module (providing titles= and friends) as well.",
        "apihelp-paraminfo-param-formatmodules": "List of format module names (value of <var>format</var> parameter). Use <var>$1modules</var> instead.",
        "apihelp-paraminfo-example-1": "Show info for <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, and <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Show info for all submodules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
 
        "apihelp-parse-description": "Parses content and returns parser output.\n\nSee the various prop-modules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd> to get information from the current version of a page.\n\nThere are several ways to specify the text to parse:\n# Specify a page or revision, using <var>$1page</var>, <var>$1pageid</var>, or <var>$1oldid</var>.\n# Specify content explicitly, using <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Specify only a summary to parse. <var>$1prop</var> should be given an empty value.",
        "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, <var>$1contentmodel</var> must be specified, and [[API]] will be used as the title.",
index 4b42964..91b169f 100644 (file)
        "apihelp-options-example-change": "Modifier les préférences <kbd>skin</kbd> et <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Réinitialiser toutes les préférences, puis définir <kbd>skin</kbd> et <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obtenir des informations sur les modules de l’API.",
-       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>, ou tous les sous-modules avec <kbd>+*</kbd>, ou tousles sous-modules récursivement avec <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format des chaînes d’aide.",
        "apihelp-paraminfo-param-querymodules": "Liste des noms de module de requêtage (valeur des paramètres <var>prop</var>, <var>meta</var> ou <var>list</var>=). Utiliser <kbd>$1modules=query+foo</kbd> au lieu de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obtenir aussi des informations sur le module principal (niveau supérieur). Utiliser plutôt <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obtenir aussi des informations sur le module pageset (en fournissant titles= et ses amis).",
        "apihelp-paraminfo-param-formatmodules": "Liste des noms de module de mise en forme (valeur du paramètre <var>format</var>). Utiliser plutôt <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Afficher les informations pour <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> et <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Afficher les informations pour tous les sous-modules de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analyse le contenu et renvoie le résultat de l’analyseur.\n\nVoyez les différents modules prop de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> pour avoir de l’information sur la version actuelle d’une page.\n\nIl y a plusieurs moyens de spécifier le texte à analyser :\n# Spécifier une page ou une révision, en utilisant <var>$1page</var>, <var>$1pageid</var> ou <var>$1oldid</var>.\n# Spécifier explicitement un contenu, en utilisant <var>$1text</var>, <var>$1title</var> et <var>$1contentmodel</var>\n# Spécifier uniquement un résumé à analyser. <var>$1prop</var> doit recevoir une valeur vide.",
        "apihelp-parse-param-title": "Titre de la page à laquelle appartient le texte. Si omis, <var>$1contentmodel</var> doit être spécifié, et [[API]] sera utilisé comme titre.",
        "apihelp-parse-param-text": "Texte à analyser. utiliser <var>$1title</var> ou <var>$1contentmodel</var> pour contrôler le modèle de contenu.",
        "api-help-param-deprecated": "Obsolète.",
        "api-help-param-required": "Ce paramètre est obligatoire.",
        "api-help-datatypes-header": "Type de données",
-       "api-help-datatypes": "Certains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
+       "api-help-datatypes": "Les entrées dans MédiaWiki doivent être en UTF-8 à la norme NFC. MédiaWiki peut tenter de convertir d’autres types d’entrée, mais cela peut faire échouer certaines opérations (comme les [[Special:ApiHelp/edit|modifications]] avec contrôles MD5) to fail.\n\nCertains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
        "api-help-param-type-limit": "Type : entier ou <kbd>max</kbd>",
        "api-help-param-type-integer": "Type : {{PLURAL:$1|1=entier|2=liste d’entiers}}",
        "api-help-param-type-boolean": "Type : booléen ([[Special:ApiHelp/main#main/datatypes|détails]])",
        "api-help-param-type-timestamp": "Type : {{PLURAL:$1|1=horodatage|2=liste d’horodatages}} ([[Special:ApiHelp/main#main/datatypes|formats autorisés]])",
        "api-help-param-type-user": "Type : {{PLURAL:$1|1=nom d’utilisateur|2=liste de noms d’utilisateur}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd>)}} : $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]])}} : $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Doit être vide|Peut être vide, ou $2}}",
        "api-help-param-limit": "Pas plus de $1 autorisé.",
        "api-help-param-limit2": "Pas plus de $1 autorisé ($2 pour les robots).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=La valeur ne doit pas être supérieure|2=Les valeurs ne doivent pas être supérieures}} à $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=La valeur doit|2=Les valeurs doivent}} être entre $2 et $3.",
        "api-help-param-upload": "Doit être envoyé comme un fichier importé utilisant multipart/form-data.",
-       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]].",
        "api-help-param-multi-max": "Le nombre maximal de valeurs est {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} pour les robots).",
        "api-help-param-default": "Par défaut : $1",
        "api-help-param-default-empty": "Par défaut : <span class=\"apihelp-empty\">(vide)</span>",
index 4180fae..bdb1bec 100644 (file)
        "apihelp-options-example-change": "Cambiar as preferencias <kbd>skin</kbd> and <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Restaurar todas as preferencias, logo fixar <kbd>skin</kbd> e <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obter información sobre módulos API.",
-       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>, ou tódolos submódulos con <kbd>+*</kbd>, ou tódolos submódulos recursivamente con <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Formato das cadeas de axuda.",
        "apihelp-paraminfo-param-querymodules": "Lista dos nomes de módulos de consulta (valores dos parámetros <var>prop</var>, <var>meta</var> ou <var>list</var>). Use <kbd>$1modules=query+foo</kbd> no canto de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obter información sobre o módulo principal (nivel superior). No canto use <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obter información sobre o módulo pageset (proporcionando títulos= e amigos).",
        "apihelp-paraminfo-param-formatmodules": "Lista dos nomes de módulo de formato (valores do parámetro <var>formato</var>). No canto use <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Amosar información para <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, e <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Mostrar a información para tódolos submódulos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analiza o contido e devolve o resultado do analizador.\n\nVexa varios módulos propostos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> para obter información sobre a versión actual dunha páxina.\n\nHai varias formas de especificar o texto a analizar:\n# Especificar unha páxina ou revisión, usando <var>$1page</var>, <var>$1pageid</var>, ou <var>$1oldid</var>.\n# Especificando contido explícitamente, usando <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Especificando só un resumo a analizar. <var>$1prop</var> debe ter un valor baleiro.",
        "apihelp-parse-param-title": "Título da páxina á que pertence o texto. Se non se indica, debe especificarse <var>$1contentmodel</var>, e [[API]] usarase como o título.",
        "apihelp-parse-param-text": "Texto a analizar. Use <var>$1title</var> ou <var>$1contentmodel</var> para controlar o modelo de contido.",
index 6d2dbf0..f3beff6 100644 (file)
        "apihelp-query+deletedrevs-param-end": "列挙の終点となるタイムスタンプ。",
        "apihelp-query+deletedrevs-param-from": "列挙の始点となるページ名。",
        "apihelp-query+deletedrevs-param-to": "列挙の終点となるページ名。",
+       "apihelp-query+deletedrevs-param-tag": "このタグが付与された版のみを一覧表示する。",
+       "apihelp-query+deletedrevs-param-user": "この利用者による版のみを一覧表示する。",
        "apihelp-query+deletedrevs-param-excludeuser": "この利用者による版を一覧表示しない。",
        "apihelp-query+deletedrevs-param-namespace": "この名前空間に含まれるページのみを一覧表示します。",
        "apihelp-query+deletedrevs-param-limit": "一覧表示する版の最大量。",
        "apihelp-query+info-description": "ページの基本的な情報を取得します。",
        "apihelp-query+info-param-prop": "追加で取得するプロパティ:",
        "apihelp-query+info-paramvalue-prop-protection": "それぞれのページの保護レベルを一覧表示する。",
+       "apihelp-query+info-param-token": "代わりに [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] を使用してください。",
        "apihelp-query+info-example-simple": "<kbd>Main Page</kbd> に関する情報を取得する。",
        "apihelp-query+iwbacklinks-param-prefix": "インターウィキ接頭辞。",
        "apihelp-query+iwbacklinks-param-title": "検索するウィキ間リンク。<var>$1blprefix</var>と同時に使用しなければなりません。",
        "api-help-param-deprecated": "廃止予定です。",
        "api-help-param-required": "このパラメーターは必須です。",
        "api-help-datatypes-header": "データ型",
-       "api-help-param-list": "{{PLURAL:$1|1=値 (次の値のいずれか1つ)|2=値 (<kbd>{{!}}</kbd>で区切る)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=å\80¤ (次ã\81®å\80¤ã\81®ã\81\84ã\81\9aã\82\8cã\81\8b\81¤)|2=å\80¤ (<kbd>{{!}}</kbd>ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|å\88¥ã\81®æ\96\87å­\97å\88\97]]ã\81§å\8cºå\88\87ã\82\8b)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=空欄にしてください|空欄にするか、または $2}}",
        "api-help-param-integer-min": "{{PLURAL:$1|値}}は $2 以上にしてください。",
        "api-help-param-integer-max": "{{PLURAL:$1|値}}は $3 以下にしてください。",
        "api-help-param-integer-minmax": "{{PLURAL:$1|値}}は $2 以上 $3 以下にしてください。",
        "api-help-param-upload": "multipart/form-data 形式でファイルをアップロードしてください。",
-       "api-help-param-multi-separate": "複数の値は <kbd>|</kbd> で区切ってください。",
+       "api-help-param-multi-separate": "è¤\87æ\95°ã\81®å\80¤ã\81¯ <kbd>|</kbd> ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|代ã\82\8fã\82\8aã\81®æ\96\87å­\97]]ã\81§å\8cºå\88\87ã\81£ã\81¦ã\81\8fã\81 ã\81\95ã\81\84ã\80\82",
        "api-help-param-multi-max": "値の最大値は {{PLURAL:$1|$1}} (ボットの場合は {{PLURAL:$2|$2}}) です。",
        "api-help-param-default": "既定値: $1",
        "api-help-param-default-empty": "既定値: <span class=\"apihelp-empty\">(空)</span>",
index dabc560..6e85779 100644 (file)
@@ -51,7 +51,7 @@
        "apihelp-feedrecentchanges-param-tagfilter": "Filtrar per balisa.",
        "apihelp-filerevert-param-comment": "Telecargar lo comentari.",
        "apihelp-filerevert-param-archivename": "Nom d’archiu de la revision de restablir.",
-       "apihelp-import-param-summary": "Importar lo resumit.",
+       "apihelp-import-param-summary": "Resumit de l’importacion de l’entrada de jornal.",
        "apihelp-import-param-xml": "Fichièr XML telecargat.",
        "apihelp-login-param-name": "Nom d'utilizaire.",
        "apihelp-login-param-password": "Senhal.",
index 650acb9..abbc69b 100644 (file)
        "apihelp-paraminfo-param-pagesetmodule": "{{doc-apihelp-param|paraminfo|pagesetmodule}}",
        "apihelp-paraminfo-param-formatmodules": "{{doc-apihelp-param|paraminfo|formatmodules}}",
        "apihelp-paraminfo-example-1": "{{doc-apihelp-example|paraminfo}}",
+       "apihelp-paraminfo-example-2": "{{doc-apihelp-example|paraminfo}}",
        "apihelp-parse-description": "{{doc-apihelp-description|parse}}",
        "apihelp-parse-param-title": "{{doc-apihelp-param|parse|title}}",
        "apihelp-parse-param-text": "{{doc-apihelp-param|parse|text}}",
index 3205248..35389b0 100644 (file)
        "api-help-parameters": "{{PLURAL:$1|Parameter|Parametrar}}:",
        "api-help-param-deprecated": "Föråldrad.",
        "api-help-param-required": "Denna parameter är obligatorisk.",
-       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Måste vara tom|Kan vara tom, eller $2}}",
        "api-help-param-limit": "Inte mer än $1 tillåts.",
-       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts."
+       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts.",
+       "api-help-param-multi-separate": "Separera värden med <kbd>|</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]]."
 }
index 45b43df..2f65c8e 100644 (file)
        "apihelp-options-example-change": "Змінити налаштування <kbd>skin</kbd> та <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Скинути всі налаштування, потім встановити <kbd>skin</kbd> та <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Отримати інформацію про модулі API.",
-       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>, усі підмодулі через <kbd>+*</kbd> або усі підмодулі рекурсивно через <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Формат рядків довідки.",
        "apihelp-paraminfo-param-querymodules": "Список назв модулів запитів (значення параметра <var>prop</var>, <var>meta</var> або <var>list</var>). Використати <kbd>$1modules=query+foo</kbd> замість <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Отримати інформацію також про основний модуль (топ-рівень). Використати натомість <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Отримати також інформацію про модуль pageset (з вказанням titles= і рідних).",
        "apihelp-paraminfo-param-formatmodules": "Список назв модулів форматування (значення параметра <var>format</var>). Використати натомість <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Показати інформацію для <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> та <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Показати інформацію про всі підмодулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Аналізує вміст і видає парсер виходу.\n\nДив. різні prop-модулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>, щоб отримати інформацію з поточної версії сторінки.\n\nЄ декілька способів вказати текст для аналізу:\n# Вказати сторінку або версію, використавши <var>$1page</var>, <var>$1pageid</var> або <var>$1oldid</var>.\n# Вказати безпосередньо, використавши <var>$1text</var>, <var>$1title</var> і <var>$1contentmodel</var>.\n# Вказати лише підсумок аналізу. <var>$1prop</var> повинен мати порожнє значення.",
        "apihelp-parse-param-title": "Назва сторінки, якій належить текст. Якщо пропущена, має бути вказано <var>$1contentmodel</var>, а як назву буде вжито [[API]].",
        "apihelp-parse-param-text": "Текст для аналізу. Використати <var>$1title</var> або <var>$1contentmodel</var> для контролю моделі вмісту.",
index 9768d36..55c4ad5 100644 (file)
@@ -233,4 +233,12 @@ class WikiTextStructure {
                $this->extractWikitextParts();
                return $this->auxText;
        }
+
+       /**
+        * Get the defaultsort property
+        * @return string|null
+        */
+       public function getDefaultSort() {
+               return $this->parserOutput->getProperty( 'defaultsort' );
+       }
 }
index 1c46d28..978ac44 100644 (file)
@@ -160,6 +160,7 @@ class WikitextContentHandler extends TextContentHandler {
                $fields['opening_text'] = $structure->getOpeningText();
                $fields['text'] = $structure->getMainText(); // overwrites one from ContentHandler
                $fields['auxiliary_text'] = $structure->getAuxiliaryText();
+               $fields['defaultsort'] = $structure->getDefaultSort();
 
                $title = $page->getTitle();
                if ( NS_FILE == $title->getNamespace() ) {
index 86d40f4..1019e72 100644 (file)
@@ -441,6 +441,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function setTransactionListener( $name, callable $callback = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function startAtomic( $fname = __METHOD__ ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
index 1adf73d..a864f0f 100644 (file)
@@ -74,8 +74,10 @@ abstract class DatabaseBase implements IDatabase {
        protected $mTrxPreCommitCallbacks = [];
        /** @var array[] List of (callable, method name) */
        protected $mTrxEndCallbacks = [];
-       /** @var bool Whether to suppress triggering of post-commit callbacks */
-       protected $suppressPostCommitCallbacks = false;
+       /** @var array[] Map of (name => (callable, method name)) */
+       protected $mTrxRecurringCallbacks = [];
+       /** @var bool Whether to suppress triggering of transaction end callbacks */
+       protected $mTrxEndCallbacksSuppressed = false;
 
        /** @var string */
        protected $mTablePrefix;
@@ -1063,6 +1065,7 @@ abstract class DatabaseBase implements IDatabase {
                try {
                        // Handle callbacks in mTrxEndCallbacks
                        $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+                       $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
                        return null;
                } catch ( Exception $e ) {
                        // Already logged; move on...
@@ -2642,16 +2645,24 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
+       final public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->mTrxRecurringCallbacks[$name] = [ $callback, wfGetCaller() ];
+               } else {
+                       unset( $this->mTrxRecurringCallbacks[$name] );
+               }
+       }
+
        /**
-        * Whether to disable running of post-commit callbacks
+        * Whether to disable running of post-COMMIT/ROLLBACK callbacks
         *
         * This method should not be used outside of Database/LoadBalancer
         *
         * @param bool $suppress
         * @since 1.28
         */
-       final public function setPostCommitCallbackSupression( $suppress ) {
-               $this->suppressPostCommitCallbacks = $suppress;
+       final public function setTrxEndCallbackSuppression( $suppress ) {
+               $this->mTrxEndCallbacksSuppressed = $suppress;
        }
 
        /**
@@ -2664,7 +2675,7 @@ abstract class DatabaseBase implements IDatabase {
         * @throws Exception
         */
        public function runOnTransactionIdleCallbacks( $trigger ) {
-               if ( $this->suppressPostCommitCallbacks ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
                        return;
                }
 
@@ -2734,6 +2745,38 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
+       /**
+        * Actually run any "transaction listener" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
+        * @throws Exception
+        * @since 1.20
+        */
+       public function runTransactionListenerCallbacks( $trigger ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
+                       return;
+               }
+
+               /** @var Exception $e */
+               $e = null; // first exception
+
+               foreach ( $this->mTrxRecurringCallbacks as $callback ) {
+                       try {
+                               list( $phpCallback ) = $callback;
+                               $phpCallback( $trigger, $this );
+                       } catch ( Exception $ex ) {
+                               MWExceptionHandler::logException( $ex );
+                               $e = $e ?: $ex;
+                       }
+               }
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any first exception
+               }
+       }
+
        final public function startAtomic( $fname = __METHOD__ ) {
                if ( !$this->mTrxLevel ) {
                        $this->begin( $fname, self::TRANSACTION_INTERNAL );
@@ -2875,6 +2918,7 @@ abstract class DatabaseBase implements IDatabase {
                }
 
                $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_COMMIT );
        }
 
        /**
@@ -2920,6 +2964,7 @@ abstract class DatabaseBase implements IDatabase {
                $this->mTrxIdleCallbacks = []; // clear
                $this->mTrxPreCommitCallbacks = []; // clear
                $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
        }
 
        /**
@@ -2937,6 +2982,18 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
+       public function clearSnapshot( $fname = __METHOD__ ) {
+               if ( $this->writesOrCallbacksPending() || $this->explicitTrxActive() ) {
+                       // This only flushes transactions to clear snapshots, not to write data
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Cannot COMMIT to clear snapshot because writes are pending."
+                       );
+               }
+
+               $this->commit( $fname, self::FLUSHING_INTERNAL );
+       }
+
        public function explicitTrxActive() {
                return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
        }
index 5c632ca..5f543c3 100644 (file)
@@ -35,9 +35,9 @@
 interface IDatabase {
        /** @var int Callback triggered immediately due to no active transaction */
        const TRIGGER_IDLE = 1;
-       /** @var int Callback triggered by commit */
+       /** @var int Callback triggered by COMMIT */
        const TRIGGER_COMMIT = 2;
-       /** @var int Callback triggered by rollback */
+       /** @var int Callback triggered by ROLLBACK */
        const TRIGGER_ROLLBACK = 3;
 
        /** @var string Transaction is requested by regular caller outside of the DB layer */
@@ -205,6 +205,7 @@ interface IDatabase {
        /**
         * Returns true if there is a transaction open with possible write
         * queries or transaction pre-commit/idle callbacks waiting on it to finish.
+        * This does *not* count recurring callbacks, e.g. from setTransactionListener().
         *
         * @return bool
         */
@@ -1273,7 +1274,7 @@ interface IDatabase {
         * This is useful for combining cooperative locks and DB transactions.
         *
         * The callback takes one argument:
-        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
         *
         * @param callable $callback
         * @return mixed
@@ -1295,7 +1296,7 @@ interface IDatabase {
         * Updates will execute in the order they were enqueued.
         *
         * The callback takes one argument:
-        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
         *
         * @param callable $callback
         * @since 1.20
@@ -1318,6 +1319,23 @@ interface IDatabase {
         */
        public function onTransactionPreCommitOrIdle( callable $callback );
 
+       /**
+        * Run a callback each time any transaction commits or rolls back
+        *
+        * The callback takes two arguments:
+        *   - IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK
+        *   - This IDatabase object
+        * Callbacks must commit any transactions that they begin.
+        *
+        * Registering a callback here will not affect writesOrCallbacks() pending
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a listener
+        * @return mixed
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null );
+
        /**
         * Begin an atomic section of statements
         *
index dfa4c29..2e6f602 100644 (file)
@@ -44,8 +44,12 @@ abstract class LBFactory implements DestructibleService {
 
        /** @var mixed */
        protected $ticket;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       protected $trxRoundId = false;
        /** @var string|bool Reason all LBs are read-only or false if not */
        protected $readOnlyReason = false;
+       /** @var callable[] */
+       protected $replicationWaitCallbacks = [];
 
        const SHUTDOWN_NO_CHRONPROT = 1; // don't save ChronologyProtector positions (for async code)
 
@@ -229,9 +233,18 @@ abstract class LBFactory implements DestructibleService {
         * This allows for custom transaction rounds from any outer transaction scope.
         *
         * @param string $fname
+        * @throws DBTransactionError
         * @since 1.28
         */
        public function beginMasterChanges( $fname = __METHOD__ ) {
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "Transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+               // Set DBO_TRX flags on all appropriate DBs
                $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
        }
 
@@ -256,19 +269,20 @@ abstract class LBFactory implements DestructibleService {
         * @throws Exception
         */
        public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
-               // Perform all pre-commit callbacks, aborting on failure
-               $this->forEachLBCallMethod( 'runMasterPreCommitCallbacks' );
-               // Perform all pre-commit checks, aborting on failure
+               // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
+               $this->forEachLBCallMethod( 'finalizeMasterChanges' );
+               $this->trxRoundId = false;
+               // Perform pre-commit checks, aborting on failure
                $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
                // Log the DBs and methods involved in multi-DB transactions
                $this->logIfMultiDbTransaction();
-               // Actually perform the commit on all master DB connections
+               // Actually perform the commit on all master DB connections and revert DBO_TRX
                $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
                // Run all post-commit callbacks
                /** @var Exception $e */
                $e = null; // first callback exception
                $this->forEachLB( function ( LoadBalancer $lb ) use ( &$e ) {
-                       $ex = $lb->runMasterPostCommitCallbacks();
+                       $ex = $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_COMMIT );
                        $e = $e ?: $ex;
                } );
                // Commit any dangling DBO_TRX transactions from callbacks on one DB to another DB
@@ -285,7 +299,13 @@ abstract class LBFactory implements DestructibleService {
         * @since 1.23
         */
        public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               $this->trxRoundId = false;
+               $this->forEachLBCallMethod( 'suppressTransactionEndCallbacks' );
                $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
+               // Run all post-rollback callbacks
+               $this->forEachLB( function ( LoadBalancer $lb ) {
+                       $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_ROLLBACK );
+               } );
        }
 
        /**
@@ -384,6 +404,10 @@ abstract class LBFactory implements DestructibleService {
                        'ifWritesSince' => null
                ];
 
+               foreach ( $this->replicationWaitCallbacks as $callback ) {
+                       $callback();
+               }
+
                // Figure out which clusters need to be checked
                /** @var LoadBalancer[] $lbs */
                $lbs = [];
@@ -436,6 +460,23 @@ abstract class LBFactory implements DestructibleService {
                }
        }
 
+       /**
+        * Add a callback to be run in every call to waitForReplication() before waiting
+        *
+        * Callbacks must clear any transactions that they start
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a callback
+        * @since 1.28
+        */
+       public function setWaitForReplicationListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->replicationWaitCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->replicationWaitCallbacks[$name] );
+               }
+       }
+
        /**
         * Get a token asserting that no transaction writes are active
         *
@@ -530,6 +571,15 @@ abstract class LBFactory implements DestructibleService {
                } );
        }
 
+       /**
+        * @param LoadBalancer $lb
+        */
+       protected function initLoadBalancer( LoadBalancer $lb ) {
+               if ( $this->trxRoundId !== false ) {
+                       $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
+               }
+       }
+
        /**
         * Close all open database connections on all open load balancers.
         * @since 1.28
index f201081..c0f6509 100644 (file)
@@ -313,7 +313,7 @@ class LBFactoryMulti extends LBFactory {
         * @return LoadBalancer
         */
        private function newLoadBalancer( $template, $loads, $groupLoads, $readOnlyReason ) {
-               return new LoadBalancer( [
+               $lb = new LoadBalancer( [
                        'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
                        'loadMonitor' => $this->loadMonitorClass,
                        'readOnlyReason' => $readOnlyReason,
@@ -321,6 +321,10 @@ class LBFactoryMulti extends LBFactory {
                        'srvCache' => $this->srvCache,
                        'wanCache' => $this->wanCache
                ] );
+
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
        }
 
        /**
index c7c4752..c06b5b1 100644 (file)
@@ -133,7 +133,7 @@ class LBFactorySimple extends LBFactory {
        }
 
        private function newLoadBalancer( array $servers ) {
-               return new LoadBalancer( [
+               $lb = new LoadBalancer( [
                        'servers' => $servers,
                        'loadMonitor' => $this->loadMonitorClass,
                        'readOnlyReason' => $this->readOnlyReason,
@@ -141,6 +141,10 @@ class LBFactorySimple extends LBFactory {
                        'srvCache' => $this->srvCache,
                        'wanCache' => $this->wanCache
                ] );
+
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
        }
 
        /**
index 32729dd..c554f17 100644 (file)
@@ -51,6 +51,8 @@ class LoadBalancer {
        private $srvCache;
        /** @var WANObjectCache */
        private $wanCache;
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
 
        /** @var bool|DatabaseBase Database connection that caused a problem */
        private $mErrorConnection;
@@ -68,9 +70,10 @@ class LoadBalancer {
        private $readOnlyReason = false;
        /** @var integer Total connections opened */
        private $connsOpened = 0;
-
-       /** @var TransactionProfiler */
-       protected $trxProfiler;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       private $trxRoundId = false;
+       /** @var array[] Map of (name => callable) */
+       private $trxRecurringCallbacks = [];
 
        /** @var integer Warn when this many connection are held */
        const CONN_HELD_WARN_THRESHOLD = 10;
@@ -864,6 +867,15 @@ class LoadBalancer {
                        $this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() )
                );
                $db->setTransactionProfiler( $this->trxProfiler );
+               if ( $this->trxRoundId !== false ) {
+                       $this->applyTransactionRoundFlags( $db );
+               }
+
+               if ( $server['serverIndex'] === $this->getWriterIndex() ) {
+                       foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
+                               $db->setTransactionListener( $name, $callback );
+                       }
+               }
 
                return $db;
        }
@@ -1059,24 +1071,47 @@ class LoadBalancer {
        /**
         * Commit transactions on all open connections
         * @param string $fname Caller name
+        * @throws DBExpectedError
         */
        public function commitAll( $fname = __METHOD__ ) {
-               $this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
-               } );
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore && $conn->getLBInfo( 'master' ) ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
+                       }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
        }
 
        /**
         * Perform all pre-commit callbacks that remain part of the atomic transactions
-        * and disable any post-commit callbacks until runMasterPostCommitCallbacks()
+        * and disable any post-commit callbacks until runMasterPostTrxCallbacks()
         * @since 1.28
         */
-       public function runMasterPreCommitCallbacks() {
+       public function finalizeMasterChanges() {
                $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
-                       // Any error will cause all DB transactions to be rolled back together.
+                       // Any error should cause all DB transactions to be rolled back together
+                       $conn->setTrxEndCallbackSuppression( false );
                        $conn->runOnTransactionPreCommitCallbacks();
-                       // Defer post-commit callbacks until COMMIT finishes for all DBs.
-                       $conn->setPostCommitCallbackSupression( true );
+                       // Defer post-commit callbacks until COMMIT finishes for all DBs
+                       $conn->setTrxEndCallbackSuppression( true );
                } );
        }
 
@@ -1129,55 +1164,96 @@ class LoadBalancer {
         * This allows for custom transaction rounds from any outer transaction scope.
         *
         * @param string $fname
+        * @throws DBExpectedError
         * @since 1.28
         */
        public function beginMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       if ( $conn->writesOrCallbacksPending() ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       "Transaction with pending writes still active."
-                               );
-                       } elseif ( $conn->trxLevel() ) {
-                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
-                       }
-                       if ( $conn->getFlag( DBO_DEFAULT ) ) {
-                               // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
-                               // Force DBO_TRX even in CLI mode since a commit round is expected soon.
-                               $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
-                               $conn->onTransactionResolution( function () use ( $conn ) {
-                                       $conn->restoreFlags( $conn::RESTORE_PRIOR );
-                               } );
-                       } else {
-                               // Config has explicitly requested DBO_TRX be either on or off; respect that.
-                               // This is useful for things like blob stores which use auto-commit mode.
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: Transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+
+               $failures = [];
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, &$failures ) {
+                               $conn->setTrxEndCallbackSuppression( true );
+                               try {
+                                       $conn->clearSnapshot( $fname );
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               $conn->setTrxEndCallbackSuppression( false );
+                               $this->applyTransactionRoundFlags( $conn );
                        }
-               } );
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Flush failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
        }
 
        /**
         * Issue COMMIT on all master connections where writes where done
         * @param string $fname Caller name
+        * @throws DBExpectedError
         */
        public function commitMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       if ( $conn->writesOrCallbacksPending() ) {
-                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       if ( $conn->writesOrCallbacksPending() ) {
+                                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                                       } elseif ( $restore ) {
+                                               $conn->clearSnapshot( $fname );
+                                       }
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
                        }
-               } );
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
        }
 
        /**
-        * Issue all pending post-commit callbacks
+        * Issue all pending post-COMMIT/ROLLBACK callbacks
+        * @param integer $type IDatabase::TRIGGER_* constant
         * @return Exception|null The first exception or null if there were none
         * @since 1.28
         */
-       public function runMasterPostCommitCallbacks() {
+       public function runMasterPostTrxCallbacks( $type ) {
                $e = null; // first exception
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( &$e ) {
-                       $conn->setPostCommitCallbackSupression( false );
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $type, &$e ) {
+                       $conn->clearSnapshot( __METHOD__ ); // clear no-op transactions
+
+                       $conn->setTrxEndCallbackSuppression( false );
+                       try {
+                               $conn->runOnTransactionIdleCallbacks( $type );
+                       } catch ( Exception $ex ) {
+                               $e = $e ?: $ex;
+                       }
                        try {
-                               $conn->runOnTransactionIdleCallbacks( $conn::TRIGGER_COMMIT );
+                               $conn->runTransactionListenerCallbacks( $type );
                        } catch ( Exception $ex ) {
                                $e = $e ?: $ex;
                        }
@@ -1193,29 +1269,51 @@ class LoadBalancer {
         * @since 1.23
         */
        public function rollbackMasterChanges( $fname = __METHOD__ ) {
-               $failedServers = [];
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       try {
-                                               $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
-                                       } catch ( DBError $e ) {
-                                               MWExceptionHandler::logException( $e );
-                                               $failedServers[] = $conn->getServer();
-                                       }
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore ) {
+                               if ( $conn->writesOrCallbacksPending() ) {
+                                       $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
                                }
                        }
+               );
+       }
+
+       /**
+        * Suppress all pending post-COMMIT/ROLLBACK callbacks
+        * @return Exception|null The first exception or null if there were none
+        * @since 1.28
+        */
+       public function suppressTransactionEndCallbacks() {
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+                       $conn->setTrxEndCallbackSuppression( true );
+               } );
+       }
+
+       /**
+        * @param DatabaseBase $conn
+        */
+       private function applyTransactionRoundFlags( DatabaseBase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
+                       // Force DBO_TRX even in CLI mode since a commit round is expected soon.
+                       $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
+                       // If config has explicitly requested DBO_TRX be either on or off by not
+                       // setting DBO_DEFAULT, then respect that. Forcing no transactions is useful
+                       // for things like blob stores (ExternalStore) which want auto-commit mode.
                }
+       }
 
-               if ( $failedServers ) {
-                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
-                               implode( ', ', array_unique( $failedServers ) ) );
+       /**
+        * @param DatabaseBase $conn
+        */
+       private function undoTransactionRoundFlags( DatabaseBase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       $conn->restoreFlags( $conn::RESTORE_PRIOR );
                }
        }
 
@@ -1576,4 +1674,25 @@ class LoadBalancer {
        public function clearLagTimeCache() {
                $this->getLoadMonitor()->clearCaches();
        }
+
+       /**
+        * Set a callback via DatabaseBase::setTransactionListener() on
+        * all current and future master connections of this load balancer
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->trxRecurringCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->trxRecurringCallbacks[$name] );
+               }
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $name, $callback ) {
+                               $conn->setTransactionListener( $name, $callback );
+                       }
+               );
+       }
 }
index ee14e1a..5622f95 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class for managing the deferred updates
@@ -46,6 +47,8 @@ class DeferredUpdates {
        const PRESEND = 1; // for updates that should run before flushing output buffer
        const POSTSEND = 2; // for updates that should run after flushing output buffer
 
+       const BIG_QUEUE_SIZE = 100;
+
        /**
         * Add an update to the deferred list
         *
@@ -181,6 +184,71 @@ class DeferredUpdates {
                }
        }
 
+       /**
+        * Run all deferred updates immediately if there are no DB writes active
+        *
+        * If $mode is 'run' but there are busy databates, EnqueueableDataUpdate
+        * tasks will be enqueued anyway for the sake of progress.
+        *
+        * @param string $mode Use "enqueue" to use the job queue when possible
+        * @return bool Whether updates were allowed to run
+        * @since 1.28
+        */
+       public static function tryOpportunisticExecute( $mode = 'run' ) {
+               static $recursionGuard = false;
+               if ( $recursionGuard ) {
+                       return false; // COMMITs trigger inside update loop and inside some updates
+               }
+
+               try {
+                       $recursionGuard = true;
+                       if ( !self::getBusyDbConnections() ) {
+                               self::doUpdates( $mode );
+                               return true;
+                       }
+
+                       if ( self::pendingUpdatesCount() >= self::BIG_QUEUE_SIZE ) {
+                               // If we cannot run the updates with outer transaction context, try to
+                               // at least enqueue all the updates that support queueing to job queue
+                               self::$preSendUpdates = self::enqueueUpdates( self::$preSendUpdates );
+                               self::$postSendUpdates = self::enqueueUpdates( self::$postSendUpdates );
+                       }
+
+                       return !self::pendingUpdatesCount();
+               } finally {
+                       $recursionGuard = false;
+               }
+       }
+
+       /**
+        * Enqueue a job for each EnqueueableDataUpdate item and return the other items
+        *
+        * @param DeferrableUpdate[] $updates A list of deferred update instances
+        * @return DeferrableUpdate[] Remaining updates that do not support being queued
+        */
+       private static function enqueueUpdates( array $updates ) {
+               $remaining = [];
+
+               foreach ( $updates as $update ) {
+                       if ( $update instanceof EnqueueableDataUpdate ) {
+                               $spec = $update->getAsJobSpecification();
+                               JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
+                       } else {
+                               $remaining[] = $update;
+                       }
+               }
+
+               return $remaining;
+       }
+
+       /**
+        * @return integer Number of enqueued updates
+        * @since 1.28
+        */
+       public static function pendingUpdatesCount() {
+               return count( self::$preSendUpdates ) + count( self::$postSendUpdates );
+       }
+
        /**
         * Clear all pending updates without performing them. Generally, you don't
         * want or need to call this. Unit tests need it though.
@@ -189,4 +257,44 @@ class DeferredUpdates {
                self::$preSendUpdates = [];
                self::$postSendUpdates = [];
        }
+
+       /**
+        * Set the rollback/commit watcher on a DB to trigger update runs when safe
+        *
+        * @TODO: use this to replace DB logic in push()
+        * @param LoadBalancer $lb
+        * @since 1.28
+        */
+       public static function installDBListener( LoadBalancer $lb ) {
+               static $triggers = [ IDatabase::TRIGGER_COMMIT, IDatabase::TRIGGER_ROLLBACK ];
+               // Hook into active master connections to find a moment where no writes are pending
+               $lb->setTransactionListener(
+                       __METHOD__,
+                       function ( $trigger, IDatabase $conn ) use ( $triggers ) {
+                               global $wgCommandLineMode;
+
+                               if ( $wgCommandLineMode && in_array( $trigger, $triggers ) ) {
+                                       DeferredUpdates::tryOpportunisticExecute();
+                               }
+                       }
+               );
+       }
+
+       /**
+        * @return IDatabase[] Connection where commit() cannot be called yet
+        */
+       private static function getBusyDbConnections() {
+               $connsBusy = [];
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->forEachLB( function ( LoadBalancer $lb ) use ( &$connsBusy ) {
+                       $lb->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$connsBusy ) {
+                               if ( $conn->writesOrCallbacksPending() || $conn->explicitTrxActive() ) {
+                                       $connsBusy[] = $conn;
+                               }
+                       } );
+               } );
+
+               return $connsBusy;
+       }
 }
index 47f2b21..ca3500e 100644 (file)
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
+
 /**
  * Update object handling the cleanup of links tables after a page was deleted.
  **/
-class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
        /** @var WikiPage */
        protected $page;
        /** @var integer */
@@ -30,6 +32,9 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        /** @var string */
        protected $timestamp;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * @param WikiPage $page Page we are updating
         * @param integer|null $pageId ID of the page we are updating [optional]
@@ -37,8 +42,6 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
         * @throws MWException
         */
        function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
-               parent::__construct( false ); // no implicit transaction
-
                $this->page = $page;
                if ( $pageId ) {
                        $this->pageId = $pageId; // page ID at time of deletion
@@ -52,23 +55,25 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        }
 
        public function doUpdate() {
-               $config = RequestContext::getMain()->getConfig();
+               $services = MediaWikiServices::getInstance();
+               $config = $services->getMainConfig();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $batchSize = $config->get( 'UpdateRowsPerQuery' );
-               $factory = wfGetLBFactory();
 
                // Page may already be deleted, so don't just getId()
                $id = $this->pageId;
                // Make sure all links update threads see the changes of each other.
                // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = LinksUpdate::acquirePageLock( $this->mDb, $id );
+               $scopedLock = LinksUpdate::acquirePageLock( $this->getDB(), $id );
 
                $title = $this->page->getTitle();
+               $dbw = $this->getDB(); // convenience
 
                // Delete restrictions for it
-               $this->mDb->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
+               $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
 
                // Fix category table counts
-               $cats = $this->mDb->selectFieldValues(
+               $cats = $dbw->selectFieldValues(
                        'categorylinks',
                        'cl_to',
                        [ 'cl_from' => $id ],
@@ -78,8 +83,8 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                foreach ( $catBatches as $catBatch ) {
                        $this->page->updateCategoryCounts( [], $catBatch, $id );
                        if ( count( $catBatches ) > 1 ) {
-                               $factory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                );
                        }
                }
@@ -87,19 +92,19 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                // Refresh the category table entry if it seems to have no pages. Check
                // master for the most up-to-date cat_pages count.
                if ( $title->getNamespace() === NS_CATEGORY ) {
-                       $row = $this->mDb->selectRow(
+                       $row = $dbw->selectRow(
                                'category',
                                [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
                                [ 'cat_title' => $title->getDBkey(), 'cat_pages <= 0' ],
                                __METHOD__
                        );
                        if ( $row ) {
-                               $cat = Category::newFromRow( $row, $title )->refreshCounts();
+                               Category::newFromRow( $row, $title )->refreshCounts();
                        }
                }
 
                // If using cascading deletes, we can skip some explicit deletes
-               if ( !$this->mDb->cascadingDeletes() ) {
+               if ( !$dbw->cascadingDeletes() ) {
                        // Delete outgoing links
                        $this->batchDeleteByPK(
                                'pagelinks',
@@ -144,14 +149,14 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                $batchSize
                        );
                        // Delete any redirect entry or page props entries
-                       $this->mDb->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
-                       $this->mDb->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
+                       $dbw->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
+                       $dbw->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
                }
 
                // If using cleanup triggers, we can skip some manual deletes
-               if ( !$this->mDb->cleanupTriggers() ) {
+               if ( !$dbw->cleanupTriggers() ) {
                        // Find recentchanges entries to clean up...
-                       $rcIdsForTitle = $this->mDb->selectFieldValues(
+                       $rcIdsForTitle = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [
@@ -159,11 +164,11 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                        'rc_namespace' => $title->getNamespace(),
                                        'rc_title' => $title->getDBkey(),
                                        'rc_timestamp < ' .
-                                               $this->mDb->addQuotes( $this->mDb->timestamp( $this->timestamp ) )
+                                               $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) )
                                ],
                                __METHOD__
                        );
-                       $rcIdsForPage = $this->mDb->selectFieldValues(
+                       $rcIdsForPage = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [ 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ],
@@ -173,10 +178,10 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                        // T98706: delete by PK to avoid lock contention with RC delete log insertions
                        $rcIdBatches = array_chunk( array_merge( $rcIdsForTitle, $rcIdsForPage ), $batchSize );
                        foreach ( $rcIdBatches as $rcIdBatch ) {
-                               $this->mDb->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
+                               $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
                                if ( count( $rcIdBatches ) > 1 ) {
-                                       $factory->commitAndWaitForReplication(
-                                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                                       $lbFactory->commitAndWaitForReplication(
+                                               __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                        );
                                }
                        }
@@ -187,17 +192,19 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        }
 
        private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
-               $dbw = $this->mDb; // convenience
-               $factory = wfGetLBFactory();
+               $services = MediaWikiServices::getInstance();
+               $lbFactory = $services->getDBLoadBalancerFactory();
+               $dbw = $this->getDB(); // convenience
+
                $res = $dbw->select( $table, $pk, $conds, __METHOD__ );
 
                $pkDeleteConds = [];
                foreach ( $res as $row ) {
-                       $pkDeleteConds[] = $this->mDb->makeList( (array)$row, LIST_AND );
+                       $pkDeleteConds[] = $dbw->makeList( (array)$row, LIST_AND );
                        if ( count( $pkDeleteConds ) >= $bSize ) {
                                $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
-                               $factory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                );
                                $pkDeleteConds = [];
                        }
@@ -208,9 +215,17 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                }
        }
 
+       protected function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'deleteLinks',
                                [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
index ec7360e..6124a71 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * Class the manages updates of *_link tables as well as similar extension-managed tables
  *
@@ -27,7 +29,7 @@
  *
  * See docs/deferred.txt
  */
-class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        // @todo make members protected, but make sure extensions don't break
 
        /** @var int Page ID of the article linked from */
@@ -94,6 +96,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         */
        private $user;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * Constructor
         *
@@ -103,9 +108,6 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @throws MWException
         */
        function __construct( Title $title, ParserOutput $parserOutput, $recursive = true ) {
-               // Implicit transactions are disabled as they interfere with batching
-               parent::__construct( false );
-
                $this->mTitle = $title;
                $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
 
@@ -160,7 +162,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        public function doUpdate() {
                // Make sure all links update threads see the changes of each other.
                // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = self::acquirePageLock( $this->mDb, $this->mId );
+               $scopedLock = self::acquirePageLock( $this->getDB(), $this->mId );
 
                Hooks::run( 'LinksUpdate', [ &$this ] );
                $this->doIncrementalUpdate();
@@ -168,7 +170,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                // Commit and release the lock
                ScopedCallback::consume( $scopedLock );
                // Run post-commit hooks without DBO_TRX
-               $this->mDb->onTransactionIdle( function() {
+               $this->getDB()->onTransactionIdle( function() {
                        Hooks::run( 'LinksUpdateComplete', [ &$this ] );
                } );
        }
@@ -315,7 +317,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $cats
         */
        function invalidateCategories( $cats ) {
-               PurgeJobUtils::invalidatePages( $this->mDb, NS_CATEGORY, array_keys( $cats ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_CATEGORY, array_keys( $cats ) );
        }
 
        /**
@@ -334,7 +336,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $images
         */
        function invalidateImageDescriptions( $images ) {
-               PurgeJobUtils::invalidatePages( $this->mDb, NS_FILE, array_keys( $images ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_FILE, array_keys( $images ) );
        }
 
        /**
@@ -345,8 +347,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $insertions Rows to insert
         */
        private function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
-               $bSize = RequestContext::getMain()->getConfig()->get( 'UpdateRowsPerQuery' );
-               $factory = wfGetLBFactory();
+               $services = MediaWikiServices::getInstance();
+               $bSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
+               $factory = $services->getDBLoadBalancerFactory();
 
                if ( $table === 'page_props' ) {
                        $fromField = 'pp_page';
@@ -378,7 +381,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        foreach ( $deletionBatches as $deletionBatch ) {
                                $deleteWheres[] = [
                                        $fromField => $this->mId,
-                                       $this->mDb->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
+                                       $this->getDB()->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
                                ];
                        }
                } else {
@@ -397,17 +400,17 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                foreach ( $deleteWheres as $deleteWhere ) {
-                       $this->mDb->delete( $table, $deleteWhere, __METHOD__ );
+                       $this->getDB()->delete( $table, $deleteWhere, __METHOD__ );
                        $factory->commitAndWaitForReplication(
-                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
                        );
                }
 
                $insertBatches = array_chunk( $insertions, $bSize );
                foreach ( $insertBatches as $insertBatch ) {
-                       $this->mDb->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
+                       $this->getDB()->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
                        $factory->commitAndWaitForReplication(
-                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
                        );
                }
 
@@ -494,7 +497,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                foreach ( $diffs as $url => $dummy ) {
                        foreach ( wfMakeUrlIndexes( $url ) as $index ) {
                                $arr[] = [
-                                       'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
+                                       'el_id' => $this->getDB()->nextSequenceValue( 'externallinks_el_id_seq' ),
                                        'el_from' => $this->mId,
                                        'el_to' => $url,
                                        'el_index' => $index,
@@ -540,7 +543,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                                'cl_from' => $this->mId,
                                'cl_to' => $name,
                                'cl_sortkey' => $sortkey,
-                               'cl_timestamp' => $this->mDb->timestamp(),
+                               'cl_timestamp' => $this->getDB()->timestamp(),
                                'cl_sortkey_prefix' => $prefix,
                                'cl_collation' => $wgCategoryCollation,
                                'cl_type' => $type,
@@ -778,8 +781,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingLinks() {
-               $res = $this->mDb->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
-                       [ 'pl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
+                       [ 'pl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->pl_namespace] ) ) {
@@ -797,8 +800,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingTemplates() {
-               $res = $this->mDb->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
-                       [ 'tl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
+                       [ 'tl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->tl_namespace] ) ) {
@@ -816,8 +819,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingImages() {
-               $res = $this->mDb->select( 'imagelinks', [ 'il_to' ],
-                       [ 'il_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'imagelinks', [ 'il_to' ],
+                       [ 'il_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->il_to] = 1;
@@ -832,8 +835,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingExternals() {
-               $res = $this->mDb->select( 'externallinks', [ 'el_to' ],
-                       [ 'el_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'externallinks', [ 'el_to' ],
+                       [ 'el_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->el_to] = 1;
@@ -848,8 +851,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingCategories() {
-               $res = $this->mDb->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
-                       [ 'cl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
+                       [ 'cl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->cl_to] = $row->cl_sortkey_prefix;
@@ -865,8 +868,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingInterlangs() {
-               $res = $this->mDb->select( 'langlinks', [ 'll_lang', 'll_title' ],
-                       [ 'll_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'langlinks', [ 'll_lang', 'll_title' ],
+                       [ 'll_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->ll_lang] = $row->ll_title;
@@ -879,9 +882,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * Get an array of existing inline interwiki links, as a 2-D array
         * @return array (prefix => array(dbkey => 1))
         */
-       protected function getExistingInterwikis() {
-               $res = $this->mDb->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
-                       [ 'iwl_from' => $this->mId ], __METHOD__, $this->mOptions );
+       private function getExistingInterwikis() {
+               $res = $this->getDB()->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
+                       [ 'iwl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->iwl_prefix] ) ) {
@@ -899,8 +902,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array Array of property names and values
         */
        private function getExistingProperties() {
-               $res = $this->mDb->select( 'page_props', [ 'pp_propname', 'pp_value' ],
-                       [ 'pp_page' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'page_props', [ 'pp_propname', 'pp_value' ],
+                       [ 'pp_page' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->pp_propname] = $row->pp_value;
@@ -1050,18 +1053,29 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        /**
         * Update links table freshness
         */
-       protected function updateLinksTimestamp() {
+       private function updateLinksTimestamp() {
                if ( $this->mId ) {
                        // The link updates made here only reflect the freshness of the parser output
                        $timestamp = $this->mParserOutput->getCacheTime();
-                       $this->mDb->update( 'page',
-                               [ 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ],
+                       $this->getDB()->update( 'page',
+                               [ 'page_links_updated' => $this->getDB()->timestamp( $timestamp ) ],
                                [ 'page_id' => $this->mId ],
                                __METHOD__
                        );
                }
        }
 
+       /**
+        * @return IDatabase
+        */
+       private function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                if ( $this->user ) {
                        $userInfo = [
@@ -1079,7 +1093,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'refreshLinksPrioritized',
                                [
index ff06915..c7163ea 100644 (file)
@@ -29,6 +29,8 @@
  *       a transaction will automatically be wrapped around the update. Starting another
  *       one would break the outer transaction bracket. If need be, subclasses can override
  *       the beginTransaction() and commitTransaction() methods.
+ *
+ * @deprecated Since 1.28 Use DataUpdate directly, injecting the database
  */
 abstract class SqlDataUpdate extends DataUpdate {
        /** @var IDatabase Database connection reference */
index 8d25726..de3cdbe 100644 (file)
 
 use \MediaWiki\Logger\LoggerFactory;
 
-/**
- * Bump this number when serialized cache records may be incompatible.
- */
-define( 'MW_FILE_VERSION', 9 );
-
 /**
  * Class to represent a local file in the wiki's own database
  *
@@ -46,6 +41,8 @@ define( 'MW_FILE_VERSION', 9 );
  * @ingroup FileAbstraction
  */
 class LocalFile extends File {
+       const VERSION = 10; // cache version
+
        const CACHE_FIELD_MAX_LEN = 1000;
 
        /** @var bool Does the file exist on disk? (loadFromXxx) */
@@ -240,83 +237,71 @@ class LocalFile extends File {
         * @return string|bool
         */
        function getCacheKey() {
-               $hashedName = md5( $this->getName() );
-
-               return $this->repo->getSharedCacheKey( 'file', $hashedName );
+               return $this->repo->getSharedCacheKey( 'file', sha1( $this->getName() ) );
        }
 
        /**
-        * Try to load file metadata from memcached. Returns true on success.
-        * @return bool
+        * Try to load file metadata from memcached, falling back to the database
         */
        private function loadFromCache() {
                $this->dataLoaded = false;
                $this->extraDataLoaded = false;
-               $key = $this->getCacheKey();
 
+               $key = $this->getCacheKey();
                if ( !$key ) {
-                       return false;
-               }
-
-               $cache = ObjectCache::getMainWANInstance();
-               $cachedValues = $cache->get( $key );
+                       $this->loadFromDB( self::READ_NORMAL );
 
-               // Check if the key existed and belongs to this version of MediaWiki
-               if ( is_array( $cachedValues ) && $cachedValues['version'] == MW_FILE_VERSION ) {
-                       $this->fileExists = $cachedValues['fileExists'];
-                       if ( $this->fileExists ) {
-                               $this->setProps( $cachedValues );
-                       }
-                       $this->dataLoaded = true;
-                       $this->extraDataLoaded = true;
-                       foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                               $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
-                       }
+                       return;
                }
 
-               return $this->dataLoaded;
-       }
-
-       /**
-        * Save the file metadata to memcached
-        * @param array $cacheSetOpts Result of Database::getCacheSetOptions()
-        */
-       private function saveToCache( array $cacheSetOpts ) {
-               $this->load();
+               $cache = ObjectCache::getMainWANInstance();
+               $cachedValues = $cache->getWithSetCallback(
+                       $key,
+                       $cache::TTL_WEEK,
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+                               $setOpts += Database::getCacheSetOptions( $this->repo->getSlaveDB() );
+
+                               $this->loadFromDB( self::READ_NORMAL );
+
+                               $fields = $this->getCacheFields( '' );
+                               $cacheVal['fileExists'] = $this->fileExists;
+                               if ( $this->fileExists ) {
+                                       foreach ( $fields as $field ) {
+                                               $cacheVal[$field] = $this->$field;
+                                       }
+                               }
+                               // Strip off excessive entries from the subset of fields that can become large.
+                               // If the cache value gets to large it will not fit in memcached and nothing will
+                               // get cached at all, causing master queries for any file access.
+                               foreach ( $this->getLazyCacheFields( '' ) as $field ) {
+                                       if ( isset( $cacheVal[$field] )
+                                               && strlen( $cacheVal[$field] ) > 100 * 1024
+                                       ) {
+                                               unset( $cacheVal[$field] ); // don't let the value get too big
+                                       }
+                               }
 
-               $key = $this->getCacheKey();
-               if ( !$key ) {
-                       return;
-               }
+                               if ( $this->fileExists ) {
+                                       $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->timestamp ), $ttl );
+                               } else {
+                                       $ttl = $cache::TTL_DAY;
+                               }
 
-               $fields = $this->getCacheFields( '' );
-               $cacheVal = [ 'version' => MW_FILE_VERSION ];
-               $cacheVal['fileExists'] = $this->fileExists;
+                               return $cacheVal;
+                       },
+                       [ 'version' => self::VERSION ]
+               );
 
+               $this->fileExists = $cachedValues['fileExists'];
                if ( $this->fileExists ) {
-                       foreach ( $fields as $field ) {
-                               $cacheVal[$field] = $this->$field;
-                       }
+                       $this->setProps( $cachedValues );
                }
 
-               // Strip off excessive entries from the subset of fields that can become large.
-               // If the cache value gets to large it will not fit in memcached and nothing will
-               // get cached at all, causing master queries for any file access.
+               $this->dataLoaded = true;
+               $this->extraDataLoaded = true;
                foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                       if ( isset( $cacheVal[$field] ) && strlen( $cacheVal[$field] ) > 100 * 1024 ) {
-                               unset( $cacheVal[$field] ); // don't let the value get too big
-                       }
+                       $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
                }
-
-               // Cache presence for 1 week and negatives for 1 day
-               $wanCache = ObjectCache::getMainWANInstance();
-               if ( $this->fileExists ) {
-                       $ttl = $wanCache::TTL_WEEK;
-                       $ttl = $wanCache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->timestamp ), $ttl );
-               } else {
-                       $ttl = $wanCache::TTL_DAY;
-               }
-               $wanCache->set( $key, $cacheVal, $ttl, $cacheSetOpts );
        }
 
        /**
@@ -551,13 +536,13 @@ class LocalFile extends File {
         */
        function load( $flags = 0 ) {
                if ( !$this->dataLoaded ) {
-                       if ( ( $flags & self::READ_LATEST ) || !$this->loadFromCache() ) {
-                               $opts = Database::getCacheSetOptions( $this->repo->getSlaveDB() );
+                       if ( $flags & self::READ_LATEST ) {
                                $this->loadFromDB( $flags );
-                               $this->saveToCache( $opts );
+                       } else {
+                               $this->loadFromCache();
                        }
-                       $this->dataLoaded = true;
                }
+
                if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
                        // @note: loads on name/timestamp to reduce race condition problems
                        $this->loadExtraFromDB();
@@ -773,7 +758,7 @@ class LocalFile extends File {
 
                if ( $type == 'text' ) {
                        return $this->user_text;
-               } elseif ( $type == 'id' ) {
+               } else { // id
                        return (int)$this->user;
                }
        }
@@ -1628,7 +1613,9 @@ class LocalFile extends File {
                        $sha1 = $repo->isVirtualUrl( $srcPath )
                                ? $repo->getFileSha1( $srcPath )
                                : FSFile::getSha1Base36FromPath( $srcPath );
-                       $dst = $repo->getBackend()->getPathForSHA1( $sha1 );
+                       /** @var FileBackendDBRepoWrapper $wrapperBackend */
+                       $wrapperBackend = $repo->getBackend();
+                       $dst = $wrapperBackend->getPathForSHA1( $sha1 );
                        $status = $repo->quickImport( $src, $dst );
                        if ( $flags & File::DELETE_SOURCE ) {
                                unlink( $srcPath );
@@ -2499,6 +2486,7 @@ class LocalFileRestoreBatch {
         * @return FileRepoStatus
         */
        public function execute() {
+               /** @var Language */
                global $wgLang;
 
                $repo = $this->file->getRepo();
index 6f418d3..6fa270a 100644 (file)
@@ -22,7 +22,7 @@
        "config-page-dbsettings": "Sazê Database",
        "config-page-name": "Name",
        "config-page-options": "Weçinegi",
-       "config-page-install": "Bıselagne",
+       "config-page-install": "Bar ke",
        "config-page-complete": "Temamyayo",
        "config-page-restart": "Barkerdışi fına ser kı",
        "config-page-readme": "Mı bıwane",
index fca2977..7c50e82 100644 (file)
@@ -6,13 +6,14 @@
                        "सरोज कुमार ढकाल",
                        "Ganesh Paudel",
                        "बिप्लब आनन्द",
-                       "Nirjal stha"
+                       "Nirjal stha",
+                       "राम प्रसाद जोशी"
                ]
        },
        "config-desc": "मेडियाविकिको लागि स्थापक",
        "config-title": "मेडिया विकि $1 स्थापना",
        "config-information": "जानकारी",
-       "config-localsettings-badkey": "तपाà¤\87लà¥\87 à¤¦à¤¿à¤¨à¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\95à¥\81नà¥\8dà¤\9cà¥\80 à¤\97लत à¤\9b ।",
+       "config-localsettings-badkey": "तपाà¤\88à¤\82लà¥\87 à¤¦à¤¿à¤\8fà¤\95à¥\8b à¤\95à¥\81à¤\82à¤\9cà¥\80 à¤®à¤¿à¤²à¥\87न ।",
        "config-your-language": "तपाईंको भाषा:",
        "config-your-language-help": "इन्स्टल गर्दा उपयोग गर्ने भाषा छान्नुहोस् ।",
        "config-wiki-language": "विकि भाषाहरू",
index 035baac..b4d9c70 100644 (file)
@@ -395,7 +395,8 @@ class Parser {
         * @param int $revid Number to pass in {{REVISIONID}}
         * @return ParserOutput A ParserOutput
         */
-       public function parse( $text, Title $title, ParserOptions $options,
+       public function parse(
+               $text, Title $title, ParserOptions $options,
                $linestart = true, $clearState = true, $revid = null
        ) {
                /**
@@ -462,6 +463,10 @@ class Parser {
                        }
                }
 
+               # Done parsing! Compute runtime adaptive expiry if set
+               $this->mOutput->finalizeAdaptiveCacheExpiry();
+
+               # Warn if too many heavyweight parser functions were used
                if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
                        $this->limitationWarn( 'expensive-parserfunction',
                                $this->mExpensiveFunctionCount,
@@ -3144,14 +3149,17 @@ class Parser {
                                                $context->setUser( User::newFromName( '127.0.0.1', false ) );
                                        }
                                        $context->setLanguage( $this->mOptions->getUserLangObj() );
-                                       $ret = SpecialPageFactory::capturePath( $title, $context, $this->getLinkRenderer() );
+                                       $ret = SpecialPageFactory::capturePath(
+                                               $title, $context, $this->getLinkRenderer() );
                                        if ( $ret ) {
                                                $text = $context->getOutput()->getHTML();
                                                $this->mOutput->addOutputPageMetadata( $context->getOutput() );
                                                $found = true;
                                                $isHTML = true;
                                                if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
-                                                       $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+                                                       $this->mOutput->updateRuntimeAdaptiveExpiry(
+                                                               $specialPage->maxIncludeCacheTime()
+                                                       );
                                                }
                                        }
                                } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
index f052812..9dfa97c 100644 (file)
@@ -209,9 +209,21 @@ class ParserOutput extends CacheTime {
        /** @var integer|null Assumed rev ID for {{REVISIONID}} if no revision is set */
        private $mSpeculativeRevId;
 
+       /** @var integer Upper bound of expiry based on parse duration */
+       private $mMaxAdaptiveExpiry = INF;
+
        const EDITSECTION_REGEX =
                '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
 
+       // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
+       // Current values imply that m=3933.333333 and b=-333.333333
+       // See https://www.nngroup.com/articles/website-response-times/
+       const PARSE_FAST_SEC = .100; // perceived "fast" page parse
+       const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
+       const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
+       const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
+       const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing)
+
        public function __construct( $text = '', $languageLinks = [], $categoryLinks = [],
                $unused = false, $titletext = ''
        ) {
@@ -1037,9 +1049,41 @@ class ParserOutput extends CacheTime {
        }
 
        /**
-        * Save space for serialization by removing useless values
-        * @return array
+        * Lower the runtime adaptive TTL to at most this value
+        *
+        * @param integer $ttl
+        * @since 1.28
+        */
+       public function updateRuntimeAdaptiveExpiry( $ttl ) {
+               $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
+               $this->updateCacheExpiry( $ttl );
+       }
+
+       /**
+        * Call this when parsing is done to lower the TTL based on low parse times
+        *
+        * @since 1.28
         */
+       public function finalizeAdaptiveCacheExpiry() {
+               if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
+                       return; // not set
+               }
+
+               $runtime = $this->getTimeSinceStart( 'wall' );
+               if ( is_float( $runtime ) ) {
+                       $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
+                               / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
+                       // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
+                       $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
+
+                       $adaptiveTTL = min(
+                               max( $slope * $runtime + $point, self::MIN_AR_TTL ),
+                               $this->mMaxAdaptiveExpiry
+                       );
+                       $this->updateCacheExpiry( $adaptiveTTL );
+               }
+       }
+
        public function __sleep() {
                return array_diff(
                        array_keys( get_object_vars( $this ) ),
index 7499853..6806ee5 100644 (file)
@@ -35,6 +35,7 @@ interface SearchIndexField {
         * Do not index this field, just store it.
         */
        const FLAG_NO_INDEX = 8;
+
        /**
         * Get mapping for specific search engine
         * @param SearchEngine $engine
index fb1943f..06be7bc 100644 (file)
@@ -388,13 +388,29 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                        // unless they are emailing themselves, in which case one
                        // copy of the message is sufficient.
                        if ( $data['CCMe'] && $to != $from ) {
-                               $cc_subject = $context->msg( 'emailccsubject' )->rawParams(
+                               $ccTo = $from;
+                               $ccFrom = $from;
+                               $ccSubject = $context->msg( 'emailccsubject' )->rawParams(
                                        $target->getName(), $subject )->text();
-
-                               // target and sender are equal, because this is the CC for the sender
-                               Hooks::run( 'EmailUserCC', [ &$from, &$from, &$cc_subject, &$text ] );
-
-                               $ccStatus = UserMailer::send( $from, $from, $cc_subject, $text );
+                               $ccText = $text;
+
+                               Hooks::run( 'EmailUserCC', [ &$ccTo, &$ccFrom, &$ccSubject, &$ccText ] );
+
+                               if ( $config->get( 'UserEmailUseReplyTo' ) ) {
+                                       $mailFrom = new MailAddress(
+                                               $config->get( 'PasswordSender' ),
+                                               wfMessage( 'emailsender' )->inContentLanguage()->text()
+                                       );
+                                       $replyTo = $ccFrom;
+                               } else {
+                                       $mailFrom = $ccFrom;
+                                       $replyTo = null;
+                               }
+
+                               $ccStatus = UserMailer::send(
+                                       $ccTo, $mailFrom, $ccSubject, $ccText, [
+                                               'replyTo' => $replyTo,
+                               ] );
                                $status->merge( $ccStatus );
                        }
 
index c1c9660..fdbce6e 100644 (file)
        "exif-compression-34712": "جيه بي إي جي2000",
        "exif-copyrighted-true": "محفوظ الحقوق",
        "exif-copyrighted-false": "حالة حقوق النشر غير مُعرّفة",
+       "exif-photometricinterpretation-0": "أسود وأبيض (الأبيض هو 0)",
        "exif-photometricinterpretation-1": "أسود وأبيض (الأسود 0)",
        "exif-photometricinterpretation-2": "آر جي بي",
+       "exif-photometricinterpretation-3": "لوح الألوان",
+       "exif-photometricinterpretation-4": "قناع الشفافية",
+       "exif-photometricinterpretation-5": "مفصول (ربما CMYK)",
        "exif-photometricinterpretation-6": "واي سب سر",
+       "exif-photometricinterpretation-32803": "مصفوفة فلترة الألوان",
+       "exif-photometricinterpretation-34892": "خام خطي",
        "exif-unknowndate": "تاريخ غير معروف",
        "exif-orientation-1": "عادي",
        "exif-orientation-2": "مقلوبة أفقياً",
        "autoredircomment": "تحويل إلى [[$1]]",
        "autosumm-new": "أنشأ الصفحة ب'$1'",
        "autosumm-newblank": "أنشأ صفحة فارغة",
-       "size-bytes": "$1 بايت",
+       "size-bytes": "$1 {{PLURAL:$1|بايت}}",
        "size-kilobytes": "$1 كيلوبايت",
        "size-megabytes": "$1 ميجابايت",
        "size-gigabytes": "$1 جيجابايت",
        "size-exabytes": "$1 إكسابايت",
        "size-zetabytes": "$1 زيتابايت",
        "size-yottabytes": "$1 يوتابايت",
+       "size-pixel": "$1 {{PLURAL:$1|بكسل}}",
        "bitrate-bits": "$1بيت لكل ثانية",
        "bitrate-kilobits": "$1كيلوبيت لكل ثانية",
        "bitrate-megabits": "$1ميجابيت لكل ثانية",
index 1dfc717..14bf745 100644 (file)
        "timezone-local": "Мясцовы",
        "duplicate-defaultsort": "Папярэджаньне: Ключ сартыроўкі па змоўчваньні «$2» замяняе папярэдні ключ сартыроўкі па змоўчваньні «$1».",
        "duplicate-displaytitle": "<strong>Папярэджаньне:</strong> назва для адлюстраваньня «$2» перапісвае ранейшую назву для адлюстраваньня «$1».",
+       "restricted-displaytitle": "<strong>Увага:</strong> назва для адлюстраваньня «$1» была праігнараваная, бо яна не супадае зь цяперашняй назвай старонкі.",
        "invalid-indicator-name": "<strong>Памылка:</strong> атрыбут <code>name</code> індыкатараў статусу старонкі ня мусіць быць пустым.",
        "version": "Вэрсія",
        "version-extensions": "Усталяваныя пашырэньні",
        "api-error-nomodule": "Унутраная памылка: ня выбраны модуль загрузкі.",
        "api-error-ok-but-empty": "Унутраная памылка: няма адказу ад сэрвэра.",
        "api-error-overwrite": "Замена існуючага файла забароненая.",
+       "api-error-ratelimited": "Вы спрабуеце загрузіць за кароткі час болей файлаў, чым дазваляе вікі.\nКалі ласка, паспрабуйце яшчэ раз празь некалькі хвілінаў.",
        "api-error-stashfailed": "Унутраная памылка: сэрвэр ня змог захаваць часовы файл.",
        "api-error-publishfailed": "Унутраная памылка: сэрвэр ня змог захаваць часловы файл.",
        "api-error-stasherror": "Падчас загрузкі файла ў сховішча адбылася памылка.",
        "api-error-unknownerror": "Невядомая памылка: «$1».",
        "api-error-uploaddisabled": "Загрузка ў гэтую вікі адключаная.",
        "api-error-verification-error": "Гэты файл можа быць пашкоджаны, ці мае няслушнае пашырэньне.",
+       "api-error-was-deleted": "Файл з такой назвай ужо загружаўся раней і быў потым выдалены.",
        "duration-seconds": "$1 {{PLURAL:$1|сэкунда|сэкунды|сэкундаў}}",
        "duration-minutes": "$1 {{PLURAL:$1|хвіліна|хвіліны|хвілінаў}}",
        "duration-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "mediastatistics": "Статыстыка мэдыяфайлаў",
        "mediastatistics-summary": "Статыстыка тыпаў загружаных файлаў. Яна ўключае толькі актуальныя вэрсіі файлаў. Старыя і выдаленыя вэрсіі ня ўлічваюцца.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Агульны памер файлу для гэтага разьдзелу: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%).",
        "mediastatistics-table-mimetype": "MIME-тып",
        "mediastatistics-table-extensions": "Магчымыя пашырэньні",
        "mediastatistics-table-count": "Колькасьць файлаў",
index 13b9137..2f0ea5e 100644 (file)
        "category-empty": "''Diese Kategorie enthält zurzeit keine Seiten oder Medien.''",
        "hidden-categories": "{{PLURAL:$1|Versteckte Kategorie|Versteckte Kategorien}}",
        "hidden-category-category": "Versteckte Kategorien",
-       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Unterkategorie:|{{PLURAL:$1|Folgende Unterkategorie ist eine von insgesamt $2 Unterkategorien in dieser Kategorie:|Es werden $1 von insgesamt $2 Unterkategorien in dieser Kategorie angezeigt.}}}}",
+       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Unterkategorie.|Diese Kategorie enthält {{PLURAL:$1|folgende Unterkategorie|die folgende $1 Unterkategorien}}, von $2 insgesamt.}}",
        "category-subcat-count-limited": "Diese Kategorie enthält folgende {{PLURAL:$1|Unterkategorie|$1 Unterkategorien}}:",
-       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Seite:|{{PLURAL:$1|Folgende Seite ist eine von insgesamt $2 Seiten in dieser Kategorie:|Es werden $1 von insgesamt $2 Seiten in dieser Kategorie angezeigt.}}}}",
-       "category-article-count-limited": "Folgende {{PLURAL:$1|Seite ist|$1 Seiten sind}} in dieser Kategorie enthalten:",
-       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Datei:|{{PLURAL:$1|Folgende Datei ist eine von insgesamt $2 Dateien in dieser Kategorie:|Es werden $1 von insgesamt $2 Dateien in dieser Kategorie angezeigt:}}}}",
+       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält nur die folgende Seite.|Folgende {{PLURAL:$1|Seite ist| $1 Seiten sind}} in dieser Kategorie, von $2 insgesamt.}}",
+       "category-article-count-limited": "{{PLURAL:$1|Folgende Seite ist|Die folgenden $1 Seiten sind}} in dieser Kategorie enthalten:",
+       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Datei.|Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie, von $2 insgesamt.}}",
        "category-file-count-limited": "Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie enthalten:",
        "listingcontinuesabbrev": "(Fortsetzung)",
        "index-category": "Indexierte Seiten",
index 9026636..547d300 100644 (file)
        "about": "Heqa cı de",
        "article": "Pela zerreki",
        "newwindow": "(pençereyê newey de beno a)",
-       "cancel": "İbtal kı",
+       "cancel": "Bıtexelne",
        "moredotdotdot": "Vêşi...",
        "morenotlisted": "Vêşi lista nêbi...",
        "mypage": "Pele",
        "anontalk": "Werênayış",
        "navigation": "Pusula",
        "and": "&#32;u",
-       "qbfind": "Bıvin",
+       "qbfind": "Bıvêne",
        "qbbrowse": "Çım ra viyarne",
        "qbedit": "Bıvurne",
        "qbpageoptions": "Ena pele",
        "view-foreign": "$1 de bıvêne",
        "edit": "Bıvurne",
        "edit-local": "Şınasnayışê lokali bıvurne",
-       "create": "Bıvıraz",
+       "create": "Vıraze",
        "create-local": "Şınasnayışê lokali cı ke",
        "editthispage": "Ena pele bıvurne",
        "create-this-page": "Na pele bınuse",
        "specialpage": "Pela xısusiye",
        "personaltools": "Hacetê şexsiy",
        "articlepage": "Pera zerreki bıvin",
-       "talk": "Hurênayış",
+       "talk": "Werênayış",
        "views": "Asayışi",
        "toolbox": "Haceti",
        "userpage": "Pela karberi bıvêne",
        "viewhelppage": "Pera peşti bıvin",
        "categorypage": "Pela kategoriya bıasne",
        "viewtalkpage": "Werênayışi bıvêne",
-       "otherlanguages": "Tayna zıwanan dı",
+       "otherlanguages": "Zıwananê binan de",
        "redirectedfrom": "($1 ra kırışı yê)",
        "redirectpagesub": "Pela berdışi",
        "redirectto": "Beno hetê:",
        "sort-descending": "Rêzkerdışo kêmbiyaye",
        "sort-ascending": "Rêzkerdışo zêdiyaye",
        "nstab-main": "Pele",
-       "nstab-user": "Pera karberi",
+       "nstab-user": "Pela karberi",
        "nstab-media": "Pela medya",
-       "nstab-special": "Pera spesiyal",
+       "nstab-special": "Pela xısusiye",
        "nstab-project": "Pela proceyi",
        "nstab-image": "Dosya",
        "nstab-mediawiki": "Mesac",
        "botpasswords-label-appid": "Nameyê boti:",
        "botpasswords-label-create": "Vıraze",
        "botpasswords-label-update": "Rocane ke",
-       "botpasswords-label-cancel": "İbtal ke",
+       "botpasswords-label-cancel": "Bıtexelne",
        "botpasswords-label-delete": "Bestere",
        "botpasswords-label-resetpassword": "Parola raçarne",
        "botpasswords-label-grants-column": "Dayen",
        "resetpass_forbidden": "parolayi nêvuryayi",
        "resetpass-no-info": "şıma gani hesab akere u hona bıeşke bırese cı",
        "resetpass-submit-loggedin": "Parola bıvurne",
-       "resetpass-submit-cancel": "İbtal ke",
+       "resetpass-submit-cancel": "Bıtexelne",
        "resetpass-wrong-oldpass": "parolayo parola maqbul niyo.\nşıma ya parolaye xo vurnayo ya zi parolayo muwaqqat waşto.",
        "resetpass-recycled": "Parolaya şımaya newiye wa paroloya şımaya verêne ra ferqıne bo.",
        "resetpass-temp-emailed": "E postaya rışyayê yubkoda şıma ronıştış akerdo.  Ronıştışi xo temammkerdışi rê yu parolaya newi lazım a",
        "summary": "Xulasa:",
        "subject": "Mewzu:",
        "minoredit": "Vurriyayışo werdiyo",
-       "watchthis": "Seyr kı",
-       "savearticle": "Qeyd kı",
+       "watchthis": "Na pele seyr ke",
+       "savearticle": "Qeyd ke",
        "savechanges": "Vurnayışan qeyd ke",
        "publishpage": "Perer bıhesırne",
        "publishchanges": "Vurnayışa vıla ke",
        "preview": "Verqayt",
-       "showpreview": "Verasayışi bıvin",
-       "showdiff": "Vuryayışa bıasne",
+       "showpreview": "Verqayti bıvêne",
+       "showdiff": "Vurriyayışan bımocne",
        "anoneditwarning": "<strong>İqaz:</strong> Şıma be hesabê xo nêkewtê cı. \nAdresê şımayê IP tarixê vırnayışê na pele de do qeyd bo. Eke şıma <strong>[$1 cıkewê]</strong> ya zi <strong>[$2 hesab vırazê]</strong>, vurnayışê şıma be zewbina kare ra nameyê şıma rê bar beno.",
        "anonpreviewwarning": "\"Şıma be hesabê xo nêkewtê cı. Eke qeyd kerê, adresê şımaê IP tarixê vırnayışê na pele de do qeyd bo.\"",
        "missingsummary": "'''DİQET:''' Şıma jû xulasa nênuşte.\nEke şıma \"{{int:savearticle}}\" reyna bıtıknê, vırnayışê şıma bê xulasa qeyd beno.",
        "template-protected": "(kılit biyo)",
        "template-semiprotected": "(nimey ena pele kılit biya)",
        "hiddencategories": "Ena per de {{PLURAL:$1|1 kategoriyo nımıte|$1 kategoriyê nımıtey}} muhtewa benê:",
-       "edittools": "<!-- Text here will be shown below edit and upload forms. -->",
+       "edittools": "<div id=\"specialcharss\" class=\"toccolours specialchars\" style=\"margin-top:.5em; padding: .3em .5em; font-size: 100%; color:#aaa; text-align:left;\" title=\"{{int:bw-edittools-tooltip}}\">\n<p class=\"specialbasic\" id=\"Standard\">\n'''{{int:bw-edittools-lead-in}}''' \n<charinsert>Á á É é Í í Ó ó Ú ú Ý ý</charinsert> –\n<charinsert>À à È è Ì ì Ò ò Ù ù </charinsert> –\n<charinsert> â Ê ê Î î Ô ô Û û </charinsert> –\n<charinsert>Ä ä Ë ë Ï ï Ö ö Ü ü Ÿ ÿ</charinsert> –\n<charinsert>Æ æ Ø ø Œ œ ẞ ß </charinsert> –\n<charinsert>Å å Ů ů </charinsert> –\n<charinsert>àã Ẽ ẽ ɛ̃ Ĩ ĩ Ñ ñ Õ õ ɔ̃ Ũ ũ </charinsert> –\n<charinsert>Рð Þ þ </charinsert> –\n<charinsert>Ç ç Ģ ģ Ķ ķ Ļ ļ Ņ ņ Ŗ ŗ Ş ş Ţ ţ </charinsert> –\n<charinsert>Ć ć Ĺ ĺ Ń ń Ŕ ŕ Ś ś Ý ý Ź ź </charinsert> –\n<charinsert>Č č Ď ď Ľ ľ Ň ň Ř ř Š š Ť ť Ž ž </charinsert> –\n<charinsert>Ǎ ǎ Ě ě Ǐ ǐ Ǒ ǒ Ǔ ǔ </charinsert> –\n<charinsert>Ā ā Ē ē Ī ī Ō ō Ū ū </charinsert> –\n<charinsert>ǖ ǘ ǚ ǜ </charinsert> –\n<charinsert>Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŵ ŵ Ŷ ŷ </charinsert> –\n<charinsert>Ă ă Ğ ğ Ŭ ŭ </charinsert> –\n<charinsert>Ċ ċ Ė ė Ġ ġ Għ għ İ ı Ż ż </charinsert> –\n<charinsert>Ą ą Ę ę Į į Ų ų </charinsert> –\n<charinsert>Ő ő Ű ű </charinsert> –\n<charinsert>Đ đ Ħ ħ Ł ł Ŀ ŀ </charinsert> –\n<charinsert>Ɖ ɖ Ɛ ɛ Ƒ ƒ Ɣ ɣ Ŋ ŋ Ɔ ɔ Ʋ ʋ </charinsert> -\n<charinsert>Ə ə </charinsert> –\n<charinsert>– — ’</charinsert> –\n<charinsert>~ | ° ¹ ² ³ ⅛ ¼ ⅓ ⅜ ½ ⅝ ¾ ⅔ ⅞ € $ ¥ £ † × ← → ↔ ↑ ± ≠ © ® ™ ‰ «+» ‹+› „+“ „+” ‚+‘ ¡ ¿ …</charinsert> –\n<charinsert>&amp;nbsp; &nbsp; [[Category:+]] #REDIRECT[[+]] {{msg-mw|+|notext=1}} &#33;!FUZZY!! ~~~~  &lt;nowiki>+</nowiki></charinsert>\n<charinsert>ڈ ڑ ٹ </charinsert>\n<charinsert>ټ څ ځ ډ ړ ږ ښ ڼ ؤ ي ې ۍ ئ </charinsert>\n<charinsert>{{{+}}} {{+}} {{subst:+}} <noinclude>+</noinclude></charinsert>\n<charinsert>&lt;!--&nbsp;+&nbsp;--> &lt;br&nbsp;/></charinsert>\n</p></div>",
        "edittools-upload": "-",
        "nocreatetext": "{{SITENAME}}, Pelê neweyi vıraştış re destur çino.\nşıma eşkeni tepiya şêri u eke şıma qayd biyaye yê [[Special:UserLogin|şıma eşkeni hesab akeri]], eke niye [[Special:UserLogin|şıma eşkeni qayd bıbiy]].",
        "nocreate-loggedin": "Desturê şıma çıniyo ke pelanê neweyan vırazê.",
        "skin-preview": "Verqayt",
        "datedefault": "Tercih çıniyo",
        "prefs-labs": "Xacetê labs",
-       "prefs-user-pages": "Pera Karberi",
-       "prefs-personal": "Pera  karberi",
+       "prefs-user-pages": "Pelê karberi",
+       "prefs-personal": "Pela karberi",
        "prefs-rc": "Vurriyayışê peyêni",
        "prefs-watchlist": "Lista seyrkerdışi",
        "prefs-editwatchlist": "Lista seyrkerdışi bıvurne",
        "rcnotefrom": "Cêr de <strong>$2</strong> ra nata {{PLURAL:$5|vurnayışiyê}} asenê (tewr vêşi <strong>$1</strong> asenê) <strong>$3, $4</strong>",
        "rclistfrom": "$3 $2 ra tepiya vurnayışanê neweyan bımocne",
        "rcshowhideminor": "vurriyayışê werdi $1",
-       "rcshowhideminor-show": "Bıasne",
+       "rcshowhideminor-show": "Bımocne",
        "rcshowhideminor-hide": "Bınımne",
        "rcshowhidebots": "botan $1",
-       "rcshowhidebots-show": "Bıasne",
+       "rcshowhidebots-show": "Bımocne",
        "rcshowhidebots-hide": "Bınımne",
        "rcshowhideliu": "karberê qeydbiyayeyi $1",
        "rcshowhideliu-show": "Bımocne",
        "rcshowhideliu-hide": "Bınımne",
        "rcshowhideanons": "karberê bênameyi $1",
-       "rcshowhideanons-show": "Bıasne",
+       "rcshowhideanons-show": "Bımocne",
        "rcshowhideanons-hide": "Bınımne",
        "rcshowhidepatr": "$1 vurnayışê ke dewriya geyrayê",
        "rcshowhidepatr-show": "Bımocne",
        "recentchanges-page-added-to-category": "[[:$1]] kerd kategoriye miyan",
        "recentchanges-page-removed-from-category": "[[:$1]] kategoriye ra vet",
        "autochange-username": "MediaWiki vurnayışo otomatik",
-       "upload": "Dosya bıselagne",
-       "uploadbtn": "Dosya bıselagne",
+       "upload": "Dosya bar ke",
+       "uploadbtn": "Dosya bar ke",
        "reuploaddesc": "Barkerdışi iptal ke u peyser şo formê barkerdışi",
        "upload-tryagain": "Deskripyonê dosyayî ke vurîya ey qeyd bike",
        "uploadnologin": "Şıma cıkewtış nêvıraşto",
        "upload-too-many-redirects": "Eno URL de zaf redireksiyonî esto.",
        "upload-http-error": "Yew ğeletê HTTPî biyo: $1",
        "upload-copy-upload-invalid-domain": "Na domain ra kopyayê barkerdışanê nêbenê.",
-       "upload-dialog-title": "Dosya bıselagne",
+       "upload-dialog-title": "Dosya bar ke",
        "upload-dialog-button-cancel": "Bıterkın",
        "upload-dialog-button-done": "Temam",
        "upload-dialog-button-save": "Bışevekne",
-       "upload-dialog-button-upload": "Bıselagne",
+       "upload-dialog-button-upload": "Bar ke",
        "upload-form-label-infoform-title": "Teferuati",
        "upload-form-label-infoform-name": "Name",
        "upload-form-label-infoform-description": "Şınasnayış",
        "mimesearch": "MIME bigêre",
        "mimesearch-summary": "Na pele, dosyayanê MIME goreyê tewran ra parzûn kena. Cıkewtış: tewrê zerreki/tewro bınên ya zi tewrê zerreki/*, nımune: <code>image/jpeg</code>.",
        "mimetype": "Babetê NIME",
-       "download": "Bıselagne",
+       "download": "Bar ke",
        "unwatchedpages": "Pelanê seyrnibiyeyî",
        "listredirects": "Listeya Hetenayışan",
        "listduplicatedfiles": "Lista dosyeyanê ke kopyaya cı vêniyena",
        "removedwatchtext": "Ena pela \"[[:$1]]\" biya wedariya [[Special:Watchlist|listeyê seyr-kerdışi şıma]].",
        "removedwatchtext-short": "Pera $1`i listeya seyran de şıma ra wedari yê",
        "watch": "Seyr ke",
-       "watchthispage": "Seyr kı",
+       "watchthispage": "Na pele seyr ke",
        "unwatch": "Teqib meke",
        "unwatchthispage": "temaşa kerdışê peli vındarn.",
        "notanarticle": "mebhesê peli niyo",
        "restriction-edit": "Bıvurne",
        "restriction-move": "Bıkırış",
        "restriction-create": "Bıvıraz",
-       "restriction-upload": "Bıselagne",
+       "restriction-upload": "Bar ke",
        "restriction-level-sysop": "tam pawiyayo",
        "restriction-level-autoconfirmed": "nêm pawiyayo",
        "restriction-level-all": "kamci be sewiya",
        "namespace_association": "Heruna nameyanê elaqedaran",
        "tooltip-namespace_association": "Herunda canemiya elekeyın nışan kerdışi sero qıse kerdışi yana zerre dekerdışi rê ena dora tesdiqi nışan kerê",
        "blanknamespace": "(Ser)",
-       "contributions": "İştirakê {{GENDER:$1|karber}}i",
+       "contributions": "İştırakê {{GENDER:$1|karber}}i",
        "contributions-title": "Dekerdenê karber de $1",
        "mycontris": "İştıraki",
        "anoncontribs": "İştıraki",
        "sp-contributions-newbies-title": "Îştîrakê karberî ser hesabê neweyî",
        "sp-contributions-blocklog": "qeydê kılitbiyayeyi",
        "sp-contributions-deleted": "iştırakê karberi esterdi",
-       "sp-contributions-uploads": "Selagneyey",
+       "sp-contributions-uploads": "Barkerdışi",
        "sp-contributions-logs": "qeydi",
        "sp-contributions-talk": "werênayış",
        "sp-contributions-userrights": "idareyê heqanê karberan",
        "tooltip-ca-nstab-special": "Na pelaya xas a, şıma nêşenê sero vurnayış bıkerê",
        "tooltip-ca-nstab-project": "Pela proceyi bıvêne",
        "tooltip-ca-nstab-image": "Pera dosyayer bıvin",
-       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bıasne",
+       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bımocne",
        "tooltip-ca-nstab-template": "Şabloni bıvêne",
        "tooltip-ca-nstab-help": "Pela peşti bıvêne",
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Kategoriya nımıtiye|Kategoriyê nımıtey}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Şablono|Şablonê}} ke mocniyenê ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|1 Pele|$1 Pelan}} de bestiya pıra",
-       "pageinfo-toolboxlink": "Zanayışa perer",
+       "pageinfo-toolboxlink": "Melumatê pele",
        "pageinfo-redirectsto": "Beno hetê",
        "pageinfo-redirectsto-info": "melumat",
        "pageinfo-contentpage": "Zey jû pela zerreki hesebiyena",
        "fileduplicatesearch-result-1": "Dosyayê ''$1î'' de hem-kopya çini yo.",
        "fileduplicatesearch-result-n": "Dosyayê ''$1î'' de {{PLURAL:$2|1 hem-kopya|$2 hem-kopyayî'}} esto.",
        "fileduplicatesearch-noresults": "Ebe namey \"$1\" ra dosya nêdiyayê.",
-       "specialpages": "Page bağsey",
+       "specialpages": "Pelê xısusiyi",
        "specialpages-note-top": "Kıtabek",
        "specialpages-note": "* Pelê xasê normali.\n* <span class=\"mw-specialpagerestricted\">Pelê xasê nımıtey.</span>",
        "specialpages-group-maintenance": "Raporê pawıtışi",
        "feedback-bugcheck": "Harika! Sadece [xırabina ke $1 ] çınyayışê cı kontrol keno.",
        "feedback-bugnew": "Mı qontrol ke. Xetaya newi xeber ke",
        "feedback-bugornote": "Jew mersela teferruato teknik esta şıma reca malumatê şıma hazıro se [ $1  jew xırab rapor] bıvinê.Zewbi zi, formê cerê xo rê şenê karfiyê. Vatışê xo pela da \"[ $3  $2 ]\", namey karber dê xoya piya u wasteriya karfiye.",
-       "feedback-cancel": "İbtal kı",
+       "feedback-cancel": "Bıtexelne",
        "feedback-close": "Biya star",
        "feedback-error1": "Xeta: API ra neticey ne vıcyay",
        "feedback-error2": "Xeta: Timar kerdış nebı",
index 379da19..08857b4 100644 (file)
@@ -6,7 +6,8 @@
                        "रमेश सिंह बोहरा",
                        "राम प्रसाद जोशी",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Nirajan pant"
                ]
        },
        "tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
        "versionrequiredtext": "ये पाना प्रयोग गर्नका लागि MediaWiki $1 संस्करण चाहिन्छ ।\nहेर  [[Special:Version|version page]]",
        "ok": "भयो",
        "retrievedfrom": " \"$1\" बठे निकालिया",
-       "youhavenewmessages": "तमखी लेखा($3)मी $1($2) छ ।",
+       "youhavenewmessages": "{{PLURAL:$3|तम सित छन}} $1 ($2)।",
        "youhavenewmessagesfromusers": "तमखी लेखा {{PLURAL:$3|प्रयोगकर्ता|$3 प्रयोगकर्तान}}($2)बठे$1",
        "youhavenewmessagesmanyusers": "तमलाई धेरै प्रयोगकर्ताहरू($2) बठे $1 छ ।",
        "newmessageslinkplural": "{{PLURAL:$1|एक नौलो रैबार|999=नौला रैबारहरू}}",
        "mailmypassword": "पासवर्ड पूर्वनिर्धारित गर",
        "passwordremindertitle": "{{SITENAME}}का लागि नयाँ अस्थायी पासवर्ड",
        "passwordremindertext": "कसैले (सायद तमी, IP ठेगाना $1 बाट), {{SITENAME}}($4) को लागि नौलो पासवर्ड अनुरोध गर्या छ । प्रयोगकर्ता \"$2\" को लागि नौलो अस्थायी पासवर्ड \"$3\"तयार पारिया छ । यदि यो तमरो इच्छामी भयाको भया अहिले तमीले लगइन गरीबर नौलो पासवर्ड छान्नु पड्ड्या हुन्छ ।\nतमरो अस्थायी पासवर्ड  {{PLURAL:$5|एक दिन|$5 दिनहरू पछि}} अमान्य हुन्याछ ।\n\nयदि कोही अरुले नै अनुरोध गर्याको हो भण्या , या तमीले आफ्नो पासवर्ड सम्झ्यौ भण्या, अथवा\nत्यैलाई परिवर्तन गर्न चाहन्नौ भण्या, तमीले यो सन्देसको वेवास्ता गद्दसक्द्याहौ र पुरानै पासवर्ड प्रयोग गरिरहन सक्द्याहौ ।",
-       "blocked-mailpassword": "तमरà¥\8b IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤\97ायाà¤\95à¥\8b à¤\9b, à¤° à¤¤à¥\8dयसà¥\88लà¥\87 à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\95à¥\8b à¤²à¤¾à¤\97ि à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97दà¥\8dया à¤\85नà¥\81मति à¤\9bà¥\88न ।",
+       "blocked-mailpassword": "तमरा IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤¾à¤\87राà¤\87à¤\9b। à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\87, à¤¤à¤®à¤°à¤¾ IP à¤ à¥\87à¤\97ाना à¤¬à¤ à¥\87à¤\87 à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\85दà¥\8dदà¥\8dया à¤\85नà¥\81मति à¤\86थिन।",
        "mailerror": " चिठी :$1 पठाउँदा गल्ती भयो",
        "noemailprefs": "निम्न सुविधाहरू राम्डरी काम गद्दको लागि तमरो रोजाईमी आफ्नो ई-मेल ठेगाना खुलाओ ।",
        "emailconfirmlink": "तमरो ई-मेल ठेगाना पक्का गर",
        "passwordreset-email": "इमेल ठेगाना:",
        "passwordreset-emailtitle": "{{SITENAME}}मा खाता विवरण",
        "passwordreset-emailelement": "प्रयोगकर्ताको नाम: \n$1\n\nअस्थाई पासवर्ड: \n$2",
-       "passwordreset-emailsentemail": "पासवरà¥\8dड à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95ा à¤²à¤¾à¤\97ि à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87या à¤\9b।",
+       "passwordreset-emailsentemail": "यदि à¤¯à¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¤à¤® à¤¸à¤¿à¤¤ à¤¸à¤®à¥\8dबनà¥\8dधित à¤\9b à¤­à¤£à¥\8dया, à¤¤à¤¬ à¤¯à¤\95 à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤°à¤¿à¤¸à¥\87à¤\9f à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\8fलà¥\8b।",
        "passwordreset-invalideamil": "अबैध ई-मेल ठेगाना",
        "changeemail": "इमेल ठेगाना बदेल वा हटा",
        "changeemail-header": "आफ्नो इमेल ठेगाना परिवर्तन गद्द यो फारम भर । यैलाई पुष्टि गद्द तमीले आफ्नो पासवर्ड हाल्नु पडन्छ।",
        "updated": "नौला",
        "note": "'''सूचना:'''",
        "continue-editing": "सम्पादन क्षेत्रमी जाओ",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 सम्पादन गरिदै",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझ्यो: $1",
        "yourtext": "तमरा पाठहरू",
        "blanknamespace": "(मुख्य)",
        "contributions": "{{GENDER:$1|प्रयोगकर्ता}}को योगदान",
        "mycontris": "मेरो योगदानहरू",
+       "anoncontribs": "योगदान",
        "month": "महिना बठे (लै पैल्ली):",
        "year": "वर्ष बठे( लौ पैल्ली):",
        "sp-contributions-toponly": "नवीनतम संशोधनका सम्पादनहरू मात्र धेकाओ",
        "whatlinkshere-prev": "{{PLURAL:$1|पैलो|पैलो $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अर्को|अर्को $1}}",
        "whatlinkshere-links": "← लिंकहरू",
-       "whatlinkshere-hideredirs": "$1 à¤\85नà¥\81पà¥\8dरà¥\87षित हुन्छ",
-       "whatlinkshere-hidetrans": "$1 à¤ªà¤¾à¤°à¤¦à¤°à¥\8dशन",
-       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81",
+       "whatlinkshere-hideredirs": "$1 à¤ªà¥\81न:निरà¥\8dदà¥\87शित हुन्छ",
+       "whatlinkshere-hidetrans": "$1 à¤¸à¤®à¥\8dमà¥\80ल",
+       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82",
        "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
        "whatlinkshere-filters": "छानियाका",
        "ipbreason-dropdown": "* ब्लक गर्नुका समान्य कारणहरू\n** झूटो सूचना दियाको\n** पानानबठे सामाग्रीहरू हटायाको\n** बाहिरी जालक्षेत्र (sites)सित नचाहिंदो लिङ्क गर्याको \n** पानानमी बकवास/गाली-गलौच हाल्याको\n** भै धेकाउने व्यवहार/उत्पीडन (सताउने कार्य) गर्याको\n** धेरै गलत खाताहरू बनायाको\n** प्रयोगकर्ता नाम अस्वीकार्य",
        "import-error-edit": "तमलाई सम्पादन गद्या अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-error-create": "तमलाई नयाँ बनाउने अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनहरू}} आयात भयो",
-       "tooltip-pt-userpage": "तमरो प्रयोगकर्ता पानो",
+       "tooltip-pt-userpage": "{{GENDER:|तमरो प्रयोगकर्ता}} पान्नो",
        "tooltip-pt-anonuserpage": "तमी जो IP ठेगानाको रुपमी सम्पादन गद्दै छौ , त्यैको प्रयोगकर्ता पानो निम्न छ :",
-       "tooltip-pt-mytalk": "तमरो कुरडीकानी पानो",
-       "tooltip-pt-preferences": "तमरा अभिरुचिहरू",
+       "tooltip-pt-mytalk": "{{GENDER:|तमरो}} कुरडीकानी पान्नो",
+       "tooltip-pt-preferences": "{{GENDER:|तमरी}} अभिरुचि",
        "tooltip-pt-watchlist": "पृष्ठहरूको सूची जैका फेरबदलहरुलाई तमले पहरा गरिराखेका छौ ।",
-       "tooltip-pt-mycontris": "तमरो योगदानको सूची",
+       "tooltip-pt-mycontris": "{{GENDER:|तमरा}} योगदानअनऐ सूची",
        "tooltip-pt-login": "तमलाई प्रवेशगद्द सुझाव दिइन्छ ; याद अर यो जरुरी आथिन भण्या ।",
        "tooltip-pt-logout": "बाहिर निस्कन्या (लग आउट)",
        "tooltip-pt-createaccount": "तमलाई खाता बनौन लै लग इन अद्द हम हौसला अद्दाउ; काइकि, यो अनिवार्य नाइथी भण्या ।",
index a620724..a298d35 100644 (file)
        "watchthis": "Atenti ĉi tiun paĝon",
        "savearticle": "Konservi paĝon",
        "savechanges": "Konservi ŝanĝojn",
-       "publishpage": "Publikigi paĝon",
+       "publishpage": "Eldoni paĝon",
        "publishchanges": "Eldoni ŝanĝojn",
        "preview": "Antaŭrigardo",
        "showpreview": "Antaŭrigardo",
        "content-json-empty-object": "Malplena objeto",
        "content-json-empty-array": "Malplena tabelo",
        "deprecated-self-close-category": "Paĝoj kun nevalida memferma HTML‑etikedo",
+       "deprecated-self-close-category-desc": "La paĝo enhavas malvalidajn memfermajn HTML-markojn, tiajn kiel <code>&lt;b/></code> aŭ <code>&lt;span/></code>. Ilia konduto baldaŭ estos ŝanĝita por esti kongrua kun la specifiko de HTML5, tial ilia uzo en vikia teksto estas malrekomendata.",
        "duplicate-args-warning": "'''Averto:''' [[:$1]] vokas al [[:$2]] kun pli ol unu valoro por la parametro \"$3\". Nur la lasta liverita valoro estos uzata.",
        "duplicate-args-category": "Paĝoj kun pluroblaj argumentoj en ŝablonvokoj",
        "duplicate-args-category-desc": "La paĝo enhavas uzon de ŝablono kun pluroble uzitaj argumentoj, kiel ekzemple <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> aŭ <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "grant-group-high-volume": "Efektivigi ampleksegajn agojn",
        "grant-group-customization": "Personecigoj kaj preferoj",
        "grant-group-administration": "Efektivigi administrajn agojn",
+       "grant-group-private-information": "Aliru privatan datumon pri vi",
        "grant-group-other": "Diversaj aktivecoj",
        "grant-blockusers": "Bloki kaj malbloki uzantojn",
        "grant-createaccount": "Krei kontojn",
        "grant-highvolume": "Ampleksega redaktado",
        "grant-oversight": "Kaŝi uzantojn kaj forigi reviziaĵojn",
        "grant-patrol": "Patroli ŝanĝojn al pâgoj",
+       "grant-privateinfo": "Aliro al privataj informoj",
        "grant-protect": "Protekti kaj malprotekti paĝojn",
        "grant-rollback": "Malfari ŝanĝojn de paĝoj",
        "grant-sendemail": "Retpoŝti al aliaj uzantoj",
        "watchnologin": "Ne ensalutinta",
        "addwatch": "Aldoniĝi al atentaro",
        "addedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis aldonitaj al via [[Special:Watchlist|atentaro]].",
+       "addedwatchtext-talk": "«[[:$1]]» kaj asociita kun ĝi paĝo estas aldonitaj al via [[Special:Watchlist|atentaro]].",
        "addedwatchtext-short": "La paĝo \"$1\" estis aldonita al via atento-listo.",
        "removewatch": "Forigi el atentaro",
        "removedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis forigita el via [[Special:Watchlist|atentaro]].",
+       "removedwatchtext-talk": "«[[:$1]]» kaj asociita kin ĝi paĝo estas forigitaj el via [[Special:Watchlist|atentaro]].",
        "removedwatchtext-short": "La paĝo \"$1\" estis forigita el via atento-listo.",
        "watch": "Atenti",
        "watchthispage": "Priatenti paĝon",
        "undeletehistorynoadmin": "Ĉi tiu artikolo estis forigita. La kaŭzo por la forigo estas montrata en la malsupra resumo, kune kun detaloj pri la uzantoj, kiuj redaktis ĉi tiun paĝon antaŭ la forigo. La aktuala teksto de ĉi tiuj forigitaj versioj estas atingebla nur de administrantoj.",
        "undelete-revision": "Forigita versio de $1 (ekde $4, $5) fare de $3:",
        "undeleterevision-missing": "Malvalida aŭ malaperita revizio.\nVi verŝajne havas malbonan ligilon, aŭ la revizio eble estis restarigita aŭ forigita de la arkivo.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Unu versio|$1 versioj}} estas nerestarigeblaj, ĉar {{PLURAL:$1|ĝ|il}}ia <code>rev_id</code> jam estas uzata.",
        "undelete-nodiff": "Neniu antaŭa versio troviĝis.",
        "undeletebtn": "Restarigi",
        "undeletelink": "vidi/restarigi",
        "sp-contributions-newbies-sub": "Kontribuoj de novaj uzantoj. Forigitaj paĝoj ne estas montritaj.",
        "sp-contributions-newbies-title": "Kontribuoj de novaj uzantoj",
        "sp-contributions-blocklog": "protokolo de forbaroj",
-       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de uzanto",
-       "sp-contributions-deleted": "forigitaj kontribuoj de uzanto",
+       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de uzant{{GENDER:$1||in}}o",
+       "sp-contributions-deleted": "forigitaj kontribuoj de uzant{{GENDER:$1||in}}o",
        "sp-contributions-uploads": "alŝutoj",
        "sp-contributions-logs": "protokoloj",
        "sp-contributions-talk": "diskuto",
        "linkaccounts-submit": "Ligi kontojn",
        "unlinkaccounts": "Malligi kontojn",
        "unlinkaccounts-success": "La konto estis malligita.",
-       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?"
+       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?",
+       "userjsispublic": "Bonvolu noti: subpaĝoj en JavaScript ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj.",
+       "usercssispublic": "Bonvolu noti: subpaĝoj en CSS ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj."
 }
index 240be82..083585f 100644 (file)
        "passwordreset-email": "E-mail helbidea:",
        "passwordreset-emailtitle": "{{SITENAME}}-rako kontuaren xehetasunak",
        "passwordreset-emailelement": "Erabiltzaile izena: \n$1\n\nBehin-behineko pasahitza: \n$2",
-       "passwordreset-emailsentemail": "Hau zure konturako erregistratuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
+       "passwordreset-emailsentemail": "Hau zure kontuarekin lotuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
        "changeemail": "Aldatu edo kendu e-mail helbidea",
        "changeemail-header": "Bete ezazu inprimaki hau, zure helbide elektronikoa aldatzeko. Zure kontuari helbide elektronikorik elkartuta ez izatea nahi baduzu, utz ezazu hutsik helbide elektroniko berria, inprimakia bidaltzen duzunean.",
        "changeemail-no-info": "Orrialde honetara zuzenean sartzeko izena eman behar duzu.",
        "accmailtext": "[[User talk:$1|$1]]-entzako ausaz sortutako pasahitza $2-(r)a bidali da.\n\n''[[Special:ChangePassword|pasahitz aldaketa]]'' orrialdean alda daiteke, behin barruan sartuta.",
        "newarticle": "(Berria)",
        "newarticletext": "Orrialde hau ez da existitzen oraindik. Orrialde sortu nahi baduzu, beheko koadroan idazten hasi zaitezke (ikus [$1 laguntza orrialdea] informazio gehiagorako). Hona nahi gabe etorri bazara, nabigatzaileko '''atzera''' botoian klik egin.",
-       "anontalkpagetext": "----''Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.''",
+       "anontalkpagetext": "<em>Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.</em>\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.",
        "noarticletext": "Oraindik ez dago testurik orri honetan.\nEdukiz hornitzeko, aukera hauek dituzu: beste orri batzuetan [[Special:Search/{{PAGENAME}}|orri izenburu hau bilatzea]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} lotutako logak bilatzea],\nedo [{{fullurl:{{FULLPAGENAME}}|action=edit}} orri hau sortzea]</span>.",
        "noarticletext-nopermission": "Une honetan ez dago testurik orrialde honetan.\nBeste orrialdeetan [[Special:Search/{{PAGENAME}}|izenburu hau bilatu dezakezu]],\nedo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} erlazionatutako erregistroak bilatu]</span>, baina ez duzu orrialde hau sortzeko baimenik.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" lankidea ez dago erregistatuta. Mesedez, konprobatu orri hau editatu/sortu nahi duzun.",
        "preferences": "Hobespenak",
        "mypreferences": "Hobespenak",
        "prefs-edits": "Aldaketa kopurua:",
-       "prefsnologintext2": "Mesedez $1 zure hobespenak aldatzeko.",
+       "prefsnologintext2": "Mesedez saioa hasi zure hobespenak aldatzeko.",
        "prefs-skin": "Itxura",
        "skin-preview": "Aurrebista",
        "datedefault": "Hobespenik ez",
        "uploadstash": "Gordailu bat igo",
        "uploadstash-clear": "Kodetutako fitxategiak izkutatu",
        "uploadstash-nofiles": "Ez duzu kodetutako fitxategirik.",
-       "uploadstash-errclear": "Fitxategiak ezabatzeak ez du arrakastarik izan.",
+       "uploadstash-errclear": "Fitxategiak ezabatzeak akatsa eman du.",
        "uploadstash-refresh": "Fitxategien zerrenda eguneratu",
        "img-auth-accessdenied": "Sarbide ukatua",
        "img-auth-nopathinfo": "PATH_INFO falta da.\nZure zerbitzaria ez dago informazio hau pasatzeko konfiguratuta.\nCGI-oinarriduna izan daiteke, img_auth onartzen ez duena.\nIkusi https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "nolicense": "Hautatu gabe",
        "licenses-edit": "Aldatu lizentzien aukerak",
        "license-nopreview": "(Aurreikuspenik ez)",
-       "upload_source_url": " (baliozko URL publikoa)",
-       "upload_source_file": " (zure ordenagailuko fitxategi bat)",
+       "upload_source_url": "(zuk aukeratutako fitxategi baliozko URL publikorako)",
+       "upload_source_file": "(zure ordenagailuko fitxategi bat)",
        "listfiles-delete": "ezabatu",
        "listfiles-summary": "Orri berezi honek igotako fitxategi guztiak erakusten ditu.",
        "listfiles_search_for": "Irudiaren izenagatik bilatu:",
        "tooltip-feed-rss": "Orrialde honen RSS jarioa",
        "tooltip-feed-atom": "Orrialde honen atom jarioa",
        "tooltip-t-contributions": "{{GENDER:$1|Lankide honen}} ekarpen zerrenda ikusi",
-       "tooltip-t-emailuser": "Lankide honi e-posta mezua bidali",
+       "tooltip-t-emailuser": "{{GENDER:$1|Lankide honi}} e-posta mezua bidali",
        "tooltip-t-info": "Orrialde honi buruzko informazio gehiago",
        "tooltip-t-upload": "Irudiak edo media fitxategiak igo",
        "tooltip-t-specialpages": "Orri berezi guztien zerrenda",
index 47d668d..f587eac 100644 (file)
        "revertmove": "પૂર્વવત",
        "delete_and_move_text": "== પાનું દૂર કરવાની જરૂર છે  ==\nલક્ષ્ય પાનું  \"[[:$1]]\" પહેલેથી અસ્તિત્વમાં છે.\nશું તમે આને હટાવીને સ્થળાંતર કરવાનો માર્ગ મોકળો કરવા માંગો છો?",
        "delete_and_move_confirm": "હા, આ પાનું હટાવો",
-       "delete_and_move_reason": "હટાવવાનું કામ આગળ વધાવવા ભૂંસી દેવાયુ \"[[$1]]\"",
+       "delete_and_move_reason": "\"[[$1]]\"થી ખસેડીને માહિતી અહિં લાવવા માટે ભૂંસી દેવાયું.",
        "selfmove": "સ્રોત ને લક્ષ્ય શીર્ષકો સમાન છે;\nપાના ને તેવા જ નામ ધરાવતા પાના પર પુનઃ સ્થાપન નહીં કરી શકાય.",
        "immobile-source-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
        "immobile-target-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
index d781023..c4788aa 100644 (file)
@@ -12,7 +12,8 @@
                        "Wyvernoid",
                        "לערי ריינהארט",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Robin van der Vliet"
                ]
        },
        "tog-underline": "Sub-strekizez ligili:",
        "minoredit": "Ico esas mikra redaktajo",
        "watchthis": "Surveyar ica pagino",
        "savearticle": "Registragar pagino",
+       "publishpage": "Publikigar pagino",
+       "publishchanges": "Publikigar chanji",
        "preview": "Previdar",
        "showpreview": "Previdar",
        "showdiff": "Montrez chanji",
index 321c5d4..6b2599e 100644 (file)
        "log-action-filter-block-block": "ブロック",
        "log-action-filter-block-reblock": "ブロック変更",
        "log-action-filter-block-unblock": "ブロック解除",
+       "log-action-filter-contentmodel-change": "コンテンツモデルの変更",
        "log-action-filter-delete-delete": "ページの削除",
        "log-action-filter-delete-restore": "ページの復帰",
        "log-action-filter-delete-event": "記録の削除",
index 5c4bd0b..71603bd 100644 (file)
        "img-auth-accessdenied": "Aksès ditulak",
        "img-auth-nopathinfo": "Kélangan PATH_INFO.\nSasana Sampéyan durung disetèl kanggo ngliwati inpormasi iki.\nMungkin amarga abasis-CGI lan ora bisa nyengkuyung img_auth.\nDelok https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Alur sing dijaluk dudu dirèktori unggah kakonpigurasi.",
-       "img-auth-badtitle": "Ora bisa mbangun judhul sah saka \"$1\".",
+       "img-auth-badtitle": "Ora bisa ngyasa sesirah sing sah saka \"$1\".",
        "img-auth-nologinnWL": "Sampéyan durung mlebu log lan \"$1\" ora nèng daptar putih.",
        "img-auth-nofile": "Berkas \"$1\" ora ana.",
        "img-auth-isdir": "Sampéyan lagi njajal ngaksès dirèktori \"$1\".\nNamung aksès berkas sing dililakaké.",
        "unusedimages": "Berkas sing ora dienggo",
        "wantedcategories": "Kategori sing diperlokaké",
        "wantedpages": "Kaca sing dipèngini",
-       "wantedpages-badtitle": "Judhul ora valid ing sèt asil: $1",
+       "wantedpages-badtitle": "Sesirah ora sah ing omboyakan kasil: $1",
        "wantedfiles": "Berkas sing diperlokaké",
        "wantedfiletext-cat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>. Lan, kaca sing nyartakaké berkas sing ora ana bakal kadaptar nèng [[:$1]].",
        "wantedfiletext-nocat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>.",
        "protectedpages-expiry": "Kadaluwarsa",
        "protectedpages-reason": "Alesan",
        "protectedtitles": "Sesirah direksa",
-       "protectedtitlesempty": "Ora ana irah-irahan utawa judhul sing direksa karo paramèter-paramèter iki.",
+       "protectedtitlesempty": "Ora ana sesirah sing saiki kareksa mawa paramèter iki.",
        "listusers": "Daftar panganggo",
        "listusers-editsonly": "Tampilaké mung panganggo sing nduwèni kontribusi",
        "listusers-creationsort": "Urut miturut tanggal digawé",
        "delete_and_move_text": "Kaca jujugan \"[[:$1]]\" wis ana.\nApa sampéyan kersa mbusak iku supaya kacané bisa dilih?",
        "delete_and_move_confirm": "Ya, busak kaca iku.",
        "delete_and_move_reason": "Dibusak kanggo jaga-jaga ananing pamindhahan saka \"[[$1]]\"",
-       "selfmove": "Pangalihan kaca ora bisa dilakoni amerga irah-irahan utawa judhul sumber lan tujuané padha.",
+       "selfmove": "Sesirah sumber lan tujuan padha;\nora bisa ngalih nyang tujuan sing padha.",
        "immobile-source-namespace": "Ora bisa mindhahaké kaca jroning bilik jeneng \"$1\"",
        "immobile-target-namespace": "Ora bisa mindhahaké kaca menyang bilik jeneng \"$1\"",
        "immobile-target-namespace-iw": "Pranala interwiki dudu target sing sah kanggo pamindhahan kaca.",
index 8ec2e36..0037459 100644 (file)
        "noindex-category": "არ არსებობს ინდექსირებული გვერდები",
        "broken-file-category": "გვერდები ფაილების არასწორი ბმულებით",
        "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "შესახებ",
        "article": "სტატია",
        "newwindow": "(ახალ ფანჯარაში)",
        "tagline": "{{SITENAME}} გვერდიდან",
        "help": "დახმარება",
        "search": "ძიება",
+       "search-ignored-headings": "#<!-- დატოვეთ ეს ხაზი უცვლელად --> <pre>\n# სათაურები, რომლებთაც ძიება დააგნორებს.\n# ცვლილებები აისახება როგორც კი სათაურის მქონე გვერდს ინდექსირება გაუკეთდება.\n# შეგიძლიათ გააკეთოთ გვერდის ძალით ხელახალი ინდექსირება null edit-ის გაკეთებით.\n# სინტაქსი შემდეგია:\n#   * ყველაფერი \"#\" სიმბოლოდან ხაზის ბოლმდე კომენტარია.\n#   * ყველა არა-ცარიელი ხაზი is the exact title to ignore, case and everything.\nწყაროები\nრესურსები ინტერნეტში\nიხილეთ აგრეთვე\n #</pre> <!-- დატოვეთ ეს ხაზი უცვლელად -->",
        "searchbutton": "ძიება",
        "go": "სტატია",
        "searcharticle": "გვერდი",
        "passwordreset-emailelement": "მომხმარებლის სახელი: \n$1\n\nდროებითი პაროლი: \n$2",
        "passwordreset-emailsentemail": "თუ ეს მეილი თქვენს ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
        "passwordreset-emailsentusername": "თუ არსებობს მეილი, რომელიც ამ ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
-       "passwordreset-emailsent-capture": "ქვემოთ ნაჩვენები პაროლის თავიდან დასაყენებელი წერილი გაიგზავნა.",
-       "passwordreset-emailerror-capture": "ქვემოთ მოცემულია შექმნილი პაროლის დასაყენებელი წერილი, რომლის გაგზავნაც {{GENDER:$2|მომხმარებელთან}} ვერ მოხერხდა: $1 გამო",
        "passwordreset-emailsent-capture2": "პაროლის გაუქმების შესახებ {{PLURAL:$1|მეილი|მეილები}} გაიგზავნა. {{PLURAL:$1|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|მომხმარებელთან}} მეილის გაგზავნა ვერ მოხერხდა: $1 {{PLURAL:$3|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-nocaller": "გამომძახებელი უნდა იყოს მიწოდებული",
        "passwordreset-nodata": "არც მომხმარებლის სახელი და არც ელ-ფოსტის მისამართი არ იყო მოწოდებული",
        "changeemail": "ელ-ფოსტის მისამართის შეცვლა ან წაშლა",
        "changeemail-header": "შეავსეთ ეს ფორმა მეილის შესაცვლელად. თუ გსურთ თქვენი ანგარიში არ იყოს დაკავშირებული არცერთ მეილთან, ახალი მეილის მისამართის ველი დატოვეთ ცარიელი.",
-       "changeemail-passwordrequired": "ამ ცვლილების დასადასტურებლად დაგჭირდებათ პაროლის შეყვანა.",
        "changeemail-no-info": "თქვენ ავტირიზებული უნდა იყოთ ამ გვერდთან უშუალო წვდომისთვის.",
        "changeemail-oldemail": "ელ-ფოსტის ამჟამინდელი მისამართი:",
        "changeemail-newemail": "ახალი ელ-ფოსტის მისამართი:",
        "content-model-css": "CSS",
        "content-json-empty-object": "ცარიელი ობიექტი",
        "content-json-empty-array": "ცარიელი ტაბლო",
+       "deprecated-self-close-category": "გვერდები, რომლებიც იყენებენ არავალიდურ თვითდახურვად HTML ტეგებს",
        "duplicate-args-warning": "<strong>გაფრთხილება:</strong> [[:$1]] იძახებს [[:$2]]-ის \"$3\" პარამეტრის ერთზე მეტ მნიშვნელობას. აისახება მხოლოდ ბოლოს გამოყენებული მნიშვნელობა.",
        "duplicate-args-category": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას",
        "duplicate-args-category-desc": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას, როგორებიც არის <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ან <code><nowiki>{{foo|bar|1=bar}}</nowiki></code>.",
        "undo-nochange": "როგორც ჩანს, რედაქტირება უკვე გაუქმდა.",
        "undo-summary": "[[Special:Contributions/$2|$2-ის]]([[User talk:$2|განხილვა]]) ცვლილებების გაუქმება (№$1)",
        "undo-summary-username-hidden": "ცვლილების გაუქმება $1, მომხმარებლის მიერ, რომლის სახელი დამალულია",
-       "cantcreateaccounttitle": "ანგარიშის შექმნა ვერ ხერხდება",
        "cantcreateaccount-text": "ამ IP-მისამართიდან აიკრძალა (<b>$1</b>) მომხმარებელ [[User:$3|$3]]-ის მიერ.\n\n$3 -ემ ამგვარი ახსნა : ''$2''",
        "cantcreateaccount-range-text": "{{GENDER:$3|მომხმარებელმა}} [[User:$3|$3]] ანგარიშის ან IP-მისამართის $1 შექმნისთვის {{GENDER:$3|დაადო}} აკრძალვა <strong>$1</strong>, თქვენი IP-მისამართის ჩათვლით ($4).\n\nმითითებულია შემდეგი მიზეზი: $2.",
        "viewpagelogs": "ამ გვერდისთვის სარეგისტრაციო ჟურნალების ჩვენება",
        "grant-highvolume": "დიდი მოცულობით რედაქტირება",
        "grant-oversight": "მომხმარებლებისა და შესწორებების დამალვა",
        "grant-patrol": "გვერდების რედაქტირებების შემოწმება",
+       "grant-privateinfo": "პირად ინფორმაციაზე წვდომა",
        "grant-protect": "გვერდების და დაცვა და დაცვის მოხსნა",
        "grant-rollback": "გვერდების რედაქტირებების სწრაფი გაუქმება",
        "grant-sendemail": "გაგუგზავნე ელექტრონული ფოსტა სხვა მომხმარებლებს",
        "rightslogtext": "მომხმარებელთა უფლებების ცვლილებათა ჟურბალი",
        "action-read": "ამ გვერდის კითხვა",
        "action-edit": "ამ გვერდის რედაქტირება",
-       "action-createpage": "á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
-       "action-createtalk": "á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
+       "action-createpage": "á\83\90á\83\9b á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
+       "action-createtalk": "á\83\90á\83\9b á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
        "action-createaccount": "ამ ანგარიშის შექმნა",
        "action-autocreateaccount": "გარე მომხმარებლის ანგარიშის ავტომატურად შექმნა",
        "action-history": "ამ გვერდის ისტორიის ნახვა",
        "undeletedrevisions": "$1 ვერსია აღდგენილია",
        "undeletedrevisions-files": "$1 ვერსია და $2 ფაილი აღდგენილია",
        "undeletedfiles": "$1 ფაილი აღდგენილია",
-       "cannotundelete": "á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90\n$1",
+       "cannotundelete": "á\83\96á\83\9dá\83\92á\83\98á\83\94á\83 á\83\97á\83\98 á\83\90á\83\9c á\83§á\83\95á\83\94á\83\9aá\83\90 á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90:\n$1",
        "undeletedpage": "'''$1 აღდგენილია'''\n\nუკანასკნელი წაშლილთა და აღდგენის სია შეგიძლიათ ნახოთ [[Special:Log/delete|წაშლილთა სიაში]].",
        "undelete-header": "ბოლოს წაშლილი გვერდების სიის ნახვა შეიძლება [[Special:Log/delete|წაშლათა ჟურნალში]].",
        "undelete-search-title": "წაშლილი გვერდების ძიება",
        "undelete-error-long": "ფაილის აღდგენისას წარმოიშვა შეცდომები\n\n$1",
        "undelete-show-file-confirm": "დარწმუნებული ხართ, რომ გსურთ ფაილ <nowiki>$1</nowiki>-ის წაშლილი ვერსიის ხილვა $2 $3-დან?",
        "undelete-show-file-submit": "ჰო",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "სახელთა სივრცე:",
        "invert": "ყველა, მონიშნულის გარდა",
        "tooltip-invert": "მონიშნეთ ეს უჯრა, რათა დამალოთ გვერდების ცვლილებები არჩეული სახელთა სივრცის ფარგლებში (და მასთან დაკავშირებულ სახელთა სივრცეში, თუ მსგავსი რამ მითითებულია)",
        "sp-contributions-newbies-sub": "ახალბედებისთვის",
        "sp-contributions-newbies-title": "ბოლოს დარეგისტრირებულ მომხმარებელთა წვლილი",
        "sp-contributions-blocklog": "ბლოკირების ისტორია",
-       "sp-contributions-suppresslog": "მომხმარებლის წაშლილი წვლილი",
-       "sp-contributions-deleted": "მომხმარებლის წაშლილი შესწოებები",
+       "sp-contributions-suppresslog": "{{GENDER:$1|მომხმარებლის}} წაშლილი წვლილი",
+       "sp-contributions-deleted": "{{GENDER:$1|მომხმარებლის}} წაშლილი შესწოებები",
        "sp-contributions-uploads": "ატვირთვები",
        "sp-contributions-logs": "ჟურნალები",
        "sp-contributions-talk": "განხილვა",
        "sp-contributions-username": "IP მისამართი ან მომხმარებლის სახელი:",
        "sp-contributions-toponly": "აჩვენე მხოლოდ ბოლო ვერსიები",
        "sp-contributions-newonly": "აჩვენე მხოლოდ ცვლილებები, რომელიც წარმოადგენს გვერდის შექმნილს",
+       "sp-contributions-hideminor": "მცირე რედაქტირებების დამალვა",
        "sp-contributions-submit": "ძიება",
        "whatlinkshere": "ბმული გვერდზე",
        "whatlinkshere-title": "გვერდები, რომლებიც შეიცავენ „$1“-ის ბმულებს",
        "ipb-unblock": "მომხმარებლის სახელზე ან IP მისამართზე ბლოკის მოხსნა",
        "ipb-blocklist": "იხილე არსებული ბლოკირებები",
        "ipb-blocklist-contribs": "მომხმარებელ {{GENDER:$1|$1}} წვლილი",
+       "ipb-blocklist-duration-left": "დარჩა $1",
        "unblockip": "მომხმარებელზე ბლოკის მოხსნა",
        "unblockiptext": "გამოიყენეთ ქვემოთ მოცემული ფორმულარი, რათა  დაბლოკილი IP მისამართი ან მომხმარებლის სახელი აღადგინოთ.",
        "ipusubmit": "ამ ბლოკის მოხსნა",
        "lockdbsuccesstext": "პროექტის მონაცემთა ბაზა დაიბლოკა.<br />\nარ დაგავიწყდეთ [[Special:UnlockDB|ბლოკის მოხსნა]] მონაცემთა ბაზასთან სამუშაოების გატარების შემდეგ.",
        "unlockdbsuccesstext": "მომაცემთა ბაზაზე ბლოკი მოიხსნა.",
        "lockfilenotwritable": "არ გაქვთ უფლება მონაცემთა ბაზის დაცვის ფაილის შესწორების. დასაბლოკად ან ბლოკის მოსახსნელად საჭიროა ფაილი ღია იყოს.",
+       "databaselocked": "მონაცემთა ბაზა უკვე ჩაკეტილია.",
        "databasenotlocked": "მონაცემთა ბაზა არაა ჩაკეტილი.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "$1 — გადატანა",
        "tooltip-ca-nstab-category": "გვერდის კატეგორიის ჩვენება",
        "tooltip-minoredit": "მონიშნე როგორც მცირე რედაქტირება [alt-i]",
        "tooltip-save": "თქვენი ცვლილებების შენახვა",
+       "tooltip-publish": "თქვენი ცვლილებების გამოქვეყნება",
        "tooltip-preview": "წინასწარ გადახედე ცვლილებებს, გთხოვთ გამოიყენოთ ეს შენახვამდე! [alt-p]",
        "tooltip-diff": "ტექსტში შეტანილი ცვლილებების ჩვენება. [alt-v]",
        "tooltip-compareselectedversions": "იხილეთ ამ გვერდის  ორ შერჩეულ ვერსიას შორის განსხვავებები.",
        "confirmemail_body_set": "ვიღაცამ, შესაძლოა თქვენ, IP მისამართით $1,\nპროექტში {{SITENAME}} შეცვალა ელ.ფოსტის მისამართი ანგარიშისათვის \"$2\" ამ მისამართით.\n\nიმის დასადასტურებლად, რომ ეს ანგარიში ნამდვილად თქვენ გეკუთვნით\nდა ელ.ფოსტის შესაძლებლობების  გასააქტიურებლად საიტზე {{SITENAME}}, გახსენით ეს ბმული თქვენს ბრაუზერში:\n\n$3\n\nთუ ეს თქვენ *არ* იყავით, მაშინ ელ.ფოსტის მისამართის დასტურების გასაუქმებლად, გადადით ამ ბმულზე:\n\n$5\n\nწერილის ვადის გასვლის თარიღია $4.",
        "confirmemail_invalidated": "ელ-ფოსტის დადასტურება გაუქმდა",
        "invalidateemail": "ელ-ფოსტის დადასტურების გაუქმება",
+       "notificationemail_subject_changed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები შეიცვალა",
+       "notificationemail_subject_removed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები წაიშალა",
        "scarytranscludedisabled": "[«Interwiki transcluding» გათიშულია]",
        "scarytranscludefailed": "[$1-თან დაკავშირების შეცდომა]",
        "scarytranscludefailed-httpstatus": "[ვერ მოხერხდა თარგის ჩატვირთვა $1-თვის: HTTP $2]",
        "confirm-watch-top": "დავამატო ეს გვერდი თქვენი კონტროლის სიას?",
        "confirm-unwatch-button": "დიახ",
        "confirm-unwatch-top": "მოვხსნა ეს გვერდი თქვენი კონტროლის სიიდან?",
+       "confirm-rollback-button": "კარგი",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
        "logentry-protect-protect-cascade": "$1-მ {{GENDER:$2|დაიცვა}} $3 $4 [კასკადური]",
        "logentry-protect-modify": "$1-მ {{GENDER:$2|შეცვალა}} დაცვის დონე $3 $4-სთვის",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|შეცვალა}} დაცვის დონე $3 $4 [კასკადური]",
-       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83£á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98 $3-á\83¡ $4-დან $5-ზე",
+       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98á\83¡ á\83¬á\83\94á\83\95á\83 á\83\9dá\83\91á\83\90 á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡á\83\90á\83\97á\83\95á\83\98á\83¡ {{GENDER:$6|$3}} $4-დან $5-ზე",
        "logentry-rights-rights-legacy": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ჯგუფის წევრობა $3-თვის",
        "logentry-rights-autopromote": "მომხმარებელი $1 ავტომატურად იქნა {{GENDER:$2|გადაყვანილი}} $4–დან $5–ში",
        "logentry-upload-upload": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3",
        "sessionprovider-generic": "$1 სესიები",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-სთან დაკავშირებული სესიები",
        "sessionprovider-nocookies": "შესაძლოა ქუქები გათიშულია. გთხოვთ ჩართეთ და სცადეთ განმეორებით.",
-       "randomrootpage": "შემთხვევითი ძირეული გვერდი"
+       "randomrootpage": "შემთხვევითი ძირეული გვერდი",
+       "log-action-filter-block": "ბლოკირების ტიპი:",
+       "log-action-filter-delete": "წაშლის ტიპი:",
+       "log-action-filter-import": "იმპორტის ტიპი:",
+       "log-action-filter-managetags": "ტეგის ცვლილების ტიპი:",
+       "log-action-filter-move": "გადატანის ტიპი:",
+       "log-action-filter-newusers": "ანგარიშის შექმნის ტიპი:",
+       "log-action-filter-patrol": "შემოწმების ტიპი:",
+       "log-action-filter-protect": "დაცვის ტიპი:",
+       "log-action-filter-rights": "უფლების შეცვლის ტიპი:",
+       "log-action-filter-upload": "ატვირთვის ტიპი:",
+       "log-action-filter-all": "ყველა",
+       "log-action-filter-block-block": "დაბლოკვა",
+       "log-action-filter-block-reblock": "ბლოკირების შეცვლა",
+       "log-action-filter-block-unblock": "განბლოკვა",
+       "log-action-filter-newusers-create2": "დარეგისტრირებული მომხმარებლის შექმნა",
+       "log-action-filter-newusers-autocreate": "ავტომატური შექმნა",
+       "log-action-filter-newusers-byemail": "პაროლით შექმნა, რომელიც გამოიგზავნა იმეილით",
+       "log-action-filter-patrol-autopatrol": "ავტომატური შემოწმება",
+       "log-action-filter-protect-protect": "დაცვა",
+       "log-action-filter-protect-unprotect": "დაცვის მოხსნა",
+       "log-action-filter-protect-move_prot": "დაცვა გადატანისაგან",
+       "log-action-filter-rights-autopromote": "ავტომატური შეცვლა",
+       "log-action-filter-upload-upload": "ახალი ატვირთვა",
+       "log-action-filter-upload-overwrite": "ხელახლა ატვირთვა",
+       "authmanager-authplugin-setpass-failed-title": "პაროლის ცვლილება ვერ განხორციელდა",
+       "authmanager-email-label": "ელ. ფოსტა",
+       "authmanager-email-help": "ელ. ფოსტის მისამართი",
+       "authmanager-realname-label": "ნამდვილი სახელი",
+       "authmanager-realname-help": "მომხმარებლის ნამდვილი სახელი",
+       "authprovider-resetpass-skip-label": "გამოტოვება",
+       "authprovider-resetpass-skip-help": "გამოტოვეთ პაროლის შეცვლის პროცესი.",
+       "specialpage-securitylevel-not-allowed-title": "არ არის ნებადართული",
+       "credentialsform-account": "ანგარიშის სახელი:"
 }
index 219b526..b413646 100644 (file)
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "tooltip-minoredit": "Ney jê vurnaiso qıc isaret ke",
        "tooltip-save": "Vurnaisunê ho qeyd ke",
+       "tooltip-publish": "Pele xo bışevekne",
        "tooltip-preview": "Kerem ke, vurnaisunê ho qeyd-kerdene ra ravêr be verqayt bıasne!",
        "tooltip-diff": "Kamci vurnayışi ke to meqale de kerdê, ninan basne.",
        "tooltip-compareselectedversions": "Ferqunê wertê ni dı nımınunê weçinıtu bıvêne.",
index 89742eb..25e8040 100644 (file)
        "recreate": "Izveidot no jauna",
        "confirm_purge_button": "Labi",
        "confirm-purge-top": "Iztīrīt šīs lapas kešu (''cache'')?",
+       "confirm-purge-bottom": "Lapas atjaunināšana iztīra kešatmiņu un liek parādīt lapas jaunāko versiju.",
        "confirm-watch-button": "Labi",
        "confirm-watch-top": "Pievienot šo lapu uzraugāmo lapu sarakstam?",
        "confirm-unwatch-button": "Labi",
index cd91aec..9636b64 100644 (file)
        "note": "'''सूचना:'''",
        "previewnote": "'''याद राख्नुहोस् यो केवल पूर्वावलोकन मात्र हो; तपाईंका परिवर्तनहरू संग्रहित भएका छैनन्!'''",
        "continue-editing": "सम्पादन क्षेत्रमा जानुहोस",
-       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤\82पादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\87लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\87 सेभ गरेपछि देखापर्छ।",
+       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤®à¥\8dपादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\88 सेभ गरेपछि देखापर्छ।",
        "session_fail_preview": "'''माफ गर्नुहोस्! सत्र-आँकड़ा (session data) हराउनाले हामीले तपाईंको सम्पादन प्रक्रिया अघि बढाउन सकेनौं।.'''\nकृपया पुनः प्रयास गर्नुहोस्।\nयदि फेरि पनि काम भएन भनें, [[Special:UserLogout|बाहिर गई(लग आउट गरी)]]  फेरि प्रवेश गर्नुहोस्।",
-       "session_fail_preview_html": "'''माफ गर्नुहोला! सत्र को डेटा को नोकसान को कारण ले गर्दा तपाइको सम्पादन लाइ जारी राख्न सकिएन।'''\n\n''जावास्क्रिप्ट हमलाहरु रोक्नको लागि यो पूर्वावलोकन लाइ देखाइएको छैन किन कि {{SITENAME}} मा काँचो HTML को प्रयोग गर्न मिल्ने बनाइएको छ।''\n\n'''यदि यो एक वैध प्रयास हो भने, कृपया पुन: प्रयास गर्नुहोला.'''\nयदि अझै पनि काम गरेन भने [[Special:UserLogout|निर्गमन(logging out)]] र पुन:आगमन(login) गर्ने प्रयास गर्नुहोला।",
+       "session_fail_preview_html": "माफ गर्नुहोला ! सेशन डाटा नष्ट भएको कारण तपाईंको परिवर्तन शुरक्षित गर्न सकिएन ।\n\n<em>किनकी {{SITENAME}}मा raw HTML सक्षम छ, जावास्क्रिप्ट हमहरूबाट बचाउनको लागि झलक नहीं देखाइएको छैन ।</em>\n\n<strong>यदी यो तपाईंको वैध सम्पादन यत्न थियो भने कृपया पुनः प्रयास गर्नुहोस् ।</strong>\nयदी यस पनि यस्तै भयो भने कृपया [[Special:UserLogout|लग आउट]] गरेर पुनः लग इन गर्नुहोस् तथा तपाईंको ब्राउजरले यस साइटसँग कुकीजको अनुमति दिन्छ दिन्न जाँच गर्नुहोस् ।",
        "token_suffix_mismatch": "'''सम्पादन टोकनमा विराम चिह्न र वर्ण सम्बन्धित गड़बड़ीको कारण तपाईंको सम्पादन अस्वीकार गरिएको छ'''\nपृष्ठको पाठ बचाउन सम्पादन अस्वीकार गरिएको हो।\nयस्तो त्यसबेला हुन्छ जब तपाईंले बगी वेवमा आधारित अज्ञात प्रोक्सी सेवा प्रयोग गर्नुहुन्छ।",
        "edit_form_incomplete": "'''सम्पादनको केहि भाग सर्वरसम्म पुग्न सकेन, दुइपल्ट जाँच गर्नुहोस्, तपाईंको सम्पादन यथावत रहे पुनः प्रयास गर्नुहोस्'''",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 सम्पादन गरिदै",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझियो: $1",
        "explainconflict": "तपाईंले सम्पादन कार्य सुरु गरेपछि कसैले यस पृष्टलाई परिवर्तन गरेकोछ।\nमाथिल्लो पाठक्षेत्रमा पृष्ठको वर्तमान पाठ छ।\nतपाईंको परिवर्तन तल्लो भागमा दर्शाइएकोछ। \nतपाईंले गर्नुभएको परिवर्तनलाई वर्तमान पाठसित मिसाउनु पर्नेछ, यदि तपाईंले \"{{int:savearticle}}\" थिच्नु भयो भनें पाठको माथिल्लो भाग '''मात्र''' संग्रह गरिनेछ।",
        "double-redirect-fixed-maintenance": "[[$1]]बाट [[$2]]मा दोहोरो अनुप्रेषण स्वत तय गरिंदै।",
        "double-redirect-fixer": "अनुप्रेषण तय गर्ने",
        "brokenredirects": "टुटेका रिडाइरेक्टहरू",
-       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81 à¤²à¥\87 à¤¹à¥\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहररसँग जोडिन्छन्:",
+       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82लà¥\87 à¤¹à¥\81à¤\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहरà¥\82सँग जोडिन्छन्:",
        "brokenredirects-edit": "सम्पादन",
        "brokenredirects-delete": "मेट्ने",
        "withoutinterwiki": "भाषा नभएको पृष्ठहरू",
        "protectedpages-timestamp": "समय चिन्ह",
        "protectedpages-page": "पृष्ठ",
        "protectedpages-expiry": "सकिनेछ",
-       "protectedpages-performer": "पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¸à¥\81रà¤\95à¥\8dषित à¤\97रिà¤\81दà¥\88",
+       "protectedpages-performer": "प्रयोगकर्ता सुरक्षित गरिदै",
        "protectedpages-params": "सुरक्षा प्यारामेटर",
        "protectedpages-reason": "कारण",
        "protectedpages-submit": "पानाहरू देखाउनुहोस्",
        "watchlist-options": "निगरानि सूची विकल्प",
        "watching": "निगरानी गर्दै...",
        "unwatching": "निगरानीबाट हटाउँदै...",
-       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\87à¤\81à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤¯à¥\8cà¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b।",
+       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤\8fà¤\89à¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b ।",
        "enotif_reset": "सबै पृष्ठहरू भनी दाग दिने",
        "enotif_impersonal_salutation": "{{SITENAME}} प्रयोगकर्ता",
        "enotif_subject_deleted": "{{SITENAME}} पृष्ठ $1 $2 ले {{GENDER:$2|मेटाउनु}} भयो ।",
        "whatlinkshere-links": "← लिंकहरू",
        "whatlinkshere-hideredirs": "$1 अनुप्रेषित हुन्छ",
        "whatlinkshere-hidetrans": "$1 पारदर्शन",
-       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81",
+       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82",
        "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
        "whatlinkshere-filters": "फिल्टरहरू",
        "whatlinkshere-submit": "जानुहोस्",
        "import-upload-filename": "फाइल नाम:",
        "import-comment": "टिप्पणी :",
        "importtext": "कृपया स्रोत विकिबाट फाइल निर्यात गर्नका लागि [[Special:Export|निर्यात सुविधा]]को प्रयोग गर्नुहोस। यसलाई आफ्नो कम्प्युटरमा सङ्ग्रह गरे यहाँ अपलोड गर्नुहोस।",
-       "importstart": "पà¥\83षà¥\8dठ à¤\86यात à¤\97रिà¤\81दà¥\88...",
+       "importstart": "पृष्ठ आयात गरिदै...",
        "import-revision-count": "$1 {{PLURAL:$1|पुनरावलोकन|पुनरावलोकनहरु}}",
        "importnopages": "आयातगर्नको लागि कुनै पृष्ठ छैन।",
        "imported-log-entries": "आयातित $1 {{PLURAL:$1|लग प्रविष्टी|लग प्रविष्टीहरू}}",
index 5c69a6d..7d99e09 100644 (file)
        "history": "Geschiedenis",
        "history_short": "Geschiedenis",
        "updatedmarker": "bewerkt sinds mijn laatste bezoek",
-       "printableversion": "Printervriendelijke versie",
+       "printableversion": "Printvriendelijke versie",
        "permalink": "Permanente koppeling",
        "print": "Afdrukken",
        "view": "Lezen",
        "log-action-filter-newusers": "Type accountaanmaak:",
        "log-action-filter-patrol": "Soort markering:",
        "log-action-filter-protect": "Soort beveiliging:",
+       "log-action-filter-rights": "Soort verandering van rechten:",
+       "log-action-filter-upload": "Soort upload:",
        "log-action-filter-all": "Alles",
        "log-action-filter-block-block": "Blokkade",
        "log-action-filter-block-reblock": "Aanpassing van blokkade",
        "log-action-filter-block-unblock": "Opheffing van blokkade",
        "log-action-filter-delete-delete": "Verwijderen van pagina",
        "log-action-filter-delete-restore": "Terugplaatsen van pagina",
+       "log-action-filter-managetags-create": "Aanmaken van label",
+       "log-action-filter-managetags-delete": "Verwijderen van label",
+       "log-action-filter-managetags-activate": "Activeren van label",
+       "log-action-filter-managetags-deactivate": "Deactiveren van label",
+       "log-action-filter-move-move": "Verplaatsing zonder overschrijven van doorverwijzingen",
+       "log-action-filter-move-move_redir": "Verplaatsing met overschrijven van doorverwijzingen",
        "log-action-filter-newusers-create": "Aangemaakt door een anonieme gebruiker",
        "log-action-filter-newusers-create2": "Aangemaakt door een geregistreerde gebruiker",
        "log-action-filter-newusers-autocreate": "Automatische aanmaak",
        "log-action-filter-protect-move_prot": "Beveiliging verplaatst",
        "log-action-filter-rights-rights": "Handmatige aanpassing",
        "log-action-filter-rights-autopromote": "Automatische aanpassing",
+       "log-action-filter-suppress-event": "Verbergen van logboekregel",
+       "log-action-filter-suppress-revision": "Verbergen van versie",
+       "log-action-filter-suppress-delete": "Verbergen van pagina",
        "log-action-filter-upload-upload": "Nieuwe upload",
        "log-action-filter-upload-overwrite": "Herupload",
        "authmanager-authn-autocreate-failed": "Het automatisch aanmaken van een lokaal account is mislukt: $1",
index c61a5c6..2ccdece 100644 (file)
        "right-applychangetags": "Aplicar [[Special:Tags|las balisas]] amb sas pròprias modificacions",
        "grant-generic": "ensemble de dreits « $1 »",
        "grant-blockusers": "Blocar e desblocar d'utilizaires",
-       "grant-patrol": "Marcar de paginas coma patrolhadas",
+       "grant-patrol": "Verificar las modificacions de paginas",
        "newuserlogpage": "Istoric de las creacions de comptes",
        "newuserlogpagetext": "Jornal de las creacions de comptes d'utilizaires.",
        "rightslog": "Istoric de las modificacions d'estatut",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|utilizaire seguent|utilizaires seguents}}]",
-       "rc_categories": "Limit de las categorias (separacion amb « | »)",
+       "rc_categories": "Limitar a las categorias (separadas per « | ») :",
        "rc_categories_any": "Una de las seleccionadas",
        "rc-change-size-new": "$1 {{PLURAL:$1|octet|octets}} aprèp cambiament",
        "newsectionsummary": "/* $1 */ seccion novèla",
        "undeletedrevisions": "{{PLURAL:$1|1 revision restablida|$1 revisions restablidas}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revision|$1 revisions}} e {{PLURAL:$2|1 fichièr restablit|$2 fichièrs restablits}}",
        "undeletedfiles": "$1 {{PLURAL:$1|fichièr restablit|fichièrs restablits}}",
-       "cannotundelete": "Fracàs del restabliment :\n$1",
+       "cannotundelete": "Certanas o totas las restitucions an fracassat :\n$1",
        "undeletedpage": "<strong>La pagina $1 es estada restablida</strong>.\n\nConsultatz l’[[Special:Log/delete|istoric de las supressions]] per veire la lista de las supressions e dels restabliments recents.",
        "undelete-header": "Consultatz l’[[Special:Log/delete|istoric de las supressions]] per veire las paginas recentament suprimidas.",
        "undelete-search-title": "Recercar las paginas suprimidas",
        "tooltip-feed-rss": "Flux RSS per aquesta pagina",
        "tooltip-feed-atom": "Flux Atom per aquesta pagina",
        "tooltip-t-contributions": "Veire la lista de las contribucions d'{{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
-       "tooltip-t-emailuser": "Mandar un corrièr electronic a aqueste utilizaire",
+       "tooltip-t-emailuser": "Mandar un corrièr electronic a {{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
        "tooltip-t-info": "Mai d’informacion sus aquesta pagina",
        "tooltip-t-upload": "Mandar un imatge o fichièr mèdia sul servidor",
        "tooltip-t-specialpages": "Lista de totas las paginas especialas",
        "watchlistedit-raw-done": "Vòstra lista de seguiment es estada mesa a jorn.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Una pagina es estada aponduda|$1 paginas son estadas apondudas}} :",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Una pagina es estada levada|$1 paginas son estadas levadas}} :",
-       "watchlistedit-clear-title": "Lista de seguiment voidada",
+       "watchlistedit-clear-title": "Voidar la lista de seguiment",
        "watchlistedit-clear-legend": "Escafar la lista de seguiment",
        "watchlistedit-clear-explain": "Totes los títols seràn suprimits de vòstra lista de seguiment",
        "watchlistedit-clear-titles": "Títols :",
        "version-libraries-license": "Licéncia",
        "version-libraries-description": "Descripcion",
        "version-libraries-authors": "Autors",
-       "redirect": "Redirigit per fichièr, utilizaire, pagina o ID de revision.",
+       "redirect": "Redirigir per ID de fichièr, utilizaire, pagina, revision o jornal.",
        "redirect-submit": "Validar",
        "redirect-lookup": "Recèrca :",
        "redirect-value": "Valor :",
        "tags-edit-title": "Modificar las balisas",
        "tags-edit-manage-link": "Gerir las balisas",
        "tags-edit-existing-tags": "Balisas existentas :",
-       "tags-edit-existing-tags-none": "\"Pas cap\"",
+       "tags-edit-existing-tags-none": "<em>Pas cap</em>",
        "tags-edit-new-tags": "Balisas novèlas :",
        "tags-edit-add": "Apondre aquestas balisas :",
        "tags-edit-remove": "Suprimir aquestas balisas :",
index 810c6c5..cd4e304 100644 (file)
        "hide": "Скрыть",
        "show": "Показать",
        "minoreditletter": "м",
-       "newpageletter": "н",
+       "newpageletter": "Ð\9d",
        "boteditletter": "б",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдающий участник|наблюдающих участника|наблюдающих участников}}]",
index f4ff3ad..238e705 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|पुरस्तात् (previous) $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अग्रिमम् $1}}",
        "whatlinkshere-links": "← परिसन्धयः",
-       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि पृष्ठानि",
-       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\8dयलà¥\87à¤\96भाà¤\97ाः (transclusions)",
+       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि",
+       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\81वादाः (transclusions)",
        "whatlinkshere-hidelinks": "$1 परिसन्धयः",
        "whatlinkshere-hideimages": "$1 चित्रपरिसन्धिः",
        "whatlinkshere-filters": "शोधनी",
index 0513571..492912d 100644 (file)
        "revdelete-selected-file": "{{PLURAL:$1|Vald filversion|Valda filversioner}} av [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Vald loggåtgärd|Valda loggåtgärder}}:",
        "revdelete-text-text": "Raderade sidversioner kommer fortfarande synas i sidans historik, men delar av innehållet kommer inte att vara tillgängligt offentligt.",
-       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filens historik, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
-       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
+       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filhistoriken, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
+       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
        "revdelete-text-others": "Andra administratörer kommer fortfarande att kunna komma åt det dolda innehållet och återställa det igen om inte ytterligare begränsningar används.",
        "revdelete-confirm": "Var god bekräfta att du vill göra detta, och att du förstår konsekvenserna, och att du gör så i enlighet med [[{{MediaWiki:Policy-url}}|policyn]].",
        "revdelete-suppress-text": "Undanhållande ska '''bara''' användas i följande fall:\n* Eventuell förolämpande information\n* Opassande personlig information\n*: ''hemadresser och telefonnummer, personnummer, etc.''",
        "search-relatedarticle": "Relaterad",
        "searchrelated": "relaterad",
        "searchall": "alla",
-       "showingresults": "Nedan visas upp till {{PLURAL:$1|'''1''' post|'''$1''' poster}} från och med nummer '''$2'''.",
+       "showingresults": "Nedan visas upp till {{PLURAL:$1|<strong>1</strong> resultat|<strong>$1</strong> resultat}} från och med nummer <strong>$2</strong>.",
        "showingresultsinrange": "Nedan visas upp till {{PLURAL:$3|<strong>1</strong> resultat|<strong>$1</strong> resultat}} mellan nummer <strong>$2</strong> och nummer <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultat <strong>$1</strong> av <strong>$3</strong>|Resultat <strong>$1 – $2</strong> av <strong>$3</strong>}}",
        "search-nonefound": "Inga resultat matchade frågan.",
        "email": "E-post",
        "prefs-help-realname": "Riktigt namn behöver inte anges.\nOm angivet, kan det komma att användas för att tillskriva dig ditt arbete.",
        "prefs-help-email": "Att ange e-postadress är valfritt, men gör det möjligt att få ditt lösenord mejlat till dig om du glömmer det.",
-       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. Din e-postadress avslöjas inte när andra användare kontaktar dig.",
+       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. \nDin e-postadress avslöjas inte när andra användare kontaktar dig.",
        "prefs-help-email-required": "E-postadress måste anges.",
        "prefs-info": "Grundläggande information",
        "prefs-i18n": "Internationalisering",
        "filetype-mime-mismatch": "Filtillägget \".$1\" matchar inte med den identifierade MIME-typen för filen ($2).",
        "filetype-badmime": "Uppladdning av filer av MIME-typ \"$1\" är inte tillåtet.",
        "filetype-bad-ie-mime": "Kan inte ladda upp denna fil på grund av att Internet Explorer skulle upptäcka att den är \"$1\", vilket är en otillåten och möjligtvis farlig filtyp.",
-       "filetype-unwanted-type": "'''\".$1\"''' är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
-       "filetype-banned-type": "'''\".$1\"''' är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåtna filtyper|Tillåten filtyp}} är $2.",
+       "filetype-unwanted-type": "<strong>\".$1\"</strong> är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
+       "filetype-banned-type": "<strong>\".$1\"</strong> är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
        "filetype-missing": "Filnamnet saknar ändelse (t ex \".jpg\").",
        "empty-file": "Filen du skickade var tom.",
        "file-too-large": "Filen du skickade var för stor.",
        "fileexists": "Det finns redan en fil med detta namn. Titta på <strong>[[:$1]]</strong>, om {{GENDER:|du}} inte är säker på att {{GENDER:|du}} vill ändra den.\n[[$1|thumb]]",
        "filepageexists": "Beskrivningssidan för denna fil har redan skapats på <strong>[[:$1]]</strong>, men just nu finns ingen fil med detta namn.\nDen sammanfattning du skriver här kommer inte visas på beskrivningssidan.\nFör att din sammanfattning ska visas där, så måste du redigera beskrivningssidan manuellt.\n[[$1|thumb]]",
        "fileexists-extension": "En fil med ett liknande namn finns redan: [[$2|thumb]]\n* Namn på den fil du försöker ladda upp: <strong>[[:$1]]</strong>\n* Namn på filen som redan finns: <strong>[[:$2]]</strong>\nVill du möjligen välja ett mer distinkt namn?",
-       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek ''(miniatyrbild)''. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
+       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek <em>(miniatyrbild)</em>. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
        "file-thumbnail-no": "Filnamnet börjar med <strong>$1</strong>.\nDet verkar vara en bild med förminskad storlek ''(miniatyrbild)''.\nOm du har denna bild i full storlek, ladda då hellre upp den, annars var vänlig och ändra filens namn.",
        "fileexists-forbidden": "En fil med detta namn existerar redan, och kan inte skrivas över.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "En fil med detta namn finns redan bland de delade filerna.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
        "file-exists-duplicate": "Denna fil är en dubblett av följande {{PLURAL:$1|fil|filer}}:",
-       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. Du bör kontrollera den filens raderingshistorik innan du fortsätter att återuppladda den.",
-       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den.",
+       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. \nDu bör kontrollera den filens raderingshistorik innan du fortsätter att ladda upp den på nytt.",
+       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den på nytt.",
        "uploadwarning": "Uppladdningsvarning",
        "uploadwarning-text": "Var god och ändra filbeskrivningen nedanför och försök igen.",
        "savefile": "Spara fil",
        "trackingcategories-nodesc": "Ingen beskrivning tillgänglig.",
        "trackingcategories-disabled": "Kategorin är inaktiverad",
        "mailnologin": "Ingen adress att skicka till",
-       "mailnologintext": "För att kunna skicka e-post till andra användare, måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
+       "mailnologintext": "För att kunna skicka e-post till andra användare måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
        "emailuser": "Skicka e-post till den här användaren",
        "emailuser-title-target": "Skicka e-post till denna {{GENDER:$1|användare}}",
        "emailuser-title-notarget": "E-postanvändare",
        "watchlistanontext": "Du måste logga in för att se eller redigera din bevakningslista.",
        "watchnologin": "Inte inloggad",
        "addwatch": "Lägg till i bevakningslistan",
-       "addedwatchtext": "\"[[:$1]]\" har lagts till i din [[Special:Watchlist|bevakningslista]].",
+       "addedwatchtext": "\"[[:$1]]\" och dess diskussionssida har lagts till i din [[Special:Watchlist|bevakningslista]].",
        "addedwatchtext-talk": "\"[[:$1]]\" och dess associerade sida har lagts till i din [[Special:Watchlist|bevakningslista]].",
        "addedwatchtext-short": "Sidan \"$1\" har lagts till i din bevakningslista.",
        "removewatch": "Ta bort från bevakningslistan",
        "revertpage": "Återställde redigeringar av  [[Special:Contributions/$2|$2]] ([[User talk:$2|användardiskussion]]) till senaste versionen av [[User:$1|$1]]",
        "revertpage-nouser": "Återställde redigeringar av en dold användare till den senaste versionen av {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2.",
-       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2. [$3 Visa ändringar]",
+       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste sidversion av $2. [$3 Visa ändringar]",
        "sessionfailure-title": "Sessionsfel",
        "sessionfailure": "Något med din session som inloggad är på tok. Din begärda åtgärd har avbrutits, för att förhindra att någon kapar din session. Klicka på \"Tillbaka\" i din webbläsare och ladda om den sida du kom ifrån. Försök sedan igen.",
        "changecontentmodel": "Ändra innehållsmodell för en sida",
        "logentry-contentmodel-change-revertlink": "återställ",
        "logentry-contentmodel-change-revert": "återställ",
        "protectlogpage": "Skrivskyddslogg",
-       "protectlogtext": "Detta är en lista över applicerande och borttagande av skrivskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för listan över aktiva sidskydd.",
+       "protectlogtext": "Nedan är en lista över ändringar av sidskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för en förteckning över de sidskydd som för närvarande är aktiva.",
        "protectedarticle": "skrivskyddade \"[[$1]]\"",
        "modifiedarticleprotection": "ändrade skyddsnivån för \"[[$1]]\"",
        "unprotectedarticle": "tog bort skrivskydd från \"[[$1]]\"",
        "protect-default": "Tillåt alla användare",
        "protect-fallback": "Kräv \"$1\"-behörighet",
        "protect-level-autoconfirmed": "Blockera nya och oregistrerade användare",
-       "protect-level-sysop": "Enbart administratörer",
+       "protect-level-sysop": "Tillåt endast administratörer",
        "protect-summary-cascade": "kaskaderande",
        "protect-expiring": "upphör den $1 (UTC)",
        "protect-expiring-local": "upphör $1",
        "protect-expiry-indefinite": "på obestämd tid",
        "protect-cascade": "Skydda sidor som är inkluderade i den här sidan (kaskaderande skydd)",
        "protect-cantedit": "Du kan inte ändra skrivskyddsnivån för den här sidan, eftersom du inte har behörighet att redigera den.",
-       "protect-othertime": "Annan tidsperiod:",
-       "protect-othertime-op": "annan tidsperiod",
+       "protect-othertime": "Annan tid:",
+       "protect-othertime-op": "annan tid",
        "protect-existing-expiry": "Gällande varaktighet: $2, kl. $3",
        "protect-existing-expiry-infinity": "Gällande varaktighet: oändlig",
        "protect-otherreason": "Annan/ytterligare anledning:",
        "protect-dropdown": "*Vanliga anledningar för skrivskydd\n** Upprepad vandalisering\n** Upprepad spam\n** Redigeringskrig\n** Sida med många besökare",
        "protect-edit-reasonlist": "Redigera skrivskyddsanledningar",
        "protect-expiry-options": "1 timme:1 hour,1 dygn:1 day,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
-       "restriction-type": "Typ av skydd:",
+       "restriction-type": "Behörighet:",
        "restriction-level": "Skyddsnivå:",
        "minimum-size": "Minsta storlek",
        "maximum-size": "Största storlek:",
        "undeletepagetext": "Följande {{PLURAL:$1|sida har blivit raderad|$1 sidor har blivit raderade}} men finns fortfarande i arkivet och kan återställas.\nArkivet kan ibland rensas ut.",
        "undelete-fieldset-title": "Återställ sidversioner",
        "undeleteextrahelp": "För att återställa sidans hela historik, lämna alla rutor oifyllda och klicka på '''''{{int:undeletebtn}}'''''.\nFör att göra en selektiv återställning, kryssa i de rutor som hör till de versioner som ska återställas, och klicka på '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|version|versioner}} raderade",
-       "undeletehistory": "Om du återställer sidan kommer alla tidigare versioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
+       "undeleterevisions": "$1 {{PLURAL:$1|sidversion|sidversioner}} raderade",
+       "undeletehistory": "Om du återställer sidan kommer alla tidigare sidversioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
        "undeleterevdel": "Återställningen kan inte utföras om den resulterar i att den senaste versionen är delvis borttagen.\nI sådana fall måste du se till att den senaste raderade versionen inte är ikryssad, eller att den inte är dold.",
        "undeletehistorynoadmin": "Den här sidan har blivit raderad. Anledningen till detta anges i sammanfattningen nedan, tillsammans med uppgifter om de användare som redigerat sidan innan den raderades. Enbart administratörerna har tillgång till den raderade texten.",
        "undelete-revision": "Raderad version av $1 (från den $4 kl. $5) av $3.",
        "undelete-search-prefix": "Sidor som börjar med:",
        "undelete-search-submit": "Sök",
        "undelete-no-results": "Inga sidor med sådan titel hittades i arkivet över raderade sidor.",
-       "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: filnamnet stämmer inte.",
-       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: filen saknades före radering.",
+       "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: Filnamnet stämmer inte.",
+       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: Filen saknades före radering.",
        "undelete-cleanup-error": "Fel vid radering av den oanvända arkivfilen \"$1\".",
        "undelete-missing-filearchive": "Filen med arkiv-ID $1 kunde inte återställas eftersom den inte finns i databasen. Filen kanske redan har återställts.",
        "undelete-error": "Kunde inte återställa sidan",
        "undelete-error-short": "Fel vid filåterställning: $1",
-       "undelete-error-long": "Fel inträffade när vid återställning av filen:\n\n$1",
-       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl $3?",
+       "undelete-error-long": "Fel inträffade vid återställning av filen:\n\n$1",
+       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl. $3?",
        "undelete-show-file-submit": "Ja",
        "namespace": "Namnrymd:",
        "invert": "Invertera val",
        "tooltip-namespace_association": "Markera denna ruta för att även inkludera diskussions- eller ämnesnamnrymden som är associerad med den valda namnrymden",
        "blanknamespace": "(Huvudnamnrymden)",
        "contributions": "{{GENDER:$1|Användarbidrag}}",
-       "contributions-title": "Bidrag av $1",
+       "contributions-title": "Användarbidrag av $1",
        "mycontris": "Bidrag",
        "anoncontribs": "Bidrag",
        "contribsub2": "För {{GENDER:$3|$1}} ($2)",
        "sp-contributions-logs": "loggar",
        "sp-contributions-talk": "diskussion",
        "sp-contributions-userrights": "hantering av användarrättigheter",
-       "sp-contributions-blocked-notice": "Användaren är blockerad.\nOrsaken till senaste blockeringen kan ses nedan:",
+       "sp-contributions-blocked-notice": "Användaren är blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-blocked-notice-anon": "Denna IP-adress är för närvarande blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-search": "Sök efter användarbidrag",
        "sp-contributions-username": "IP-adress eller användarnamn:",
        "whatlinkshere-title": "Sidor som länkar till \"$1\"",
        "whatlinkshere-page": "Sida:",
        "linkshere": "Följande sidor länkar till <strong>[[:$1]]</strong>:",
-       "nolinkshere": "Inga sidor länkar till '''[[:$1]]'''.",
-       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till '''[[:$1]]'''.",
+       "nolinkshere": "Inga sidor länkar till <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till <strong>[[:$1]]</strong>.",
        "isredirect": "omdirigeringssida",
        "istemplate": "inkluderad som mall",
        "isimage": "fillänk",
        "ipbemailban": "Hindra användaren från att skicka e-post",
        "ipbenableautoblock": "Blockera automatiskt den IP-adress som användaren använde senast, samt alla adresser som användaren försöker redigera ifrån",
        "ipbsubmit": "Blockera användaren",
-       "ipbother": "Annan tidsperiod:",
+       "ipbother": "Annan tid:",
        "ipboptions": "2 timmar:2 hours,1 dygn:1 day,3 dygn:3 days,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
        "ipbhidename": "Dölj användarnamnet från redigeringar och listor",
        "ipbwatchuser": "Bevaka användarens användarsida och diskussionssida",
        "createaccountblock": "kontoregistrering blockerad",
        "emailblock": "e-post blockerad",
        "blocklist-nousertalk": "kan inte redigera sin egen diskussionssida",
-       "ipblocklist-empty": "Listan över blockerade IP-adresser är tom.",
+       "ipblocklist-empty": "Listan över blockeringar är tom.",
        "ipblocklist-no-results": "Den angivna IP-adressen eller användaren är inte blockerad.",
        "blocklink": "blockera",
        "unblocklink": "ta bort blockering",
        "blocklogpage": "Blockeringslogg",
        "blocklog-showlog": "Denna användare har blivit blockerad tidigare.\nBlockeringsloggen är tillgänglig nedan som referens:",
        "blocklog-showsuppresslog": "Denna användare har tidigare blivit blockerad och dold.\nUndanhållandeloggen visas nedan för referens:",
-       "blocklogentry": "blockerade [[$1]] med blockeringstid på $2 $3",
+       "blocklogentry": "blockerade [[$1]] med en varaktighet på $2 $3",
        "reblock-logentry": "ändrade blockeringsinställningar för [[$1]] med en varaktighet på $2 $3",
        "blocklogtext": "Detta är en logg över blockeringar och avblockeringar.\nAutomatiskt blockerade IP-adresser listas ej.\nSe [[Special:BlockList|blockeringslistan]] för en översikt av gällande blockeringar.",
-       "unblocklogentry": "tog bort blockering av \"$1\"",
+       "unblocklogentry": "tog bort blockering av $1",
        "block-log-flags-anononly": "bara oinloggade",
        "block-log-flags-nocreate": "hindrar kontoregistrering",
        "block-log-flags-noautoblock": "utan automatblockering",
        "block-log-flags-angry-autoblock": "utökad automatblockering aktiverad",
        "block-log-flags-hiddenname": "användarnamn dolt",
        "range_block_disabled": "Möjligheten för administratörer att blockera intervall av IP-adresser har stängts av.",
-       "ipb_expiry_invalid": "Ogiltig varaktighetstid.",
+       "ipb_expiry_invalid": "Ogiltig utgångstid.",
        "ipb_expiry_old": "Utgångstiden har redan passerat.",
        "ipb_expiry_temp": "För att dölja användarnamnet måste blockeringen vara permanent.",
        "ipb_hide_invalid": "Kan inte undanhålla detta konto; det har fler än {{PLURAL:$1|en redigering|$1 redigeringar}}.",
        "lockdbtext": "En låsning av databasen hindrar alla användare från att redigera sidor, ändra inställningar och andra saker som kräver ändringar i databasen.\nBekräfta att du verkligen vill göra detta, och att du kommer att låsa upp databasen när underhållet är utfört.",
        "unlockdbtext": "Om du låser upp databasen kommer alla användare att åter kunna redigera sidor, ändra sina inställningar och så vidare. Bekräfta att du vill göra detta.",
        "lockconfirm": "Ja, jag vill verkligen låsa databasen.",
-       "unlockconfirm": "Ja, jag vill låsa upp databasen.",
+       "unlockconfirm": "Ja, jag vill verkligen låsa upp databasen.",
        "lockbtn": "Lås databasen",
        "unlockbtn": "Lås upp databasen",
        "locknoconfirm": "Du har inte bekräftat låsningen.",
        "immobile-source-page": "Denna sida är inte flyttbar.",
        "immobile-target-page": "Kan inte flytta till det målnamnet.",
        "bad-target-model": "Den önskade destinationen använder en annan innehållsmodell. Kan inte konvertera från $1 till $2.",
-       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden",
-       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden",
-       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen",
-       "imageinvalidfilename": "Önskat filnamn är ogiltigt",
+       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden.",
+       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden.",
+       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen.",
+       "imageinvalidfilename": "Önskat filnamn är ogiltigt.",
        "fix-double-redirects": "Uppdatera omdirigeringar som leder till den gamla titeln",
        "move-leave-redirect": "Lämna kvar en omdirigering",
        "protectedpagemovewarning": "'''Varning:''' Den här sidan har låsts så att endast användare med administratörsrättigheter kan flytta den.\nDen senaste loggposten tillhandahålls nedan som referens:",
        "exporttext": "Du kan exportera text och versionshistorik för en eller flera sidor i XML-format.\nFilen kan sedan importeras till en annan MediaWiki-wiki med hjälp av sidan [[Special:Import|importera]].\n\nExportera sidor genom att skriva in sidtitlarna i rutan här nedan.\nSkriv en titel per rad och välj om du du vill exportera alla versioner av texten med sidhistorik, eller om du enbart vill exportera den nuvarande versionen med information om den senaste redigeringen.\n\nI det senare fallet kan du även använda en länk, exempel [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] för sidan \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Exportera alla sidor",
        "exportcuronly": "Inkludera endast den nuvarande versionen, inte hela historiken",
-       "exportnohistory": "----\n'''OBS:''' export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
+       "exportnohistory": "----\n<strong>OBS:</strong> Export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
        "exportlistauthors": "Inkludera en fullständig lista över bidragsgivare för varje sida",
        "export-submit": "Exportera",
        "export-addcattext": "Lägg till sidor från kategori:",
        "allmessagesname": "Namn",
        "allmessagesdefault": "Standardtext",
        "allmessagescurrent": "Nuvarande text",
-       "allmessagestext": "Detta är en lista över alla meddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
+       "allmessagestext": "Detta är en lista över alla systemmeddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
        "allmessagesnotsupportedDB": "Den här sidan kan inte användas eftersom '''$wgUseDatabaseMessages''' är avstängd.",
        "allmessages-filter-legend": "Filtrera",
        "allmessages-filter": "Filtrera efter anpassningsgrad:",
        "file-info-size": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4",
        "file-info-size-pages": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4, $5 {{PLURAL:$5|sida|sidor}}",
        "file-nohires": "Det finns ingen version med högre upplösning.",
-       "svg-long-desc": "SVG-fil, grundstorlek: $1 × $2 pixlar, filstorlek: $3",
+       "svg-long-desc": "SVG-fil, standardstorlek: $1 × $2 pixlar, filstorlek: $3",
        "svg-long-desc-animated": "Animerad SVG-fil, standardstorlek $1 × $2 pixlar, filstorlek: $3",
        "svg-long-error": "Felaktig SVG-fil: $1",
        "show-big-image": "Originalfil",
        "exif-gpsspeed-m": "Miles i timmen",
        "exif-gpsspeed-n": "Knop",
        "exif-gpsdestdistance-k": "Kilometer",
-       "exif-gpsdestdistance-m": "Mil",
+       "exif-gpsdestdistance-m": "Miles",
        "exif-gpsdestdistance-n": "Nautiska mil",
        "exif-gpsdop-excellent": "Utmärkt ($1)",
        "exif-gpsdop-good": "Bra ($1)",
        "confirmemail": "Bekräfta e-postadress",
        "confirmemail_noemail": "Du har inte angivit någon giltig e-postadress i dina [[Special:Preferences|inställningar]].",
        "confirmemail_text": "Innan du kan använda {{SITENAME}}s funktioner för e-post måste du bekräfta din e-postadress. Aktivera knappen nedan för att skicka en bekräftelsekod till din e-postadress. Mailet kommer att innehålla en länk, som innehåller en kod. Genom att klicka på den länken eller kopiera den till din webbläsares fönster för webbadresser, bekräftar du att din e-postadress fungerar.",
-       "confirmemail_pending": "En bekräftelsekod har redan skickats till din epostadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
+       "confirmemail_pending": "En bekräftelsekod har redan skickats till din e-postadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
        "confirmemail_send": "Skicka bekräftelsekod",
        "confirmemail_sent": "E-post med bekräftelse skickat.",
        "confirmemail_oncreate": "En bekräftelsekod skickades till din epostadress. Koden behövs inte för att logga in, men du behöver koden för att få tillgång till de epostbaserade funktionerna på wikin.",
        "confirmemail_body": "Någon, troligen du, har från IP-adressen $1 registrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera funktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_changed": "Någon, troligen du, har från IP-adressen $1\nregistrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera\nfunktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk\nför att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_set": "Någon, förmodligen du, från IP-adressen $1,\nhar angivit e-postadressen till kontot \"$2\" till den här adressen på {{SITENAME}}.\n\nFör att bekräfta att kontot verkligen tillhör dig, bör du aktivera e-postfunktionerna på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm kontot *inte* tillhör dig, följ den här länken för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer att sluta fungera efter $4.",
-       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har ogiltigförklarats",
+       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har avbrutits",
        "invalidateemail": "Avbryt bekräftelse av e-postadress",
        "notificationemail_subject_changed": "Registrerad e-postadress på {{SITENAME}} har ändrats",
        "notificationemail_subject_removed": "Registrerad e-postadress på {{SITENAME}} har tagits bort",
        "table_pager_prev": "Föregående sida",
        "table_pager_first": "Första sidan",
        "table_pager_last": "Sista sidan",
-       "table_pager_limit": "Visa $1 poster per sida",
+       "table_pager_limit": "Visa $1 objekt per sida",
        "table_pager_limit_label": "Objekt per sida:",
        "table_pager_limit_submit": "Utför",
        "table_pager_empty": "Inga resultat",
        "lag-warn-high": "På grund av omfattande fördröjning i databasen visas kanske inte ändringar nyare än $1 {{PLURAL:$1|sekund|sekunder}} i den här listan.",
        "watchlistedit-normal-title": "Redigera bevakningslista",
        "watchlistedit-normal-legend": "Ta bort sidor från bevakningslistan",
-       "watchlistedit-normal-explain": "Titlar på din bevakningslista visas nedan.\nFör att ta bort en titel, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
+       "watchlistedit-normal-explain": "Sidor på din bevakningslista visas nedan.\nFör att ta bort en sida, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
        "watchlistedit-normal-submit": "Ta bort sidor",
        "watchlistedit-normal-done": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort från din bevakningslista:",
        "watchlistedit-raw-title": "Redigera bevakningslistan i råformat",
        "watchlistedit-raw-legend": "Redigera bevakningslistan i råformat",
-       "watchlistedit-raw-explain": "Titlar på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen titel per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
+       "watchlistedit-raw-explain": "Sidor på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen sida per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
        "watchlistedit-raw-titles": "Sidor:",
        "watchlistedit-raw-submit": "Uppdatera bevakningslistan",
        "watchlistedit-raw-done": "Din bevakningslista har uppdaterats.",
        "watchlistedit-clear-title": "Rensa bevakningslistan",
        "watchlistedit-clear-legend": "Rensa bevakningslistan",
        "watchlistedit-clear-explain": "Alla titlar kommer att tas bort från din bevakningslista",
-       "watchlistedit-clear-titles": "Titlar:",
+       "watchlistedit-clear-titles": "Sidor:",
        "watchlistedit-clear-submit": "Rensa bevakningslistan (Detta är permanent!)",
        "watchlistedit-clear-done": "Din bevakningslista har rensats.",
-       "watchlistedit-clear-removed": "{{PLURAL:$1|1 titel|$1 titlar}} togs bort:",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort:",
        "watchlistedit-too-many": "Det finns för många sidor att visa här.",
        "watchlisttools-clear": "Rensa bevakningslistan",
        "watchlisttools-view": "Visa relevanta ändringar",
        "tags-activate": "aktivera",
        "tags-deactivate": "inaktivera",
        "tags-hitcount": "$1 {{PLURAL:$1|ändring|ändringar}}",
-       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringstaggar.",
-       "tags-manage-blocked": "Du kan inte hantera ändringsmärken när du är blockerad.",
+       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringsmärken.",
+       "tags-manage-blocked": "Du kan inte hantera förändringsmärken när du är blockerad.",
        "tags-create-heading": "Skapa ett nytt märke",
-       "tags-create-explanation": "Som standard, kommer nyskapade taggar att bli tillgängliga för användning av användare och botar.",
+       "tags-create-explanation": "Som standard, kommer nyskapade märken att bli tillgängliga för användning av användare och botar.",
        "tags-create-tag-name": "Märkesnamn:",
        "tags-create-reason": "Anledning:",
        "tags-create-submit": "Skapa",
        "tags-deactivate-reason": "Anledning:",
        "tags-deactivate-not-allowed": "Det är inte möjligt att inaktivera märket \"$1\".",
        "tags-deactivate-submit": "Inaktivera",
-       "tags-apply-no-permission": "Du har inte behörighet att tillämpa märken på dina ändringar",
+       "tags-apply-no-permission": "Du har inte behörighet att tillämpa ändringsmärken på dina ändringar.",
        "tags-apply-blocked": "Du kan inte ange ändringsmärken med dina ändringar medans du är blockerad.",
        "tags-apply-not-allowed-one": "Märket \"$1\" kan inte läggas till manuellt.",
        "tags-apply-not-allowed-multi": "Följande {{PLURAL:$2|märke|märken}} kan inte läggas till manuellt: $1",
        "log-name-managetags": "Märkeshanteringslogg",
        "log-description-managetags": "Denna sida innehåller administrativa [[Special:Tags|märke]]srelaterade uppgifter. Loggen innehåller bara åtgärder som utförts manuellt av en administratör; märken kan skapas eller raderas av wikins mjukvara utan att en post registreras i loggen.",
        "logentry-managetags-create": "$1 {{GENDER:$2|skapade}} märket \"$4\"",
-       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|sidversion eller loggpost|sidversioner och/eller loggposter}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} märket \"$4\" för användning av användare och botar.",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} märket \"$4\" för användning av användare och botar.",
        "log-name-tag": "Märkeslogg",
        "log-description-tag": "Denna sida visar när användare har lagt till eller tagit bort [[Special:Tags|märken]] från individuella sidversioner eller loggposter. Loggen registrerar inte handlingar där märken hanteras i redigeringar, raderingar eller liknande handlingar.",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 för sidversionen $4 av sidan $3",
-       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för siden $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för sidan $3",
        "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från sidversionen $4 av sidan $3",
        "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från loggposten $5 för sidan $3",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|uppdaterade}} märken på sidversionen $4 för sidan $3 ({{PLURAL:$7|lade till}} $6; {{PLURAL:$9|tog bort}} $8)",
        "api-error-badaccess-groups": "Du får inte ladda upp filer till denna wiki.",
        "api-error-badtoken": "Internt fel: felaktig nyckel.",
        "api-error-blocked": "Du har blockerats från att redigera.",
-       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverad på den här servern.",
+       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverat på den här servern.",
        "api-error-duplicate": "Det finns redan {{PLURAL:$1|en annan fil|andra filer}} på webbplatsen med samma innehåll.",
        "api-error-duplicate-archive": "Det fanns redan {{PLURAL:$1|en annan fil|några andra filer}} på webbplatsen med samma innehåll, men {{PLURAL:$1|den har|de har}} raderats.",
        "api-error-empty-file": "Filen du skickade var tom.",
        "api-error-filename-tooshort": "Filnamnet är för kort.",
        "api-error-filetype-banned": "Denna typ av fil är förbjuden.",
        "api-error-filetype-banned-type": "$1 är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}. {{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
-       "api-error-filetype-missing": "Filen saknar en filändelse.",
+       "api-error-filetype-missing": "Filnamnet saknar en filändelse.",
        "api-error-hookaborted": "Ändringen du försökte göra avbröts av en extension hook.",
        "api-error-http": "Internt fel: Det gick inte att ansluta till servern.",
        "api-error-illegal-filename": "Filnamnet är inte tillåtet.",
-       "api-error-internal-error": "Internt fel: något gick fel med bearbetningen av din uppladdning på wikin.",
+       "api-error-internal-error": "Internt fel: Något gick fel med bearbetningen av din uppladdning på wikin.",
        "api-error-invalid-file-key": "Internt fel: filen hittades inte i tillfällig lagring.",
-       "api-error-missingparam": "Internt fel: det saknas parametrar i begäran.",
-       "api-error-missingresult": "Internt fel: kunde inte avgöra om kopieringen lyckades.",
+       "api-error-missingparam": "Internt fel: Det saknas parametrar i begäran.",
+       "api-error-missingresult": "Internt fel: Kunde inte avgöra om kopieringen lyckades.",
        "api-error-mustbeloggedin": "Du måste vara inloggad för att kunna ladda upp filer.",
        "api-error-mustbeposted": "Det finns en bugg i detta program, det använder inte rätt HTTP-metod.",
        "api-error-noimageinfo": "Uppladdningen lyckades, men servern gav oss inte någon information om filen.",
-       "api-error-nomodule": "Internt fel: ingen uppladdningsmodul uppsatt.",
+       "api-error-nomodule": "Internt fel: Ingen uppladdningsmodul uppsatt.",
        "api-error-ok-but-empty": "Internt fel: Inget svar från servern.",
        "api-error-overwrite": "Det är inte tillåtet att skriva över en befintlig fil.",
-       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kort tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
+       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kortare tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
        "api-error-stashfailed": "Internt fel: servern kunde inte lagra temporär fil.",
        "api-error-publishfailed": "Internt fel: Servern kunde inte publicera temporär fil.",
        "api-error-stasherror": "Ett fel uppstod under uppladdningen av filen till mellanlagringsfilen.",
        "api-error-stashwrongowner": "Filen du försöker komma åt i det temporära lagringsutrymmet tillhör inte dig.",
        "api-error-stashnosuchfilekey": "Filnyckeln som du försökte komma åt i den temporära lagringsytan existerar inte.",
        "api-error-timeout": "Servern svarade inte inom förväntad tid.",
-       "api-error-unclassified": "Ett okänt fel uppstod",
-       "api-error-unknown-code": "Okänt fel: \"$1\"",
+       "api-error-unclassified": "Ett okänt fel uppstod.",
+       "api-error-unknown-code": "Okänt fel: \"$1\".",
        "api-error-unknown-error": "Internt fel: något gick fel när vi försökte ladda upp din fil.",
-       "api-error-unknown-warning": "Okänd varning: $1",
+       "api-error-unknown-warning": "Okänd varning: \"$1\".",
        "api-error-unknownerror": "Okänt fel: \"$1\".",
        "api-error-uploaddisabled": "Uppladdning är inaktiverad på denna wiki.",
        "api-error-verification-error": "Denna fil kan vara skadad eller har fel filändelse.",
        "expand_templates_output": "Expanderad kod",
        "expand_templates_xml_output": "XML-kod",
        "expand_templates_html_output": "Rå HTML-utdata",
-       "expand_templates_ok": "Expandera",
+       "expand_templates_ok": "OK",
        "expand_templates_remove_comments": "Ta bort kommentarer",
        "expand_templates_remove_nowiki": "Undertryck <nowiki> taggar i resultatet",
        "expand_templates_generate_xml": "Visa parseträd som XML",
        "pagelang-use-default": "Använd standardspråk",
        "pagelang-select-lang": "Välj språk",
        "pagelang-submit": "Skicka",
-       "right-pagelang": "Ändra sidans språk",
+       "right-pagelang": "Ändra sidspråk",
        "action-pagelang": "ändra sidspråket",
        "log-name-pagelang": "Språkändringslogg",
        "log-description-pagelang": "Detta är en logg över ändringar i sidspråken.",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Symboler",
        "special-characters-group-greek": "Grekiska",
-       "special-characters-group-greekextended": "Grekiska utvidgad",
-       "special-characters-group-cyrillic": "Kyrilliskt",
+       "special-characters-group-greekextended": "Utökad grekiska",
+       "special-characters-group-cyrillic": "Kyrilliska",
        "special-characters-group-arabic": "Arabiska",
-       "special-characters-group-arabicextended": "Arabiska utökade",
+       "special-characters-group-arabicextended": "Utökad arabiska",
        "special-characters-group-persian": "Persiska",
        "special-characters-group-hebrew": "Hebreiska",
        "special-characters-group-bangla": "Bengali",
        "special-characters-group-gujarati": "Gujarati",
        "special-characters-group-devanagari": "Devenagari",
        "special-characters-group-thai": "Thai",
-       "special-characters-group-lao": "Laotisk",
+       "special-characters-group-lao": "Laotiska",
        "special-characters-group-khmer": "Khmer",
        "special-characters-title-endash": "tankstreck",
        "special-characters-title-emdash": "långt tankstreck",
        "log-action-filter-block": "Typ av blockering:",
        "log-action-filter-contentmodel": "Typ av innehållsmodellsändring:",
        "log-action-filter-delete": "Typ av radering:",
-       "log-action-filter-import": "Importeringstyp:",
+       "log-action-filter-import": "Typ av importering:",
        "log-action-filter-managetags": "Typ av märkeshanteringsåtgärd:",
-       "log-action-filter-move": "Flyttningstyp:",
+       "log-action-filter-move": "Typ av flyttning:",
        "log-action-filter-newusers": "Typ av kontoskapande:",
        "log-action-filter-patrol": "Typ av patrullering:",
        "log-action-filter-protect": "Typ av skydd:",
        "log-action-filter-rights": "Typ av rättighetsändring:",
-       "log-action-filter-suppress": "Censurtyp:",
+       "log-action-filter-suppress": "Typ av censur:",
        "log-action-filter-upload": "Typ av uppladdning:",
        "log-action-filter-all": "Alla",
        "log-action-filter-block-block": "Blockering",
        "log-action-filter-contentmodel-change": "Ändring av innehållsmodell",
        "log-action-filter-contentmodel-new": "Skapande av sida med icke-standardiserad innehållsmodell",
        "log-action-filter-delete-delete": "Radering av sida",
-       "log-action-filter-delete-restore": "Återställde sida",
+       "log-action-filter-delete-restore": "Återställning av sida",
        "log-action-filter-delete-event": "Radering av logg",
        "log-action-filter-delete-revision": "Radering av sidversion",
        "log-action-filter-import-interwiki": "Interwikiimport",
        "authmanager-email-label": "E-post",
        "authmanager-email-help": "E-postadress",
        "authmanager-realname-label": "Riktigt namn",
-       "authmanager-realname-help": "Användarens riktiga namnet",
+       "authmanager-realname-help": "Användarens riktiga namn",
        "authmanager-provider-password": "Lösenordsbaserad autentisering",
        "authmanager-provider-password-domain": "Lösenord- och domänbaserad autentisering",
        "authmanager-provider-temporarypassword": "Tillfälligt lösenord",
index 7f2f2ea..4dad914 100644 (file)
        "statistics-pages": "పేజీలు",
        "statistics-pages-desc": "ఈ వికీలోని అన్ని పేజీలు (చర్చా పేజీలు, దారిమార్పులు, మొదలైనవన్నీ కలుపుకొని).",
        "statistics-files": "ఎక్కించిన దస్త్రాలు",
-       "statistics-edits": "{{SITENAME}}ని మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
+       "statistics-edits": "{{SITENAME}} మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
        "statistics-edits-average": "పేజీకి సగటు మార్పులు",
        "statistics-users": "నమోదైన [[Special:ListUsers|వాడుకరులు]]",
        "statistics-users-active": "క్రియాశీల వాడుకరులు",
        "watchlist-submit": "చూపించు",
        "wlshowtime": "చూపించాల్సిన కాలం:",
        "wlshowhideminor": "చిన్న మార్పులు",
-       "wlshowhidebots": "బాట్లు",
+       "wlshowhidebots": "బాట్లు",
        "wlshowhideliu": "నమోదైన వాడుకరులు",
        "wlshowhideanons": "అజ్ఞాత వాడుకరులు",
        "wlshowhidemine": "నా మార్పులు",
index 2eaa98c..f022040 100644 (file)
        "rollbacklinkcount-morethan": "відкинути понад $1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "rollbackfailed": "Відкинути зміни не вдалося",
        "rollback-missingparam": "Відсутні обов'язкові параметри за запитом.",
+       "rollback-missingrevision": "Не вдалося завантажити дані версії.",
        "cantrollback": "Неможливо відкинути редагування, оскільки останній дописувач сторінки є її автором.",
        "alreadyrolled": "Неможливо відкинути останні редагування [[:$1]], зроблені [[User:$2|$2]] ([[User talk:$2|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), оскільки хтось інший уже змінив чи відкинув редагування цієї статті.\n\nОстанні редагування зроблено [[User:$3|$3]] ([[User talk:$3|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Пояснення редагування було: «<em>$1</em>.».",
index 6118bb0..8d4d11f 100644 (file)
        "minoredit": "معمولی ترمیم",
        "watchthis": "یہ صفحہ زیر نظر کیجیۓ",
        "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کریں",
        "publishpage": "شائع کریں",
        "publishchanges": "تبدیلیاں شائع کریں",
        "preview": "نمائش",
        "showpreview": "نمائش",
-       "showdiff": "تبدÛ\8cÙ\84Û\8cاں Ø¯Ú©Ú¾Ø§Ø¤",
+       "showdiff": "تبدÛ\8cÙ\84Û\8cاں Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
        "anoneditwarning": "<strong>انتباہ:</strong> آپ ویکیپیڈیا میں داخل نہیں ہوئے ہیں۔ لہذا اگر آپ اس صفحہ میں کوئی ترمیم کرتے ہیں تو آپکا آئی پی ایڈریس (IP) اس صفحہ کے تاریخچہ ترمیم میں محفوظ ہوجائے گا۔ اگر آپ  <strong>[$1 لاگ ان]</strong> ہوتے ہیں یا کھاتہ نہ ہونے کی صورت میں <strong>[$2 کھاتہ بنا لیتے ہیں]</strong> تو تو آپ کی ترامیم آپ کے صارف نام سے محفوظ ہوگی، جنھیں آپ کسی بھی وقت ملاحظہ کر سکتے ہیں۔",
        "missingsummary": "'''انتباہ:''' آپ نے ترمیمی خلاصہ مہیّا نہیں کیا.\nاگر آپ نے محفوظ کا بٹن دوبارہ دبایا تو آپ کی ترمیم بغیر کسی خلاصہ کے محفوظ ہوجائے گی.",
        "missingcommenttext": "براہِ کرم! تبصرہ نیچے درج کیجئے.",
index fa7133a..c194377 100644 (file)
        "minoredit": "Votükam pülik",
        "watchthis": "Galädolöd padi at",
        "savearticle": "Dakipolöd padi",
+       "publishpage": "Dabükön padi",
+       "publishchanges": "Dabükön votükamis",
        "preview": "Büologed",
        "showpreview": "Jonolöd padalogoti",
        "showdiff": "Jonolöd votükamis",
        "undo-norev": "No eplöpos ad sädunön redakami at, bi no dabinon u pämoükon.",
        "undo-summary": "Äsädunon votükami $1 fa [[Special:Contributions/$2|$2]] ([[User talk:$2|Bespikapad]])",
        "undo-summary-username-hidden": "Sädunön revidi: $1 fa geban peklenädöl",
-       "cantcreateaccounttitle": "Kal no kanon pajafön",
        "cantcreateaccount-text": "Kalijaf se ladet-IP at ('''$1''') peblokon fa geban: [[User:$3|$3]].\n\nKod blokama fa el $3 pegivöl binon ''$2''",
        "viewpagelogs": "Jonön jenotalisedis pada at",
        "nohistory": "Pad at no labon redakamajenotemi.",
index a977529..8c65987 100644 (file)
        "minoredit": "Ci n' est k' ene tchitcheye",
        "watchthis": "Shuve cist årtike",
        "savearticle": "Schaper l' pådje",
+       "savechanges": "Schaper l' pådje",
        "preview": "Vey divant",
        "showpreview": "Vey divant",
        "showdiff": "Vey les candjmints",
        "editwarning-warning": "Cwiter cisse pådje ci vos frè piede tos les candjmints ki vos avoz fwait.\nSi vos estoz elodjî, vos ploz dismete cist adviertixhmint ci dins l' linwete «Boesse di tecse» di vos preferinces.",
        "post-expand-template-inclusion-warning": "'''Asteme:''' I gn a trop di modeles dins cisse pådje ci.\nSacwants di zels ni seront nén eployîs.",
        "post-expand-template-inclusion-category": "Pådjes ki l' inclusion d' modeles est foû limite",
-       "cantcreateaccounttitle": "Vos n' ploz nén ahiver-st on conte.",
        "viewpagelogs": "Vey les djournås po cisse pådje ci",
        "nohistory": "I n' a pont d' istwere des modêyes po cisse pådje chal.",
        "currentrev": "Modêye d' asteure",
        "duration-years": "$1 anêye{{PLURAL:$1||s}}",
        "duration-decades": "$1 dijhinne{{PLURAL:$1||s}} d' anêyes",
        "duration-centuries": "$1 sieke{{PLURAL:$1||s}}",
-       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}",
-       "api-error-blacklisted": "S' i vs plait, tchoezixhoz èn ôte tite, pus esplicant."
+       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}"
 }
index eb913d7..51b0dd4 100644 (file)
        "userlogin-resetpassword-link": "פֿאַרגעסן אײַער פאַסווארט?",
        "userlogin-helplink2": "הילף מיט ארײַנלאגירן",
        "userlogin-loggedin": "איר זענט שוין אריינלאגירט ווי {{GENDER:$1|$1}}.\nניצט די פארעם אונטן כדי אריינלאגירן ווי אן אנדער באניצער.",
+       "userlogin-reauth": "איר דארפט נאכאמאל אריינלאגירן צו באשטעטיקן אז איר זענט {{GENDER:$1|$1}}.",
        "userlogin-createanother": "שאפֿן נאך א קאנטע",
        "createacct-emailrequired": "בליצפּאָסט אַדרעס",
        "createacct-emailoptional": "בליצפאסט אדרעס (אפציאנאל)",
        "createaccountreason": "אורזאַך:",
        "createacct-reason": "אורזאך",
        "createacct-reason-ph": "פֿארוואס שאפֿט איר נאך א קאנטע",
+       "createacct-reason-help": "מעלדונג געוויזן אין קאנטע־שאפֿונג לאגבוך",
        "createacct-submit": "שאפֿט אײַער קאנטע",
        "createacct-another-submit": "שאַפֿן קאנטע",
        "createacct-continue-submit": "פֿארטזעצן שאפֿן קאנטע",
        "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן!",
        "changepassword-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
        "botpasswords": "באט פאסווערטער",
+       "botpasswords-disabled": "באט פאסווערטער זענען אומאקטיווירט.",
+       "botpasswords-existing": "עקזיסטירנדע באט פאסווערטער",
+       "botpasswords-createnew": "שאפֿן א ניי באט פאסווארט",
        "botpasswords-label-appid": "באט נאמען:",
        "botpasswords-label-create": "שאַפֿן",
        "botpasswords-label-update": "דערהײַנטיקן",
        "botpasswords-label-cancel": "אַנולירן",
        "botpasswords-label-delete": "אויסמעקן",
        "botpasswords-label-resetpassword": "ווידערשטעלן פאַסווארט",
+       "botpasswords-label-restrictions": "באניץ באגרענעצונגען:",
        "botpasswords-label-grants-column": "נאכגעגעבן",
+       "botpasswords-bad-appid": "דער באט נאמען \"$1\" איז אומגילטיק.",
        "botpasswords-created-title": "באט פאסווארט געשאפן",
        "botpasswords-created-body": "דאס באט פאסווארט פאר באט־נאמען \"$1\" פון באניצער \"$2\" איז געשאפן געווארן.",
        "botpasswords-updated-title": "באט פאסווארט דערהיינטיקט",
        "passwordreset-emailsentemail": "טאמער איז דער ע־פאסט אדרעס פארקניפט מיט אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-emailsentusername": "טאמער איז פאראן אן ע־פאסט אדרעס פארקניפט מיט דעם באניצער־נאמען, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-nocaller": "מען דארף פֿארזארגן א רופֿער",
+       "passwordreset-nosuchcaller": "רופֿער איז נישט פֿאראן: $1",
        "passwordreset-invalideamil": "אומגילטיקער ע־פאסט אדרעס",
        "changeemail": "ענדערן אדער אראפנעמען ע-פּאָסט אַדרעס",
        "changeemail-header": "דערגאַנצט די פֿאָרעם צו ענדערן אייער ע-פּאָסט אַדרעס .\nטאמער ווילט איר אראפנעמען די צוארדנונג פון איינעם פון אייערע ע־פאסט אדרעסן פו אייער קאנטע, לאזט ליידיג דעם נייעם ע־פאסט אדרעס ווען איר גיט איין די פֿארעם.",
        "minoredit": "דאס איז א מינערדיגע ענדערונג",
        "watchthis": "טוט אױפֿפּאַסן דעם בלאט",
        "savearticle": "אויפהיטן בלאַט",
+       "savechanges": "אויפֿהיטן ענדערונגען",
        "publishpage": "פובליקירן בלאַט",
        "publishchanges": "פובליקירן ענדערונגען",
        "preview": "פֿאראויסקוק",
        "right-passwordreset": "באַקוקן פאַסווארט צוריקשטעלן ע־בריוו",
        "right-managechangetags": "שאפן און (אומ)אקטיווירן [[Special:Tags|טאגן]]",
        "right-applychangetags": "אנווענדן [[Special:Tags|טאגן]] צוזאמען מיט ענדערונגען",
+       "grant-generic": "\"$1\" רעכטן־בינטל",
        "grant-group-page-interaction": "אינטעראגירן מיט בלעטער",
        "grant-group-file-interaction": "אינטעראגירן מיט מעדיע",
+       "grant-group-watchlist-interaction": "אינטעראגירן מיט אייער אויפֿפאסונג־ליסטע",
        "grant-group-email": "שיקן ע־פאסט",
        "grant-createaccount": "שאַפֿן קאנטעס",
        "grant-editmywatchlist": "רעדאקטירן אײַער אויפֿפאסונג ליסטע",
        "undeletedrevisions": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} צוריקגעשטעלט",
        "undeletedrevisions-files": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} און  {{PLURAL:$2|1 טעקע|$2 טעקעס}} צוריקגעשטעלט",
        "undeletedfiles": "{{PLURAL:$1|1 טעקע|$1 טעקעס}} צוריקגעשטעלט",
-       "cannotundelete": "צוריקשטעלונג איז דורכגעפאלן: $1",
+       "cannotundelete": "×\98×\99×\99×\9c ×\90×\93ער ×\92×\90רע ×¦×\95ר×\99קש×\98×¢×\9c×\95× ×\92 ×\90×\99×\96 ×\93×\95ר×\9b×\92עפ×\90×\9c×\9f: $1",
        "undeletedpage": "'''דער בלאט $1 איז געווארן צוריקגעשטעלט.'''\n\nזעט דעם [[Special:Log/delete| אויסמעקן לאג]] פֿאר א ליסטע פון די לעצטע אויסגעמעקטע און צוריקגעשטעלטע בלעטער.",
        "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט recently deleted pages.",
        "undelete-search-title": "זוכן אויסגעמעקטע בלעטער",
        "sp-contributions-newbies-sub": "פאר נייע קאנטעס",
        "sp-contributions-newbies-title": "ביישטייערונגען פון נייע באַניצער",
        "sp-contributions-blocklog": "בלאקירן לאג",
-       "sp-contributions-suppresslog": "אונטערדריקטע באַניצער בײַשטײַערונגען",
+       "sp-contributions-suppresslog": "אונטערדריקטע {{GENDER:$1|באַניצער}} בײַשטײַערונגען",
        "sp-contributions-deleted": "אויסגעמעקטע באַניצער בײַשטײַערונגען",
        "sp-contributions-uploads": "אַרויפֿלאָדונגען",
        "sp-contributions-logs": "לאגביכער",
index ab316c0..343687e 100644 (file)
@@ -37,6 +37,7 @@ define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
 $maintClass = false;
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Abstract maintenance class for quickly writing and churning out
@@ -548,6 +549,19 @@ abstract class Maintenance {
 
        }
 
+       /**
+        * Set triggers like when to try to run deferred updates
+        * @since 1.28
+        */
+       public function setTriggers() {
+               // Hook into period lag checks which often happen in long-running scripts
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->setWaitForReplicationListener(
+                       __METHOD__,
+                       [ 'DeferredUpdates', 'tryOpportunisticExecute' ]
+               );
+       }
+
        /**
         * Run a child maintenance script. Pass all of the current arguments
         * to it.
index 1272ca2..95bd089 100644 (file)
@@ -102,6 +102,10 @@ $maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main'
 // Sanity-check required extensions are installed
 $maintenance->checkRequiredExtensions();
 
+// A good time when no DBs have writes pending is around lag checks.
+// This avoids having long running scripts just OOM and lose all the updates.
+$maintenance->setTriggers();
+
 // Do the work
 $maintenance->execute();
 
index ef56cd3..5f45176 100644 (file)
@@ -571,6 +571,7 @@ return [
        ],
        'jquery.ui.position' => [
                'deprecated' => true,
+               'targets' => [ 'mobile', 'desktop' ],
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.position.js',
                'group' => 'jquery.ui',
        ],
index 2ce54e4..294b5de 100644 (file)
         */
        mw.log.deprecate( util, 'wikiGetlink', util.getUrl, 'Use mw.util.getUrl instead.' );
 
-       /**
-        * Access key prefix. Might be wrong for browsers implementing the accessKeyLabel property.
-        * @property {string} tooltipAccessKeyPrefix
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyPrefix', $.fn.updateTooltipAccessKeys.getAccessKeyPrefix(), 'Use jquery.accessKeyLabel instead.' );
-
-       /**
-        * Regex to match accesskey tooltips.
-        *
-        * Should match:
-        *
-        * - "[ctrl-option-x]"
-        * - "[alt-shift-x]"
-        * - "[ctrl-alt-x]"
-        * - "[ctrl-x]"
-        *
-        * The accesskey is matched in group $6.
-        *
-        * Will probably not work for browsers implementing the accessKeyLabel property.
-        *
-        * @property {RegExp} tooltipAccessKeyRegexp
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyRegexp', /\[(ctrl-)?(option-)?(alt-)?(shift-)?(esc-)?(.)\]$/, 'Use jquery.accessKeyLabel instead.' );
-
        /**
         * Add the appropriate prefix to the accesskey shown in the tooltip.
         *
index 63753f9..3edf99f 100644 (file)
@@ -37,34 +37,34 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/JohnDoe" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/JohnDoe">JohnDoe</a>',
+                                       . 'title="Special:Contributions/JohnDoe"><bdi>JohnDoe</bdi></a>',
                                0, 'JohnDoe', false,
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">::1</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>::1</bdi></a>',
                                0, '::1', false,
                                'Anonymous with pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0:0:0:0:0:0:0:1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1">::1</a>',
+                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1"><bdi>::1</bdi></a>',
                                0, '0:0:0:0:0:0:0:1', false,
                                'Anonymous with almost pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001">::1</a>',
+                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001"><bdi>::1</bdi></a>',
                                0, '0000:0000:0000:0000:0000:0000:0000:0001', false,
                                'Anonymous with full IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>AlternativeUsername</bdi></a>',
                                0, '::1', 'AlternativeUsername',
                                'Anonymous with pretty IPv6 and an alternative username'
                        ],
@@ -73,14 +73,14 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">127.0.0.1</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>127.0.0.1</bdi></a>',
                                0, '127.0.0.1', false,
                                'Anonymous with IPv4'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>AlternativeUsername</bdi></a>',
                                0, '127.0.0.1', 'AlternativeUsername',
                                'Anonymous with IPv4 and an alternative username'
                        ],
index 4301fb8..49907c8 100644 (file)
@@ -49,6 +49,19 @@ END;
                $this->assertContains( "Wikitext in Heading and also html", $headings );
        }
 
+       public function testDefaultSort() {
+               $text = <<<END
+Louise Michel
+== Heading one ==
+Some text
+==== See also ====
+* Also things to see!
+{{DEFAULTSORT:Michel, Louise}}
+END;
+               $struct = $this->getStructure( $text );
+               $this->assertEquals( "Michel, Louise", $struct->getDefaultSort() );
+       }
+
        public function testHeadingsFirst() {
                $text = <<<END
 == Heading one ==
index 0751409..16297ad 100644 (file)
@@ -291,6 +291,56 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertTrue( $called, 'Callback reached' );
        }
 
+       /**
+        * @covers DatabaseBase::setTransactionListener()
+        */
+       public function testTransactionListener() {
+               $db = $this->db;
+
+               $db->setTransactionListener( 'ping', function() use ( $db, &$called ) {
+                       $called = true;
+               } );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback still reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->rollback( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $db->setTransactionListener( 'ping', null );
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertFalse( $called, 'Callback not reached' );
+       }
+
+       /**
+        * @covers DatabaseBase::clearSnapshot()
+        */
+       public function testClearSnapshot() {
+               $db = $this->db;
+
+               $db->clearSnapshot( __METHOD__ ); // ok
+               $db->clearSnapshot( __METHOD__ ); // ok
+
+               $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               $db->query( 'SELECT 1', __METHOD__ );
+               $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
+               $db->clearSnapshot( __METHOD__ ); // ok
+               $db->restoreFlags( $db::RESTORE_PRIOR );
+
+               $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
+       }
+
        public function testGetScopedLock() {
                $db = $this->db;
 
index b09e5b1..c289839 100644 (file)
@@ -50,7 +50,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase {
        private static function removeSomeHtml( $html ) {
                $html = str_replace( '&quot;', '"', $html );
                $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm
-               return trim( preg_replace( '/<(a|span)[^>]*>([^<]*)<\/\1>/', '$2', $html ) );
+               return trim( strip_tags( $html ) );
        }
 
        private static function removeApiMetaData( $val ) {
index d697507..4eac362 100644 (file)
                assert.strictEqual( mw.util.getParamValue( 'TEST', url ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
        } );
 
-       QUnit.test( 'tooltipAccessKey', 4, function ( assert ) {
-               this.suppressWarnings();
-
-               assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'tooltipAccessKeyPrefix must be a string' );
-               assert.equal( $.type( mw.util.tooltipAccessKeyRegexp ), 'regexp', 'tooltipAccessKeyRegexp is a regexp' );
-               assert.ok( mw.util.updateTooltipAccessKeys, 'updateTooltipAccessKeys is non-empty' );
-
-               'Example [a]'.replace( mw.util.tooltipAccessKeyRegexp, function ( sub, m1, m2, m3, m4, m5, m6 ) {
-                       assert.equal( m6, 'a', 'tooltipAccessKeyRegexp finds the accesskey hint' );
-               } );
-
-               this.restoreWarnings();
-       } );
-
        QUnit.test( '$content', 2, function ( assert ) {
                assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
                assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );