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
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
'GenericArrayObject' => __DIR__ . '/includes/libs/GenericArrayObject.php',
'GetConfiguration' => __DIR__ . '/maintenance/getConfiguration.php',
'GetLagTimes' => __DIR__ . '/maintenance/getLagTimes.php',
- 'GetSlaveServer' => __DIR__ . '/maintenance/getSlaveServer.php',
+ 'GetSlaveServer' => __DIR__ . '/maintenance/getReplicaServer.php',
'GetTextMaint' => __DIR__ . '/maintenance/getText.php',
'GitInfo' => __DIR__ . '/includes/GitInfo.php',
'GlobalDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
+ 'WaitConditionLoop' => __DIR__ . '/includes/libs/WaitConditionLoop.php',
'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
$this->mReason = $options['reason'];
$this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
- $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $options['expiry'] );
+ $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] );
# Boolean settings
$this->mAuto = (bool)$options['auto'];
* @return Block|null
*/
public static function newFromID( $id ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->selectRow(
'ipblocks',
self::selectFields(),
* @return bool Whether a relevant block was found
*/
protected function newLoad( $vagueTarget = null ) {
- $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_REPLICA );
if ( $this->type !== null ) {
$conds = [
# range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
# so we can improve performance by filtering on a LIKE clause
$chunk = self::getIpFragment( $start );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$like = $dbr->buildLike( $chunk, $dbr->anyString() );
# Fairly hard to make a malicious SQL statement out of hex characters,
$this->mParentBlockId = $row->ipb_parent_block_id;
// I wish I didn't have to do this
- $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $row->ipb_expiry );
+ $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $row->ipb_expiry );
$this->isHardblock( !$row->ipb_anon_only );
$this->isAutoblocking( $row->ipb_enable_autoblock );
*/
protected function getDatabaseArray( $db = null ) {
if ( !$db ) {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
}
$expiry = $db->encodeExpiry( $this->mExpiry );
return;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
$conds = [ 'rc_user_text' => (string)$block->getTarget() ];
* @param array $ipChain List of IPs (strings), usually retrieved from the
* X-Forwarded-For header of the request
* @param bool $isAnon Exclude anonymous-only blocks if false
- * @param bool $fromMaster Whether to query the master or slave database
+ * @param bool $fromMaster Whether to query the master or replica DB
* @return array Array of Blocks
* @since 1.22
*/
if ( $fromMaster ) {
$db = wfGetDB( DB_MASTER );
} else {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
}
$conds = $db->makeList( $conds, LIST_OR );
if ( !$isAnon ) {
return true;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow(
'category',
[ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
*/
public function getMembers( $limit = false, $offset = '' ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds = [ 'cl_to' => $this->getName(), 'cl_from = page_id' ];
$options = [ 'ORDER BY' => 'cl_sortkey' ];
/** @var string "AND" or "OR" */
protected $mode;
- /** @var IDatabase Read-DB slave */
+ /** @var IDatabase Read-DB replica DB */
protected $dbr;
/**
* @return array Array of page_ids (those given to seed() that match the conditions)
*/
public function run() {
- $this->dbr = wfGetDB( DB_SLAVE );
+ $this->dbr = wfGetDB( DB_REPLICA );
while ( count( $this->next ) > 0 ) {
$this->scanNextLayer();
}
}
function doCategoryQuery() {
- $dbr = wfGetDB( DB_SLAVE, 'category' );
+ $dbr = wfGetDB( DB_REPLICA, 'category' );
$this->nextPage = [
'page' => null,
* - password: DB password
* - type: DB type
*
- * - load: Ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0.
+ * - load: Ratio of DB_REPLICA load, must be >=0, the sum of all loads must be >0.
* If this is zero for any given server, no normal query traffic will be
* sent to it. It will be excluded from lag checks in maintenance scripts.
* The only way it can receive traffic is if groupLoads is used.
* to several groups, the most specific group defined here is used.
*
* - flags: bit field
- * - DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
+ * - DBO_DEFAULT -- turns on DBO_TRX only if "cliMode" is off (recommended)
* - DBO_DEBUG -- equivalent of $wgDebugDumpSql
* - DBO_TRX -- wrap entire request in a transaction
* - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
* - DBO_COMPRESS -- uses internal compression in database connections,
* if available
*
- * - max lag: (optional) Maximum replication lag before a slave will taken out of rotation
+ * - max lag: (optional) Maximum replication lag before a replica DB goes out of rotation
* - is static: (optional) Set to true if the dataset is static and no replication is used.
+ * - cliMode: (optional) Connection handles will not assume that requests are short-lived
+ * nor that INSERT..SELECT can be rewritten into a buffered SELECT and INSERT.
+ * [Default: uses value of $wgCommandLineMode]
*
* These and any other user-defined properties will be assigned to the mLBInfo member
* variable of the Database object.
* perhaps in some command-line scripts).
*
* The first server listed in this array (with key 0) will be the master. The
- * rest of the servers will be slaves. To prevent writes to your slaves due to
+ * rest of the servers will be replica DBs. To prevent writes to your replica DBs due to
* accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
- * slaves in my.cnf. You can set read_only mode at runtime using:
+ * replica DBs in my.cnf. You can set read_only mode at runtime using:
*
* @code
* SET @@read_only=1;
* @endcode
*
- * Since the effect of writing to a slave is so damaging and difficult to clean
+ * Since the effect of writing to a replica DB is so damaging and difficult to clean
* up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
* our masters, and then set read_only=0 on masters at runtime.
*/
$wgSquidMaxage = 18000;
/**
- * Cache timeout for the CDN when DB slave lag is high
+ * Cache timeout for the CDN when DB replica DB lag is high
* @see $wgSquidMaxage
* @since 1.27
*/
* If set, any SquidPurge call on a URL or URLs will send a second purge no less than
* this many seconds later via the job queue. This requires delayed job support.
* This should be safely higher than the 'max lag' value in $wgLBFactoryConf, so that
- * slave lag does not cause page to be stuck in stales states in CDN.
+ * replica DB lag does not cause page to be stuck in stales states in CDN.
*
* This also fixes race conditions in two-tiered CDN setups (e.g. cdn2 => cdn1 => MediaWiki).
* If a purge for a URL reaches cdn2 before cdn1 and a request reaches cdn2 for that URL,
*/
$wgStatsdMetricPrefix = 'MediaWiki';
+/**
+ * Sampling rate for statsd metrics as an associative array of patterns and rates.
+ * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+ * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+ * @since 1.28
+ */
+$wgStatsdSamplingRates = [];
+
/**
* InfoAction retrieves a list of transclusion links (both to and from).
* This number puts a limit on that query in the case of highly transcluded
$wgJobBackoffThrottling = [];
/**
- * Make job runners commit changes for slave-lag prone jobs one job at a time.
- * This is useful if there are many job workers that race on slave lag checks.
+ * Make job runners commit changes for replica DB-lag prone jobs one job at a time.
+ * This is useful if there are many job workers that race on replica DB lag checks.
* If set, jobs taking this many seconds of DB write time have serialized commits.
*
* Note that affected jobs may have worse lock contention. Also, if they affect
$wgAPIMaxUncachedDiffs = 1;
/**
- * Maximum amount of DB lag on a majority of DB slaves to tolerate
+ * Maximum amount of DB lag on a majority of DB replica DBs to tolerate
* before forcing bots to retry any write requests via API errors.
* This should be lower than the 'max lag' value in $wgLBFactoryConf.
*/
* Valid database indexes
* Operation-based indexes
*/
-define( 'DB_SLAVE', -1 ); # Read from the slave (or only server)
+define( 'DB_REPLICA', -1 ); # Read from a replica (or only server)
define( 'DB_MASTER', -2 ); # Write to master (or only server)
/**@}*/
# Obsolete aliases
+define( 'DB_SLAVE', -1 );
define( 'DB_READ', -1 );
define( 'DB_WRITE', -2 );
* @return bool|stdClass
*/
protected function getLastDelete() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$data = $dbr->selectRow(
[ 'logging', 'user' ],
[
$statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
$statsdSender = new SocketSender( $statsdHost, $statsdPort );
$statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
+ $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
$statsdClient->send( $context->getStats()->getBuffer() );
} catch ( Exception $ex ) {
MWExceptionHandler::logException( $ex );
* Check if the site is in read-only mode and return the message if so
*
* This checks wfConfiguredReadOnlyReason() and the main load balancer
- * for slave lag. This may result in DB_SLAVE connection being made.
+ * for replica DB lag. This may result in DB connection being made.
*
* @return string|bool String when in read-only mode; false otherwise
*/
* Get a Database object.
*
* @param int $db Index of the connection to get. May be DB_MASTER for the
- * master (for write queries), DB_SLAVE for potentially lagged read
+ * master (for write queries), DB_REPLICA for potentially lagged read
* queries, or an integer >= 0 for a particular server.
*
* @param string|string[] $groups Query groups. An array of group names that this query
*
* @param string|bool $wiki The wiki ID, or false for the current wiki
*
- * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request
+ * Note: multiple calls to wfGetDB(DB_REPLICA) during the course of one request
* will always return the same object, unless the underlying connection or load
* balancer is manually destroyed.
*
}
/**
- * Waits for the slaves to catch up to the master position
+ * Waits for the replica DBs to catch up to the master position
*
* Use this when updating very large numbers of rows, as in maintenance scripts,
- * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
*
* By default this waits on the main DB cluster of the current wiki.
* If $cluster is set to "*" it will wait on all DB clusters, including
if ( isset( self::$blobCache[$this->mOldId] ) ) {
$obj = self::$blobCache[$this->mOldId];
} else {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow(
'text',
[ 'old_flags', 'old_text' ],
* @return string|bool
*/
function getText() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow( 'cur', [ 'cur_text' ], [ 'cur_id' => $this->mCurId ] );
if ( !$row ) {
return false;
* @return array Array to be passed to Database::buildLike() or false on error
*/
public static function makeLikeArray( $filterEntry, $protocol = 'http://' ) {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
$target = $protocol . $filterEntry;
$bits = wfParseUrl( $target );
$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 ]
);
}
return null;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Up to the value of $wgShowRollbackEditCount revisions are counted
$res = $dbr->select(
$request->response()->setCookie( 'UseCDNCache', 'false', $expires, $options );
}
- // Avoid letting a few seconds of slave lag cause a month of stale data. This logic is
+ // Avoid letting a few seconds of replica DB lag cause a month of stale data. This logic is
// also intimately related to the value of $wgCdnReboundPurgeDelay.
- if ( $factory->laggedSlaveUsed() ) {
+ if ( $factory->laggedReplicaUsed() ) {
$maxAge = $config->get( 'CdnMaxageLagged' );
$context->getOutput()->lowerCdnMaxage( $maxAge );
$request->response()->header( "X-Database-Lagged: true" );
$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 );
$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;
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 );
}
}
use MediaWiki\Linker\LinkRendererFactory;
use MediaWiki\Services\SalvageableService;
use MediaWiki\Services\ServiceContainer;
+use MediaWiki\Services\NoSuchServiceException;
use MWException;
use ObjectCache;
use SearchEngine;
*/
class MergeHistory {
- /** @const int Maximum number of revisions that can be merged at once (avoid too much slave lag) */
+ /** @const int Maximum number of revisions that can be merged at once */
const REVISION_LIMIT = 5000;
/** @var Title Page from which history will be merged */
$lb->setArray( $arr );
# Fetch existence plus the hiddencat property
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$fields = array_merge(
LinkCache::getSelectFields(),
[ 'page_namespace', 'page_title', 'pp_value' ]
// Hook that allows last minute changes to the output page, e.g.
// adding of CSS or Javascript by extensions.
Hooks::run( 'BeforePageDisplay', [ &$this, &$sk ] );
- $this->getSkin()->setupSkinUserCss( $this );
try {
$sk->outputPage();
}
/**
- * Show a warning about slave lag
+ * Show a warning about replica DB lag
*
* If the lag is higher than $wgSlaveLagCritical seconds,
* then the warning is a bit more obvious. If the lag is
'user.styles',
'user.cssprefs',
] );
+ $this->getSkin()->setupSkinUserCss( $this );
// Prepare exempt modules for buildExemptModules()
$exemptGroups = [ 'site' => [], 'noscript' => [], 'private' => [], 'user' => [] ];
}
if ( $queryIDs ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->select(
'page_props',
[
}
if ( $queryIDs != [] ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->select(
'page_props',
[
* @return bool
*/
private function checkIfSent() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$sent = $dbr->selectField(
'updatelog', '1', [ 'ul_key' => $this->key ], __METHOD__ );
return $sent !== false;
*/
private function getOrCreatePingbackId() {
if ( !$this->id ) {
- $id = wfGetDB( DB_SLAVE )->selectField(
+ $id = wfGetDB( DB_REPLICA )->selectField(
'updatelog', 'ul_value', [ 'ul_key' => 'PingBack' ] );
if ( $id == false ) {
* Get extra parameters for the query string when redirecting after
* successful save.
*
- * @return array()
+ * @return array
*/
public function getExtraSuccessRedirectParameters() {
return [];
$t = Title::newFromText( $search, $ns );
$prefix = $t ? $t->getDBkey() : '';
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select( 'page',
[ 'page_id', 'page_namespace', 'page_title' ],
[
* @todo document
*/
class Revision implements IDBAccessObject {
+ /** @var int|null */
protected $mId;
-
- /**
- * @var int|null
- */
+ /** @var int|null */
protected $mPage;
+ /** @var string */
protected $mUserText;
+ /** @var string */
protected $mOrigUserText;
+ /** @var int */
protected $mUser;
+ /** @var bool */
protected $mMinorEdit;
+ /** @var string */
protected $mTimestamp;
+ /** @var int */
protected $mDeleted;
+ /** @var int */
protected $mSize;
+ /** @var string */
protected $mSha1;
+ /** @var int */
protected $mParentId;
+ /** @var string */
protected $mComment;
+ /** @var string */
protected $mText;
+ /** @var int */
protected $mTextId;
+ /** @var int */
+ protected $mUnpatrolled;
- /**
- * @var stdClass|null
- */
+ /** @var stdClass|null */
protected $mTextRow;
- /**
- * @var null|Title
- */
+ /** @var null|Title */
protected $mTitle;
+ /** @var bool */
protected $mCurrent;
+ /** @var string */
protected $mContentModel;
+ /** @var string */
protected $mContentFormat;
- /**
- * @var Content|null|bool
- */
+ /** @var Content|null|bool */
protected $mContent;
-
- /**
- * @var null|ContentHandler
- */
+ /** @var null|ContentHandler */
protected $mContentHandler;
- /**
- * @var int
- */
+ /** @var int */
protected $mQueryFlags = 0;
+ /** @var bool Used for cached values to reload user text and rev_deleted */
+ protected $mRefreshMutableFields = false;
+ /** @var string Wiki ID; false means the current wiki */
+ protected $mWiki = false;
// Revision deletion constants
const DELETED_TEXT = 1;
} else {
// Use a join to get the latest revision
$conds[] = 'rev_id=page_latest';
- $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
return self::loadFromConds( $db, $conds, $flags );
}
}
} else {
// Use a join to get the latest revision
$conds[] = 'rev_id = page_latest';
- $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
return self::loadFromConds( $db, $conds, $flags );
}
}
* Given a set of conditions, fetch a revision
*
* This method is used then a revision ID is qualified and
- * will incorporate some basic slave/master fallback logic
+ * will incorporate some basic replica DB/master fallback logic
*
* @param array $conditions
* @param int $flags (optional)
* @return Revision|null
*/
private static function newFromConds( $conditions, $flags = 0 ) {
- $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
$rev = self::loadFromConds( $db, $conditions, $flags );
// Make sure new pending/committed revision are visibile later on
* @return Revision|null
*/
private static function loadFromConds( $db, $conditions, $flags = 0 ) {
- $res = self::fetchFromConds( $db, $conditions, $flags );
- if ( $res ) {
- $row = $res->fetchObject();
- if ( $row ) {
- $ret = new Revision( $row );
- return $ret;
- }
+ $row = self::fetchFromConds( $db, $conditions, $flags );
+ if ( $row ) {
+ $rev = new Revision( $row );
+ $rev->mWiki = $db->getWikiID();
+
+ return $rev;
}
- $ret = null;
- return $ret;
+
+ return null;
}
/**
* fetch all of a given page's revisions in turn.
* Each row can be fed to the constructor to get objects.
*
- * @param Title $title
+ * @param LinkTarget $title
* @return ResultWrapper
+ * @deprecated Since 1.28
*/
- public static function fetchRevision( $title ) {
- return self::fetchFromConds(
- wfGetDB( DB_SLAVE ),
+ public static function fetchRevision( LinkTarget $title ) {
+ $row = self::fetchFromConds(
+ wfGetDB( DB_REPLICA ),
[
'rev_id=page_latest',
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey()
]
);
+
+ return new FakeResultWrapper( $row ? [ $row ] : [] );
}
/**
* @param IDatabase $db
* @param array $conditions
* @param int $flags (optional)
- * @return ResultWrapper
+ * @return stdClass
*/
private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
$fields = array_merge(
self::selectPageFields(),
self::selectUserFields()
);
- $options = [ 'LIMIT' => 1 ];
+ $options = [];
if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
$options[] = 'FOR UPDATE';
}
- return $db->select(
+ return $db->selectRow(
[ 'revision', 'page', 'user' ],
$fields,
$conditions,
}
// rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
if ( $this->mId !== null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
$row = $dbr->selectRow(
[ 'page', 'revision' ],
self::selectPageFields(),
- [ 'page_id=rev_page',
- 'rev_id' => $this->mId ],
- __METHOD__ );
+ [ 'page_id=rev_page', 'rev_id' => $this->mId ],
+ __METHOD__
+ );
if ( $row ) {
+ // @TODO: better foreign title handling
$this->mTitle = Title::newFromRow( $row );
}
}
- if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
- $this->mTitle = Title::newFromID( $this->mPage );
+ if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) {
+ // Loading by ID is best, though not possible for foreign titles
+ if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
+ $this->mTitle = Title::newFromID( $this->mPage );
+ }
}
return $this->mTitle;
* @return string
*/
public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
+ $this->loadMutableFields();
+
if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
return '';
} elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
* @return RecentChange|null
*/
public function getRecentChange( $flags = 0 ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
* @return bool
*/
public function isDeleted( $field ) {
- return ( $this->mDeleted & $field ) == $field;
+ if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
+ // Current revisions of pages cannot have the content hidden. Skipping this
+ // check is very useful for Parser as it fetches templates using newKnownCurrent().
+ // Calling getVisibility() in that case triggers a verification database query.
+ return false; // no need to check
+ }
+
+ return ( $this->getVisibility() & $field ) == $field;
}
/**
* @return int
*/
public function getVisibility() {
+ $this->loadMutableFields();
+
return (int)$this->mDeleted;
}
}
if ( !$row ) {
- // Text data is immutable; check slaves first.
- $dbr = wfGetDB( DB_SLAVE );
+ // Text data is immutable; check replica DBs first.
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow( 'text',
[ 'old_text', 'old_flags' ],
[ 'old_id' => $textId ],
__METHOD__ );
}
- // Fallback to the master in case of slave lag. Also use FOR UPDATE if it was
+ // Fallback to the master in case of replica DB lag. Also use FOR UPDATE if it was
// used to fetch this revision to avoid missing the row due to REPEATABLE-READ.
$forUpdate = ( $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING );
if ( !$row && ( $forUpdate || wfGetLB()->getServerCount() > 1 ) ) {
wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
}
- # No negative caching -- negative hits on text rows may be due to corrupted slave servers
+ # No negative caching -- negative hits on text rows may be due to corrupted replica DB servers
if ( $wgRevisionCacheExpiry && $text !== false ) {
$processCache->set( $key, $text );
$cache->set( $key, $text, $wgRevisionCacheExpiry );
* @return bool
*/
public function userCan( $field, User $user = null ) {
- return self::userCanBitfield( $this->mDeleted, $field, $user );
+ return self::userCanBitfield( $this->getVisibility(), $field, $user );
}
/**
static function getTimestampFromId( $title, $id, $flags = 0 ) {
$db = ( $flags & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
// Casting fix for databases that can't take '' for rev_id
if ( $id == '' ) {
$id = 0;
}
return true;
}
+
+ /**
+ * Load a revision based on a known page ID and current revision ID from the DB
+ *
+ * This method allows for the use of caching, though accessing anything that normally
+ * requires permission checks (aside from the text) will trigger a small DB lookup.
+ * The title will also be lazy loaded, though setTitle() can be used to preload it.
+ *
+ * @param IDatabase $db
+ * @param int $pageId Page ID
+ * @param int $revId Known current revision of this page
+ * @return Revision|bool Returns false if missing
+ * @since 1.28
+ */
+ public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) {
+ $cache = ObjectCache::getMainWANInstance();
+ return $cache->getWithSetCallback(
+ // Page/rev IDs passed in from DB to reflect history merges
+ $cache->makeGlobalKey( 'revision', $db->getWikiID(), $pageId, $revId ),
+ $cache::TTL_WEEK,
+ function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
+ $setOpts += Database::getCacheSetOptions( $db );
+
+ $rev = Revision::loadFromPageId( $db, $pageId, $revId );
+ // Reflect revision deletion and user renames
+ if ( $rev ) {
+ $rev->mTitle = null; // mutable; lazy-load
+ $rev->mRefreshMutableFields = true;
+ }
+
+ return $rev ?: false; // don't cache negatives
+ }
+ );
+ }
+
+ /**
+ * For cached revisions, make sure the user name and rev_deleted is up-to-date
+ */
+ private function loadMutableFields() {
+ if ( !$this->mRefreshMutableFields ) {
+ return; // not needed
+ }
+
+ $this->mRefreshMutableFields = false;
+ $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
+ $row = $dbr->selectRow(
+ [ 'revision', 'user' ],
+ [ 'rev_deleted', 'user_name' ],
+ [ 'rev_id' => $this->mId, 'user_id = rev_user' ],
+ __METHOD__
+ );
+ if ( $row ) { // update values
+ $this->mDeleted = (int)$row->rev_deleted;
+ $this->mUserText = $row->user_name;
+ }
+ }
}
*/
public function reset() {
if ( !$this->res ) {
- $this->res = $this->doQuery( wfGetDB( DB_SLAVE ) );
+ $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
} else {
$this->res->rewind();
}
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;
# Update schema
$u = new SiteStatsUpdate( 0, 0, 0 );
$u->doUpdate();
- self::$row = self::doLoad( wfGetDB( DB_SLAVE ) );
+ self::$row = self::doLoad( wfGetDB( DB_REPLICA ) );
}
self::$loaded = true;
static function loadAndLazyInit() {
global $wgMiserMode;
- wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
- $row = self::doLoad( wfGetDB( DB_SLAVE ) );
+ wfDebug( __METHOD__ . ": reading site_stats from replica DB\n" );
+ $row = self::doLoad( wfGetDB( DB_REPLICA ) );
if ( !self::isSane( $row ) ) {
// Might have just been initialized during this request? Underflow?
- wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
+ wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
// clean schema with mwdumper.
wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
- SiteStatsInit::doAllAndCommit( wfGetDB( DB_SLAVE ) );
+ SiteStatsInit::doAllAndCommit( wfGetDB( DB_REPLICA ) );
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
wfMemcKey( 'SiteStats', 'groupcounts', $group ),
$cache::TTL_HOUR,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
*/
static function pagesInNs( $ns ) {
if ( !isset( self::$pageCount[$ns] ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
self::$pageCount[$ns] = (int)$dbr->selectField(
'page',
'COUNT(*)',
} elseif ( $database ) {
$this->db = wfGetDB( DB_MASTER );
} else {
- $this->db = wfGetDB( DB_SLAVE, 'vslow' );
+ $this->db = wfGetDB( DB_REPLICA, 'vslow' );
}
}
* @return Title|null The new object, or null on an error
*/
public static function newFromID( $id, $flags = 0 ) {
- $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
$row = $db->selectRow(
'page',
self::getSelectFields(),
if ( !count( $ids ) ) {
return [];
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'page',
* @return Title|null An object representing the article, or null if no such article was found
*/
public static function nameOf( $id ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$s = $dbr->selectRow(
'page',
* @param string $action Action that permission needs to be checked for
* @param User $user User to check
* @param string $rigor One of (quick,full,secure)
- * - quick : does cheap permission checks from slaves (usable for GUI creation)
- * - full : does cheap and expensive checks possibly from a slave
+ * - quick : does cheap permission checks from replica DBs (usable for GUI creation)
+ * - full : does cheap and expensive checks possibly from a replica DB
* - secure : does cheap and expensive checks, using the master as needed
* @param array $ignoreErrors Array of Strings Set this to a list of message keys
* whose corresponding errors may be ignored.
* @param string $action Action that permission needs to be checked for
* @param User $user User to check
* @param string $rigor One of (quick,full,secure)
- * - quick : does cheap permission checks from slaves (usable for GUI creation)
- * - full : does cheap and expensive checks possibly from a slave
+ * - quick : does cheap permission checks from replica DBs (usable for GUI creation)
+ * - full : does cheap and expensive checks possibly from a replica DB
* - secure : does cheap and expensive checks, using the master as needed
* @param bool $short Set this to true to stop after the first permission error.
* @return array Array of arrays of the arguments to wfMessage to explain permissions problems.
}
if ( $this->mTitleProtection === null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'protected_titles',
[
return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $this->getNamespace() == NS_FILE ) {
$tables = [ 'imagelinks', 'page_restrictions' ];
* restrictions from page table (pre 1.10)
*/
public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$restrictionTypes = $this->getRestrictionTypes();
*/
public function loadRestrictions( $oldFashionedRestrictions = null ) {
if ( !$this->mRestrictionsLoaded ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $this->exists() ) {
$res = $dbr->select(
'page_restrictions',
return [];
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds['page_namespace'] = $this->getNamespace();
$conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
$options = [];
if ( $this->getNamespace() < 0 ) {
$n = 0;
} else {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$n = $dbr->selectField( 'archive', 'COUNT(*)',
[ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
if ( $this->getNamespace() < 0 ) {
return false;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$deleted = (bool)$dbr->selectField( 'archive', '1',
[ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
__METHOD__
if ( count( $options ) > 0 ) {
$db = wfGetDB( DB_MASTER );
} else {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
}
$res = $db->select(
return [];
}
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
$blNamespace = "{$prefix}_namespace";
$blTitle = "{$prefix}_title";
return [];
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
[ 'page', 'pagelinks' ],
[ 'pl_namespace', 'pl_title' ],
return $data;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'categorylinks',
* @return int|bool Old revision ID, or false if none exists
*/
public function getPreviousRevisionID( $revId, $flags = 0 ) {
- $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
$revId = $db->selectField( 'revision', 'rev_id',
[
'rev_page' => $this->getArticleID( $flags ),
* @return int|bool Next revision ID, or false if none exists
*/
public function getNextRevisionID( $revId, $flags = 0 ) {
- $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
$revId = $db->selectField( 'revision', 'rev_id',
[
'rev_page' => $this->getArticleID( $flags ),
public function getFirstRevision( $flags = 0 ) {
$pageId = $this->getArticleID( $flags );
if ( $pageId ) {
- $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
$row = $db->selectRow( 'revision', Revision::selectFields(),
[ 'rev_page' => $pageId ],
__METHOD__,
* @return bool
*/
public function isNewPage() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
}
}
if ( $this->mIsBigDeletion === null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$revCount = $dbr->selectRowCount(
'revision',
}
if ( $this->mEstimateRevisions === null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
[ 'rev_page' => $this->getArticleID() ], __METHOD__ );
}
if ( !$old || !$new ) {
return 0; // nothing to compare
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds = [
'rev_page' => $this->getArticleID(),
'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
}
return $authors;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
[
'rev_page' => $this->getArticleID(),
*/
public function getTouched( $db = null ) {
if ( $db === null ) {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
}
$touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
return $touched;
public function getRedirectsHere( $ns = null ) {
$redirs = [];
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$where = [
'rd_namespace' => $this->getNamespace(),
'rd_title' => $this->getDBkey(),
* @throws MWException
*/
private function getConnection() {
- return $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] );
+ return $this->loadBalancer->getConnection( DB_REPLICA, [ 'watchlist' ] );
}
/**
}
/**
- * @param int $slaveOrMaster DB_MASTER or DB_SLAVE
+ * @param int $dbIndex DB_MASTER or DB_REPLICA
*
* @return DatabaseBase
* @throws MWException
*/
- private function getConnection( $slaveOrMaster ) {
- return $this->loadBalancer->getConnection( $slaveOrMaster, [ 'watchlist' ] );
+ private function getConnection( $dbIndex ) {
+ return $this->loadBalancer->getConnection( $dbIndex, [ 'watchlist' ] );
}
/**
* @return int
*/
public function countWatchedItems( User $user ) {
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$return = (int)$dbr->selectField(
'watchlist',
'COUNT(*)',
* @return int
*/
public function countWatchers( LinkTarget $target ) {
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$return = (int)$dbr->selectField(
'watchlist',
'COUNT(*)',
* @throws MWException
*/
public function countVisitingWatchers( LinkTarget $target, $threshold ) {
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$visitingWatchers = (int)$dbr->selectField(
'watchlist',
'COUNT(*)',
public function countWatchersMultiple( array $targets, array $options = [] ) {
$dbOptions = [ 'GROUP BY' => [ 'wl_namespace', 'wl_title' ] ];
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
if ( array_key_exists( 'minimumWatchers', $options ) ) {
$dbOptions['HAVING'] = 'COUNT(*) >= ' . (int)$options['minimumWatchers'];
array $targetsWithVisitThresholds,
$minimumWatchers = null
) {
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$conds = $this->getVisitingWatchersCondition( $dbr, $targetsWithVisitThresholds );
return false;
}
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$row = $dbr->selectRow(
'watchlist',
'wl_notificationtimestamp',
"wl_title {$options['sort']}"
];
}
- $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_SLAVE );
+ $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_REPLICA );
$res = $db->select(
'watchlist',
return $timestamps;
}
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$lb = new LinkBatch( $targetsToLoad );
$res = $dbr->select(
$queryOptions['LIMIT'] = $unreadLimit;
}
- $dbr = $this->getConnection( DB_SLAVE );
+ $dbr = $this->getConnection( DB_REPLICA );
$rowCount = $dbr->selectRowCount(
'watchlist',
'1',
return new FakeResultWrapper( [] );
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $direction === self::DIR_PREV ) {
list( $dirs, $oper ) = [ "ASC", ">=" ];
$title = $page->getTitle();
$id = $title->getArticleID();
- $dbr = wfGetDB( DB_SLAVE );
- $dbrWatchlist = wfGetDB( DB_SLAVE, 'watchlist' );
+ $dbr = wfGetDB( DB_REPLICA );
+ $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
$setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
public function onSubmit( $data ) {
$this->useTransactionalTimeLimit();
- $source = $this->page->getFile()->getArchiveVirtualUrl(
- $this->getRequest()->getText( 'oldimage' )
- );
+ $old = $this->getRequest()->getText( 'oldimage' );
+ $localFile = $this->page->getFile();
+ $oldFile = OldLocalFile::newFromArchiveName( $this->getTitle(), $localFile->getRepo(), $old );
+
+ $source = $localFile->getArchiveVirtualUrl( $old );
$comment = $data['comment'];
+ if ( $localFile->getSha1() === $oldFile->getSha1() ) {
+ return Status::newFatal( 'filerevert-identical' );
+ }
+
// TODO: Preserve file properties from database instead of reloading from file
- return $this->page->getFile()->upload(
+ return $localFile->upload(
$source,
$comment,
$comment,
}
/**
- * Gets a default slave database connection object
+ * Gets a default replica DB connection object
* @return DatabaseBase
*/
protected function getDB() {
if ( !isset( $this->mSlaveDB ) ) {
- $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
+ $this->mSlaveDB = wfGetDB( DB_REPLICA, 'api' );
}
return $this->mSlaveDB;
* @param array $params
* @param bool|string $load Whether load the object's state from the database:
* - false: don't load (if the pageid is given, it will still be loaded)
- * - 'fromdb': load from a slave database
+ * - 'fromdb': load from a replica DB
* - 'fromdbmaster': load from the master database
* @return WikiPage
*/
class ApiExpandTemplates extends ApiBase {
public function execute() {
- // Cache may vary on $wgUser because ParserOptions gets data from it
+ // Cache may vary on the user because ParserOptions gets data from it
$this->getMain()->setCacheMode( 'anon-public-user-private' );
// Get parameters
}
}
- // If a majority of slaves are too lagged then disallow writes
- $slaveCount = wfGetLB()->getServerCount() - 1;
- if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
+ // If a majority of replica DBs are too lagged then disallow writes
+ $replicaCount = wfGetLB()->getServerCount() - 1;
+ if ( $numLagged >= ceil( $replicaCount / 2 ) ) {
$laggedServers = implode( ', ', $laggedServers );
wfDebugLog(
'api-readonly',
$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 = [];
}
$formatModules = [];
}
+ $modules = array_unique( $modules );
+
$res = [];
foreach ( $modules as $m ) {
$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
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',
];
}
# Update the links tables
$updates = $content->getSecondaryDataUpdates(
$title, null, $forceRecursiveLinkUpdate, $p_result );
- DataUpdate::runUpdates( $updates );
+ foreach ( $updates as $update ) {
+ DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
+ }
$r['linkupdate'] = true;
$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
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.
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',
];
}
$this->fld_patrolled = isset( $prop['patrolled'] );
$this->fld_tags = isset( $prop['tags'] );
- // Most of this code will use the 'contributions' group DB, which can map to slaves
+ // Most of this code will use the 'contributions' group DB, which can map to replica DBs
// with extra user based indexes or partioning by user. The additional metadata
- // queries should use a regular slave since the lookup pattern is not all by user.
- $dbSecondary = $this->getDB(); // any random slave
+ // queries should use a regular replica DB since the lookup pattern is not all by user.
+ $dbSecondary = $this->getDB(); // any random replica DB
// TODO: if the query is going only against the revision table, should this be done?
- $this->selectNamedDB( 'contributions', DB_SLAVE, 'contributions' );
+ $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
$this->idMode = false;
if ( isset( $this->params['userprefix'] ) ) {
* @return void
*/
private function run( $resultPageSet = null ) {
- $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
+ $this->selectNamedDB( 'watchlist', DB_REPLICA, 'watchlist' );
$params = $this->extractRequestParams();
* @return string|null TS_MW timestamp or null
*/
private static function lastEditTime( User $user ) {
- $time = wfGetDB( DB_SLAVE )->selectField(
+ $time = wfGetDB( DB_REPLICA )->selectField(
'recentchanges',
'MAX(rc_timestamp)',
[ 'rc_user_text' => $user->getName() ],
}
protected static function validateLogId( $logid ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->selectField( 'logging', 'log_id', [ 'log_id' => $logid ],
__METHOD__ );
return (bool)$result;
"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.",
"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.",
"Verdy p",
"Yasten",
"Trial",
- "Pols12"
+ "Pols12",
+ "The RedBurn"
]
},
"apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].",
"apihelp-edit-param-sectiontitle": "Le titre pour une nouvelle section.",
"apihelp-edit-param-text": "Contenu de la page.",
"apihelp-edit-param-summary": "Modifier le résumé. Également le titre de la section quand $1section=new et $1sectiontitle n’est pas défini.",
- "apihelp-edit-param-tags": "Modifier les balises à appliquer à la révision.",
+ "apihelp-edit-param-tags": "Modifier les balises à appliquer à la version.",
"apihelp-edit-param-minor": "Modification mineure.",
"apihelp-edit-param-notminor": "Modification non mineure.",
"apihelp-edit-param-bot": "Marquer cette modification comme robot.",
"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.",
"apihelp-protect-param-expiry": "Horodatages d’expiration. Si un seul horodatage est fourni, il sera utilisé pour toutes les protections. Utiliser <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd> ou <kbd>never</kbd> pour une protection sans expiration.",
"apihelp-protect-param-reason": "Motif de (dé)protection.",
"apihelp-protect-param-tags": "Modifier les balises à appliquer à l’entrée dans le journal de protection.",
- "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne supporte la mise en cascade.",
+ "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne prend en charge la mise en cascade.",
"apihelp-protect-param-watch": "Si activé, ajouter la page (dé)protégée à la liste de suivi de l'utilisateur actuel.",
"apihelp-protect-param-watchlist": "Ajouter ou supprimer sans condition la page de la liste de suivi de l'utilisateur actuel, utiliser les préférences ou ne pas modifier le suivi.",
"apihelp-protect-example-protect": "Protéger une page",
"apihelp-query+search-paramvalue-prop-sectiontitle": "Ajoute le titre de la section correspondante.",
"apihelp-query+search-paramvalue-prop-categorysnippet": "Ajoute un extrait analysé de la catégorie correspondante.",
"apihelp-query+search-paramvalue-prop-isfilematch": "Ajoute un booléen indiquant si la recherche correspond au contenu du fichier.",
- "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
+ "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Désuet et ignoré.</span>",
"apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
"apihelp-query+search-param-limit": "Combien de pages renvoyer au total.",
"apihelp-query+search-param-interwiki": "Inclure les résultats interwiki dans la recherche, s’ils sont disponibles.",
"apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Renvoie la liste des extensions de fichier (types de fichier) autorisées au téléchargement.",
"apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.",
"apihelp-query+siteinfo-paramvalue-prop-restrictions": "Renvoie l’information sur les types de restriction disponibles (protection).",
- "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant <var>$1inlanguagecode</var>).",
+ "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que MédiaWiki prend en charge (éventuellement localisée en utilisant <var>$1inlanguagecode</var>).",
"apihelp-query+siteinfo-paramvalue-prop-skins": "Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).",
"apihelp-query+siteinfo-paramvalue-prop-extensiontags": "Renvoie une liste des balises d’extension de l’analyseur.",
"apihelp-query+siteinfo-paramvalue-prop-functionhooks": "Renvoie une liste des accroches de fonction de l’analyseur.",
"api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de recherche spécifié.\n\n<strong>NOTE :<strong> les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
"api-pageset-param-redirects-generator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>, et dans les pages renvoyées par <var>$1generator</var>.",
"api-pageset-param-redirects-nogenerator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>.",
- "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki supporte la conversion en variantes. Les langues qui supportent la conversion en variante incluent $1.",
+ "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki prend en charge la conversion en variantes. Les langues qui prennent en charge la conversion en variante incluent $1.",
"api-help-title": "Aide de l’API de MediaWiki",
"api-help-lead": "Ceci est une page d’aide de l’API de MediaWiki générée automatiquement.\n\nDocumentation et exemples : https://www.mediawiki.org/wiki/API",
"api-help-main-header": "Module principal",
"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>",
"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.",
"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\8b1ã\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>",
"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.",
"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}}",
"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]]."
}
"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> для контролю моделі вмісту.",
"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": "获取有关页面设置模块(提供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参见<kbd>[[Special:ApiHelp/query|action=query]]</kbd>的各种prop-module以从页面的当前版本获得信息。\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>以控制内容模型。",
$username = $user->getName();
- // Try the local user from the slave DB
+ // Try the local user from the replica DB
$localId = User::idFromName( $username );
$flags = User::READ_NORMAL;
return AuthenticationResponse::newAbstain();
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow(
'user',
[
return false;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow(
'user',
[ 'user_newpassword', 'user_newpass_time' ],
}
/**
- * Get the slave connection to the database
+ * Get the replica DB connection to the database
* When non existing, will initialize the connection.
* @return DatabaseBase
*/
protected function getDB() {
if ( !isset( $this->db ) ) {
- $this->db = wfGetDB( DB_SLAVE );
+ $this->db = wfGetDB( DB_REPLICA );
}
return $this->db;
return;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$table = [ 'user', 'user_properties' ];
$fields = [ 'user_name', 'up_value' ];
$conds = [ 'user_name' => $usersToCheck ];
}
// This is similar to LinkHolderArray::replaceInternal
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$table = 'page';
$fields = array_merge(
LinkCache::getSelectFields(),
}
// Some fields heavily used for linking...
- $db = $this->mForUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+ $db = $this->mForUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
$row = $db->selectRow( 'page', self::getSelectFields(),
[ 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ],
$cache->set( $cacheKey, $blob,
// Add part of a day to TTL to avoid all modules expiring at once
$cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
- Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) )
+ Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
);
return $blob;
}
public function updateMessage( $key ) {
$moduleNames = $this->getResourceLoader()->getModulesByMessage( $key );
foreach ( $moduleNames as $moduleName ) {
- // Uses a holdoff to account for database slave lag (for MessageCache)
+ // Uses a holdoff to account for database replica DB lag (for MessageCache)
$this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
}
}
$where[] = 'global cache is expired';
$staleCache = $cache;
} elseif ( $hashVolatile ) {
- # DB results are slave lag prone until the holdoff TTL passes.
+ # DB results are replica DB lag prone until the holdoff TTL passes.
# By then, updates should be reflected in loadFromDBWithLock().
# One thread renerates the cache while others use old values.
$where[] = 'global cache is expired/volatile';
function loadFromDB( $code, $mode = null ) {
global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
- $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_SLAVE );
+ $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA );
$cache = [];
}
// Mark this cache as definitely "latest" (non-volatile) so
- // load() calls do try to refresh the cache with slave data
+ // load() calls do try to refresh the cache with replica DB data
$this->mCache[$code]['LATEST'] = time();
// Update caches if the lock was acquired
// Lookup basic info for users not yet loaded...
if ( count( $usersToQuery ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$table = [ 'user' ];
$conds = [ 'user_id' => $usersToQuery ];
$fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
if ( $this->writesDone && $this->dbw ) {
$db = $this->dbw; // see the changes in finishWrite()
} else {
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
}
$value = $db->selectField(
/**
* T109700 - Default bot flag to true when there is no corresponding RC entry
* This means all changes caused by parser functions & Lua on reparse are marked as bot
- * Also in the case no RC entry could be found due to slave lag
+ * Also in the case no RC entry could be found due to replica DB lag
*/
$bot = 1;
$lastRevId = 0;
public static function newFromConds(
$conds,
$fname = __METHOD__,
- $dbType = DB_SLAVE
+ $dbType = DB_REPLICA
) {
$db = wfGetDB( $dbType );
$row = $db->selectRow( 'recentchanges', self::selectFields(), $conds, $fname );
// Might as well look for rcids and so on.
if ( !$rc_id ) {
- // Info might be out of date, somewhat fractionally, on slave.
+ // Info might be out of date, somewhat fractionally, on replica DB.
// LogEntry/LogPage and WikiPage match rev/log/rc timestamps,
// so use that relation to avoid full table scans.
if ( $log_id ) {
);
}
} elseif ( !$log_id && !$rev_id ) {
- // Info might be out of date, somewhat fractionally, on slave.
+ // Info might be out of date, somewhat fractionally, on replica DB.
$log_id = $dbw->selectField(
'recentchanges',
'rc_logid',
$tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
// Update the summary row.
- // $prevTags can be out of date on slaves, especially when addTags is called consecutively,
+ // $prevTags can be out of date on replica DBs, especially when addTags is called consecutively,
// causing loss of tags added recently in tag_summary table.
$prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
$prevTags = $prevTags ? $prevTags : '';
throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
}
- $fields['ts_tags'] = wfGetDB( DB_SLAVE )->buildGroupConcatField(
+ $fields['ts_tags'] = wfGetDB( DB_REPLICA )->buildGroupConcatField(
',', 'change_tag', 'ct_tag', $join_cond
);
wfMemcKey( 'active-tags' ),
WANObjectCache::TTL_MINUTE * 5,
function ( $oldValue, &$ttl, array &$setOpts ) {
- $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
// Ask extensions which tags they consider active
$extensionActive = [];
wfMemcKey( 'valid-tags-db' ),
WANObjectCache::TTL_MINUTE * 5,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
wfMemcKey( 'valid-tags-hook' ),
WANObjectCache::TTL_MINUTE * 5,
function ( $oldValue, &$ttl, array &$setOpts ) {
- $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
$tags = [];
Hooks::run( 'ListDefinedTags', [ &$tags ] );
wfMemcKey( 'change-tag-statistics' ),
WANObjectCache::TTL_MINUTE * 5,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
- $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+ $dbr = wfGetDB( DB_REPLICA, 'vslow' );
$setOpts += Database::getCacheSetOptions( $dbr );
* have it / want it.
*/
public function getAutoDeleteReason( Title $title, &$hasHistory ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Get the last revision
$rev = Revision::newFromTitle( $title );
$this->extractWikitextParts();
return $this->auxText;
}
+
+ /**
+ * Get the defaultsort property
+ * @return string|null
+ */
+ public function getDefaultSort() {
+ return $this->parserOutput->getProperty( 'defaultsort' );
+ }
}
$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() ) {
* Get an appropriate DB index and options for a query
*
* @param integer $bitfield
- * @return array (DB_MASTER/DB_SLAVE, SELECT options array)
+ * @return array (DB_MASTER/DB_REPLICA, SELECT options array)
*/
public static function getDBOptions( $bitfield ) {
$index = self::hasFlags( $bitfield, IDBAccessObject::READ_LATEST )
? DB_MASTER
- : DB_SLAVE;
+ : DB_REPLICA;
$options = [];
if ( self::hasFlags( $bitfield, IDBAccessObject::READ_EXCLUSIVE ) ) {
* though certain objects may assume READ_LATEST for common use case or legacy reasons.
*
* There are four types of reads:
- * - READ_NORMAL : Potentially cached read of data (e.g. from a slave or stale replica)
+ * - READ_NORMAL : Potentially cached read of data (e.g. from a replica DB or stale replica)
* - READ_LATEST : Up-to-date read as of transaction start (e.g. from master or a quorum read)
* - READ_LOCKING : Up-to-date read as of now, that locks (shared) the records
* - READ_EXCLUSIVE : Up-to-date read as of now, that locks (exclusive) the records
* All record locks persist for the duration of the transaction.
*
* A special constant READ_LATEST_IMMUTABLE can be used for fetching append-only data. Such
- * data is either (a) on a slave and up-to-date or (b) not yet there, but on the master/quorum.
- * Because the data is append-only, it can never be stale on a slave if present.
+ * data is either (a) on a replica DB and up-to-date or (b) not yet there, but on the master/quorum.
+ * Because the data is append-only, it can never be stale on a replica DB if present.
*
* Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
* In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
*/
interface IDBAccessObject {
/** Constants for object loading bitfield flags (higher => higher QoS) */
- /** @var integer Read from a slave/non-quorum */
+ /** @var integer Read from a replica DB/non-quorum */
const READ_NORMAL = 0;
/** @var integer Read from the master/quorum */
const READ_LATEST = 1;
/** @var integer Read from the master/quorum and lock out other writers and locking readers */
const READ_EXCLUSIVE = 7; // READ_LOCKING (3) and "FOR UPDATE" (4)
- /** @var integer Read from a slave/non-quorum immutable data, using the master/quorum on miss */
+ /** @var integer Read from a replica DB or without a quorum, using the master/quorum on miss */
const READ_LATEST_IMMUTABLE = 8;
// Convenience constant for tracking how data was loaded (higher => higher QoS)
* Initialise a LoadBalancer to give it appropriate chronology protection.
*
* If the stash has a previous master position recorded, this will try to
- * make sure that the next query to a slave of that master will see changes up
+ * make sure that the next query to a replica DB of that master will see changes up
* to that position by delaying execution. The delay may timeout and allow stale
- * data if no non-lagged slaves are available.
+ * data if no non-lagged replica DBs are available.
*
* @param LoadBalancer $lb
* @return void
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() );
}
protected $mPassword;
/** @var string */
protected $mDBname;
+ /** @var bool */
+ protected $cliMode;
/** @var BagOStuff APC cache */
protected $srvCache;
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;
*/
private $mTrxTimestamp = null;
/** @var float Lag estimate at the time of BEGIN */
- private $mTrxSlaveLag = null;
+ private $mTrxReplicaLag = null;
/**
* Remembers the function name given for starting the most recent transaction via begin().
* Used to provide additional context for error reporting.
* @param array $params Parameters passed from DatabaseBase::factory()
*/
function __construct( array $params ) {
- global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode;
+ global $wgDBprefix, $wgDBmwschema;
$this->srvCache = ObjectCache::getLocalServerInstance( 'hash' );
$schema = $params['schema'];
$foreign = $params['foreign'];
+ $this->cliMode = isset( $params['cliMode'] )
+ ? $params['cliMode']
+ : ( PHP_SAPI === 'cli' );
+
$this->mFlags = $flags;
if ( $this->mFlags & DBO_DEFAULT ) {
- if ( $wgCommandLineMode ) {
+ if ( $this->cliMode ) {
$this->mFlags &= ~DBO_TRX;
} else {
$this->mFlags |= DBO_TRX;
* @return DatabaseBase|null DatabaseBase subclass or null
*/
final public static function factory( $dbType, $p = [] ) {
+ global $wgCommandLineMode;
+
$canonicalDBTypes = [
'mysql' => [ 'mysqli', 'mysql' ],
'postgres' => [],
$p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null;
}
$p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
+ $p['cliMode'] = $wgCommandLineMode;
return new $class( $p );
} else {
* queries, like inserting a row can take a long time due to row locking. This method
* uses some simple heuristics to discount those cases.
*
- * @param string $sql
+ * @param string $sql A SQL write query
* @param float $runtime Total runtime, including RTT
*/
private function updateTrxWriteQueryTime( $sql, $runtime ) {
- $indicativeOfSlaveRuntime = true;
+ // Whether this is indicative of replica DB runtime (except for RBR or ws_repl)
+ $indicativeOfReplicaRuntime = true;
if ( $runtime > self::SLOW_WRITE_SEC ) {
$verb = $this->getQueryVerb( $sql );
// insert(), upsert(), replace() are fast unless bulky in size or blocked on locks
if ( $verb === 'INSERT' ) {
- $indicativeOfSlaveRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS;
+ $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS;
} elseif ( $verb === 'REPLACE' ) {
- $indicativeOfSlaveRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS / 2;
+ $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS / 2;
}
}
$this->mTrxWriteDuration += $runtime;
$this->mTrxWriteQueryCount += 1;
- if ( $indicativeOfSlaveRuntime ) {
+ if ( $indicativeOfReplicaRuntime ) {
$this->mTrxWriteAdjDuration += $runtime;
$this->mTrxWriteAdjQueryCount += 1;
}
try {
// Handle callbacks in mTrxEndCallbacks
$this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+ $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
return null;
} catch ( Exception $e ) {
// Already logged; move on...
return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
}
+ /**
+ * @param string $field Field or column to cast
+ * @return string
+ * @since 1.28
+ */
+ public function buildStringCast( $field ) {
+ return $field;
+ }
+
public function selectDB( $db ) {
# Stub. Shouldn't cause serious problems if it's not overridden, but
# if your database engine supports a concept similar to MySQL's
$ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
} catch ( Exception $e ) {
if ( $useTrx ) {
- $this->rollback( $fname );
+ $this->rollback( $fname, self::FLUSHING_INTERNAL );
}
throw $e;
}
if ( $useTrx ) {
- $this->commit( $fname, self::TRANSACTION_INTERNAL );
+ $this->commit( $fname, self::FLUSHING_INTERNAL );
}
return $ok;
return $this->query( $sql, $fname );
}
- public function insertSelect( $destTable, $srcTable, $varMap, $conds,
+ public function insertSelect(
+ $destTable, $srcTable, $varMap, $conds,
+ $fname = __METHOD__, $insertOptions = [], $selectOptions = []
+ ) {
+ if ( $this->cliMode ) {
+ // For massive migrations with downtime, we don't want to select everything
+ // into memory and OOM, so do all this native on the server side if possible.
+ return $this->nativeInsertSelect(
+ $destTable,
+ $srcTable,
+ $varMap,
+ $conds,
+ $fname,
+ $insertOptions,
+ $selectOptions
+ );
+ }
+
+ // For web requests, do a locking SELECT and then INSERT. This puts the SELECT burden
+ // on only the master (without needing row-based-replication). It also makes it easy to
+ // know how big the INSERT is going to be.
+ $fields = [];
+ foreach ( $varMap as $dstColumn => $sourceColumnOrSql ) {
+ $fields[] = $this->fieldNameWithAlias( $sourceColumnOrSql, $dstColumn );
+ }
+ $selectOptions[] = 'FOR UPDATE';
+ $res = $this->select( $srcTable, implode( ',', $fields ), $conds, $fname, $selectOptions );
+ if ( !$res ) {
+ return false;
+ }
+
+ $rows = [];
+ foreach ( $res as $row ) {
+ $rows[] = (array)$row;
+ }
+
+ return $this->insert( $destTable, $rows, $fname, $insertOptions );
+ }
+
+ public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds,
$fname = __METHOD__,
$insertOptions = [], $selectOptions = []
) {
$this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
} else {
// If no transaction is active, then make one for this callback
- $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
+ $this->startAtomic( __METHOD__ );
try {
call_user_func( $callback );
- $this->commit( __METHOD__ );
+ $this->endAtomic( __METHOD__ );
} catch ( Exception $e ) {
- $this->rollback( __METHOD__ );
+ $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
throw $e;
}
}
}
+ 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;
}
/**
* @throws Exception
*/
public function runOnTransactionIdleCallbacks( $trigger ) {
- if ( $this->suppressPostCommitCallbacks ) {
+ if ( $this->mTrxEndCallbacksSuppressed ) {
return;
}
// Some callbacks may use startAtomic/endAtomic, so make sure
// their transactions are ended so other callbacks don't fail
if ( $this->trxLevel() ) {
- $this->rollback( __METHOD__ );
+ $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
}
}
}
}
}
+ /**
+ * 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 );
- $this->mTrxAutomatic = true;
// If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
// in all changes being in one transaction to keep requests transactional.
if ( !$this->getFlag( DBO_TRX ) ) {
try {
$res = call_user_func_array( $callback, [ $this, $fname ] );
} catch ( Exception $e ) {
- $this->rollback( $fname );
+ $this->rollback( $fname, self::FLUSHING_INTERNAL );
throw $e;
}
$this->endAtomic( $fname );
// @TODO: make this an exception at some point
$msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
wfLogDBError( $msg );
+ wfWarn( $msg );
return; // join the main transaction set
}
} elseif ( $this->getFlag( DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
// @TODO: make this an exception at some point
- wfLogDBError( "$fname: Implicit transaction expected (DBO_TRX set)." );
+ $msg = "$fname: Implicit transaction expected (DBO_TRX set).";
+ wfLogDBError( $msg );
+ wfWarn( $msg );
return; // let any writes be in the main transaction
}
$this->mTrxTimestamp = microtime( true );
$this->mTrxFname = $fname;
$this->mTrxDoneWrites = false;
- $this->mTrxAutomatic = false;
+ $this->mTrxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
$this->mTrxAutomaticAtomic = false;
$this->mTrxAtomicLevels = [];
$this->mTrxShortId = wfRandomString( 12 );
$this->mTrxWriteAdjQueryCount = 0;
$this->mTrxWriteCallers = [];
// First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
- // Get an estimate of the slave lag before then, treating estimate staleness
+ // Get an estimate of the replica DB lag before then, treating estimate staleness
// as lag itself just to be safe
$status = $this->getApproximateLagStatus();
- $this->mTrxSlaveLag = $status['lag'] + ( microtime( true ) - $status['since'] );
+ $this->mTrxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
}
/**
return; // nothing to do
} elseif ( $this->mTrxAutomatic ) {
// @TODO: make this an exception at some point
- wfLogDBError( "$fname: Explicit commit of implicit transaction." );
+ $msg = "$fname: Explicit commit of implicit transaction.";
+ wfLogDBError( $msg );
+ wfWarn( $msg );
return; // wait for the main transaction set commit round
}
}
}
$this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
+ $this->runTransactionListenerCallbacks( self::TRIGGER_COMMIT );
}
/**
$this->mTrxIdleCallbacks = []; // clear
$this->mTrxPreCommitCallbacks = []; // clear
$this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+ $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
}
/**
}
}
+ 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 );
}
}
/**
- * Get the slave lag when the current transaction started
+ * Get the replica DB lag when the current transaction started
*
* This is useful when transactions might use snapshot isolation
* (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
*/
public function getTransactionLagStatus() {
return $this->mTrxLevel
- ? [ 'lag' => $this->mTrxSlaveLag, 'since' => $this->trxTimestamp() ]
+ ? [ 'lag' => $this->mTrxReplicaLag, 'since' => $this->trxTimestamp() ]
: null;
}
/**
- * Get a slave lag estimate for this server
+ * Get a replica DB lag estimate for this server
*
* @return array ('lag': seconds or false on error, 'since': UNIX timestamp of estimate)
* @since 1.27
*/
public function getApproximateLagStatus() {
return [
- 'lag' => $this->getLBInfo( 'slave' ) ? $this->getLag() : 0,
+ 'lag' => $this->getLBInfo( 'replica' ) ? $this->getLag() : 0,
'since' => microtime( true )
];
}
* @return null|ResultWrapper
* @throws Exception
*/
- public function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+ public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
$insertOptions = [], $selectOptions = []
) {
$this->mScrollableCursor = false;
try {
- $ret = parent::insertSelect(
+ $ret = parent::nativeInsertSelect(
$destTable,
$srcTable,
$varMap,
*/
abstract class DatabaseMysqlBase extends Database {
/** @var MysqlMasterPos */
- protected $lastKnownSlavePos;
- /** @var string Method to detect slave lag */
+ protected $lastKnownReplicaPos;
+ /** @var string Method to detect replica DB lag */
protected $lagDetectionMethod;
- /** @var array Method to detect slave lag */
+ /** @var array Method to detect replica DB lag */
protected $lagDetectionOptions = [];
/** @var bool bool Whether to use GTID methods */
protected $useGTIDs = false;
$key = $cache->makeGlobalKey(
'mysql',
'master-info',
- // Using one key for all cluster slaves is preferable
+ // Using one key for all cluster replica DBs is preferable
$this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
);
if ( $this->getLBInfo( 'is static' ) === true ) {
return 0; // this is a copy of a read-only dataset with no master DB
- } elseif ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) {
+ } elseif ( $this->lastKnownReplicaPos && $this->lastKnownReplicaPos->hasReached( $pos ) ) {
return 0; // already reached this point for sure
}
if ( $status === null ) {
// T126436: jobs programmed to wait on master positions might be referencing binlogs
// with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try
- // to detect this and treat the slave as having reached the position; a proper master
+ // to detect this and treat the replica DB as having reached the position; a proper master
// switchover already requires that the new master be caught up before the switch.
- $slavePos = $this->getSlavePos();
- if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) {
- $this->lastKnownSlavePos = $slavePos;
+ $replicationPos = $this->getSlavePos();
+ if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
+ $this->lastKnownReplicaPos = $replicationPos;
$status = 0;
}
} elseif ( $status >= 0 ) {
// Remember that this position was reached to save queries next time
- $this->lastKnownSlavePos = $pos;
+ $this->lastKnownReplicaPos = $pos;
}
return $status;
return oci_free_statement( $stmt );
}
- function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+ function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
$insertOptions = [], $selectOptions = []
) {
$destTable = $this->tableName( $destTable );
return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
}
+ /**
+ * @param string $field Field or column to cast
+ * @return string
+ * @since 1.28
+ */
+ public function buildStringCast( $field ) {
+ return 'CAST ( ' . $field . ' AS VARCHAR2 )';
+ }
+
public function getSearchEngine() {
return 'SearchOracle';
}
* @param array $selectOptions
* @return bool
*/
- function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+ function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
$insertOptions = [], $selectOptions = [] ) {
$destTable = $this->tableName( $destTable );
return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
}
+ /**
+ * @param string $field Field or column to cast
+ * @return string
+ * @since 1.28
+ */
+ public function buildStringCast( $field ) {
+ return $field . '::text';
+ }
+
public function getSearchEngine() {
return 'SearchPostgres';
}
return parent::buildLike( $params ) . "ESCAPE '\' ";
}
+ /**
+ * @param string $field Field or column to cast
+ * @return string
+ * @since 1.28
+ */
+ public function buildStringCast( $field ) {
+ return 'CAST ( ' . $field . ' AS TEXT )';
+ }
+
/**
* @return string
*/
}
/**
- * An object representing a master or slave position in a replicated setup.
+ * An object representing a master or replica DB position in a replicated setup.
*
* The implementation details of this opaque type are up to the database subclass.
*/
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 */
/**
* 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
*/
public function wasReadOnlyError();
/**
- * Wait for the slave to catch up to a given master position
+ * Wait for the replica DB to catch up to a given master position
*
* @param DBMasterPos $pos
* @param int $timeout The maximum number of seconds to wait for synchronisation
- * @return int|null Zero if the slave was past that position already,
+ * @return int|null Zero if the replica DB was past that position already,
* greater than zero if we waited for some period of time, less than
* zero if it timed out, and null on error
*/
public function masterPosWait( DBMasterPos $pos, $timeout );
/**
- * Get the replication position of this slave
+ * Get the replication position of this replica DB
*
- * @return DBMasterPos|bool False if this is not a slave.
+ * @return DBMasterPos|bool False if this is not a replica DB.
*/
public function getSlavePos();
* 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
* 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
*/
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
*
public function ping( &$rtt = null );
/**
- * Get slave lag. Currently supported only by MySQL.
+ * Get replica DB lag. Currently supported only by MySQL.
*
* Note that this function will generate a fatal error on many
* installations. Most callers should use LoadBalancer::safeGetLag()
public function getLag();
/**
- * Get the slave lag when the current transaction started
+ * Get the replica DB lag when the current transaction started
* or a general lag estimate if not transaction is active
*
* This is useful when transactions might use snapshot isolation
/** @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)
* 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 ] );
}
+ /**
+ * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+ *
+ * @param string $fname Caller name
+ * @since 1.28
+ */
+ public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+ $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+ }
+
/**
* Commit on all connections. Done for two reasons:
* 1. To commit changes to the masters.
- * 2. To release the snapshot on all connections, master and slave.
+ * 2. To release the snapshot on all connections, master and replica DB.
* @param string $fname Caller name
* @param array $options Options map:
* - maxWriteDuration: abort if more than this much time was spent in write queries
* @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
* @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 );
+ } );
}
/**
}
/**
- * Detemine if any lagged slave connection was used
- * @since 1.27
+ * Detemine if any lagged replica DB connection was used
* @return bool
+ * @since 1.28
*/
- public function laggedSlaveUsed() {
+ public function laggedReplicaUsed() {
$ret = false;
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
- $ret = $ret || $lb->laggedSlaveUsed();
+ $ret = $ret || $lb->laggedReplicaUsed();
} );
return $ret;
}
+ /**
+ * @return bool
+ * @since 1.27
+ * @deprecated Since 1.28; use laggedReplicaUsed()
+ */
+ public function laggedSlaveUsed() {
+ return $this->laggedReplicaUsed();
+ }
+
/**
* Determine if any master connection has pending/written changes from this request
* @return bool
}
/**
- * Waits for the slave DBs to catch up to the current master position
+ * Waits for the replica DBs to catch up to the current master position
*
* Use this when updating very large numbers of rows, as in maintenance scripts,
- * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
*
* By default this waits on all DB clusters actually used in this request.
* This makes sense when lag being waiting on is caused by the code that does this check.
'ifWritesSince' => null
];
+ foreach ( $this->replicationWaitCallbacks as $callback ) {
+ $callback();
+ }
+
// Figure out which clusters need to be checked
/** @var LoadBalancer[] $lbs */
$lbs = [];
$masterPositions = array_fill( 0, count( $lbs ), false );
foreach ( $lbs as $i => $lb ) {
if ( $lb->getServerCount() <= 1 ) {
- // Bug 27975 - Don't try to wait for slaves if there are none
+ // Bug 27975 - Don't try to wait for replica DBs if there are none
// Prevents permission error when getting master position
continue;
} elseif ( $opts['ifWritesSince']
if ( $failed ) {
throw new DBReplicationWaitError(
- "Could not wait for slaves to catch up to " .
+ "Could not wait for replica DBs to catch up to " .
implode( ', ', $failed )
);
}
}
+ /**
+ * 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
*
// Write them to the stash
$unsavedPositions = $cp->shutdown();
// If the positions failed to write to the stash, at least wait on local datacenter
- // slaves to catch up before responding. Even if there are several DCs, this increases
+ // replica DBs to catch up before responding. Even if there are several DCs, this increases
// the chance that the user will see their own changes immediately afterwards. As long
// as the sticky DC cookie applies (same domain), this is not even an issue.
$this->forEachLB( function ( LoadBalancer $lb ) use ( $unsavedPositions ) {
} );
}
+ /**
+ * @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
* @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,
'srvCache' => $this->srvCache,
'wanCache' => $this->wanCache
] );
+
+ $this->initLoadBalancer( $lb );
+
+ return $lb;
}
/**
}
$master = false;
} else {
- $serverInfo['slave'] = true;
+ $serverInfo['replica'] = true;
}
if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
$serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
if ( $i == 0 ) {
$server['master'] = true;
} else {
- $server['slave'] = true;
+ $server['replica'] = true;
}
$server += [ 'flags' => DBO_DEFAULT ];
}
}
private function newLoadBalancer( array $servers ) {
- return new LoadBalancer( [
+ $lb = new LoadBalancer( [
'servers' => $servers,
'loadMonitor' => $this->loadMonitorClass,
'readOnlyReason' => $this->readOnlyReason,
'srvCache' => $this->srvCache,
'wanCache' => $this->wanCache
] );
+
+ $this->initLoadBalancer( $lb );
+
+ return $lb;
}
/**
private $mLoads;
/** @var array[] Map of (group => server index => weight) */
private $mGroupLoads;
- /** @var bool Whether to disregard slave lag as a factor in slave selection */
+ /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
private $mAllowLagged;
- /** @var integer Seconds to spend waiting on slave lag to resolve */
+ /** @var integer Seconds to spend waiting on replica DB lag to resolve */
private $mWaitTimeout;
/** @var array LBFactory information */
private $mParentInfo;
private $srvCache;
/** @var WANObjectCache */
private $wanCache;
+ /** @var TransactionProfiler */
+ protected $trxProfiler;
/** @var bool|DatabaseBase Database connection that caused a problem */
private $mErrorConnection;
- /** @var integer The generic (not query grouped) slave index (of $mServers) */
+ /** @var integer The generic (not query grouped) replica DB index (of $mServers) */
private $mReadIndex;
/** @var bool|DBMasterPos False if not set */
private $mWaitForPos;
- /** @var bool Whether the generic reader fell back to a lagged slave */
- private $laggedSlaveMode = false;
- /** @var bool Whether the generic reader fell back to a lagged slave */
- private $slavesDownMode = false;
+ /** @var bool Whether the generic reader fell back to a lagged replica DB */
+ private $laggedReplicaMode = false;
+ /** @var bool Whether the generic reader fell back to a lagged replica DB */
+ private $allReplicasDownMode = false;
/** @var string The last DB selection or connection error */
private $mLastError = 'Unknown error';
/** @var string|bool Reason the LB is read-only or false if not */
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;
/** @var integer Default 'max lag' when unspecified */
const MAX_LAG = 10;
- /** @var integer Max time to wait for a slave to catch up (e.g. ChronologyProtector) */
+ /** @var integer Max time to wait for a replica DB to catch up (e.g. ChronologyProtector) */
const POS_WAIT_TIMEOUT = 10;
/** @var integer Seconds to cache master server read-only status */
const TTL_CACHE_READONLY = 5;
}
}
- # Find out if all the slaves with non-zero load are lagged
+ # Find out if all the replica DBs with non-zero load are lagged
$sum = 0;
foreach ( $loads as $load ) {
$sum += $load;
}
if ( $sum == 0 ) {
- # No appropriate DB servers except maybe the master and some slaves with zero load
+ # No appropriate DB servers except maybe the master and some replica DBs with zero load
# Do NOT use the master
# Instead, this function will return false, triggering read-only mode,
- # and a lagged slave will be used instead.
+ # and a lagged replica DB will be used instead.
return false;
}
}
/**
- * Get the index of the reader connection, which may be a slave
+ * Get the index of the reader connection, which may be a replica DB
* This takes into account load ratios and lag times. It should
* always return a consistent index during a given invocation
*
# Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
$this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
- $laggedSlaveMode = false;
+ $laggedReplicaMode = false;
# No server found yet
$i = false;
- $conn = false;
# First try quickly looking through the available servers for a server that
# meets our criteria
$currentLoads = $nonErrorLoads;
while ( count( $currentLoads ) ) {
- if ( $this->mAllowLagged || $laggedSlaveMode ) {
+ if ( $this->mAllowLagged || $laggedReplicaMode ) {
$i = ArrayUtils::pickRandom( $currentLoads );
} else {
$i = false;
$i = $this->getRandomNonLagged( $currentLoads, $wiki );
}
if ( $i === false && count( $currentLoads ) != 0 ) {
- # All slaves lagged. Switch to read-only mode
- wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
+ # All replica DBs lagged. Switch to read-only mode
+ wfDebugLog( 'replication', "All replica DBs lagged. Switch to read-only mode" );
$i = ArrayUtils::pickRandom( $currentLoads );
- $laggedSlaveMode = true;
+ $laggedReplicaMode = true;
}
}
}
if ( $i !== false ) {
- # Slave connection successful
- # Wait for the session master pos for a short time
+ # Replica DB connection successful.
+ # Wait for the session master pos for a short time.
if ( $this->mWaitForPos && $i > 0 ) {
- if ( !$this->doWait( $i ) ) {
- $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
- }
+ $this->doWait( $i );
}
if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
$this->mReadIndex = $i;
- # Record if the generic reader index is in "lagged slave" mode
- if ( $laggedSlaveMode ) {
- $this->laggedSlaveMode = true;
+ # Record if the generic reader index is in "lagged replica DB" mode
+ if ( $laggedReplicaMode ) {
+ $this->laggedReplicaMode = true;
}
}
$serverName = $this->getServerName( $i );
/**
* Set the master wait position
- * If a DB_SLAVE connection has been opened already, waits
+ * If a DB_REPLICA connection has been opened already, waits
* Otherwise sets a variable telling it to wait if such a connection is opened
* @param DBMasterPos $pos
*/
if ( $i > 0 ) {
if ( !$this->doWait( $i ) ) {
- $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
- $this->laggedSlaveMode = true;
+ $this->laggedReplicaMode = true;
}
}
}
/**
- * Set the master wait position and wait for a "generic" slave to catch up to it
+ * Set the master wait position and wait for a "generic" replica DB to catch up to it
*
* This can be used a faster proxy for waitForAll()
*
$i = $this->mReadIndex;
if ( $i <= 0 ) {
- // Pick a generic slave if there isn't one yet
+ // Pick a generic replica DB if there isn't one yet
$readLoads = $this->mLoads;
- unset( $readLoads[$this->getWriterIndex()] ); // slaves only
+ unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
$readLoads = array_filter( $readLoads ); // with non-zero load
$i = ArrayUtils::pickRandom( $readLoads );
}
}
/**
- * Set the master wait position and wait for ALL slaves to catch up to it
+ * Set the master wait position and wait for ALL replica DBs to catch up to it
* @param DBMasterPos $pos
* @param int $timeout Max seconds to wait; default is mWaitTimeout
* @return bool Success (able to connect and no timeouts reached)
}
/**
- * Wait for a given slave to catch up to the master pos stored in $this
+ * Wait for a given replica DB to catch up to the master pos stored in $this
* @param int $index Server index
* @param bool $open Check the server even if a new connection has to be made
* @param int $timeout Max seconds to wait; default is mWaitTimeout
$knownReachedPos = $this->srvCache->get( $key );
if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
wfDebugLog( 'replication', __METHOD__ .
- ": slave $server known to be caught up (pos >= $knownReachedPos).\n" );
+ ": replica DB $server known to be caught up (pos >= $knownReachedPos).\n" );
return true;
}
}
}
- wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
+ wfDebugLog( 'replication', __METHOD__ . ": Waiting for replica DB $server to catch up...\n" );
$timeout = $timeout ?: $this->mWaitTimeout;
$result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
if ( $result == -1 || is_null( $result ) ) {
- // Timed out waiting for slave, use master instead
+ // Timed out waiting for replica DB, use master instead
$msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
wfDebugLog( 'replication', "$msg\n" );
wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
}
# Operation-based index
- if ( $i == DB_SLAVE ) {
+ if ( $i == DB_REPLICA ) {
$this->mLastError = 'Unknown error'; // reset error string
# Try the general server pool if $groups are unavailable.
$i = in_array( false, $groups, true )
: $this->getReaderIndex( false, $wiki );
# Couldn't find a working server in getReaderIndex()?
if ( $i === false ) {
- $this->mLastError = 'No working slave server: ' . $this->mLastError;
+ $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
return $this->reportConnectionError();
}
$serverIndex = $conn->getLBInfo( 'serverIndex' );
$refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
if ( $serverIndex === null || $refCount === null ) {
- wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
/**
* This can happen in code like:
* foreach ( $dbs as $db ) {
- * $conn = $lb->getConnection( DB_SLAVE, [], $db );
+ * $conn = $lb->getConnection( DB_REPLICA, [], $db );
* ...
* $lb->reuseConnection( $conn );
* }
$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;
}
* @return mixed
*/
public function getMasterPos() {
- # If this entire request was served from a slave without opening a connection to the
- # master (however unlikely that may be), then we can fetch the position from the slave.
+ # If this entire request was served from a replica DB without opening a connection to the
+ # master (however unlikely that may be), then we can fetch the position from the replica DB.
$masterConn = $this->getAnyOpenConnection( 0 );
if ( !$masterConn ) {
$serverCount = count( $this->mServers );
/**
* 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 );
} );
}
* 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;
}
* @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 );
}
}
+ /**
+ * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+ *
+ * @param string $fname Caller name
+ * @since 1.28
+ */
+ public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+ $this->forEachOpenReplicaConnection( function ( DatabaseBase $conn ) {
+ $conn->clearSnapshot( __METHOD__ );
+ } );
+ }
+
/**
* @return bool Whether a master connection is already open
* @since 1.24
/**
* @note This method will trigger a DB connection if not yet done
- *
* @param string|bool $wiki Wiki ID, or false for the current wiki
* @return bool Whether the generic connection for reads is highly "lagged"
*/
- public function getLaggedSlaveMode( $wiki = false ) {
+ public function getLaggedReplicaMode( $wiki = false ) {
// No-op if there is only one DB (also avoids recursion)
- if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
+ if ( !$this->laggedReplicaMode && $this->getServerCount() > 1 ) {
try {
- // See if laggedSlaveMode gets set
- $conn = $this->getConnection( DB_SLAVE, false, $wiki );
+ // See if laggedReplicaMode gets set
+ $conn = $this->getConnection( DB_REPLICA, false, $wiki );
$this->reuseConnection( $conn );
} catch ( DBConnectionError $e ) {
// Avoid expensive re-connect attempts and failures
- $this->slavesDownMode = true;
- $this->laggedSlaveMode = true;
+ $this->allReplicasDownMode = true;
+ $this->laggedReplicaMode = true;
}
}
- return $this->laggedSlaveMode;
+ return $this->laggedReplicaMode;
+ }
+
+ /**
+ * @param bool $wiki
+ * @return bool
+ * @deprecated 1.28; use getLaggedReplicaMode()
+ */
+ public function getLaggedSlaveMode( $wiki = false ) {
+ return $this->getLaggedReplicaMode( $wiki );
}
/**
* @note This method will never cause a new DB connection
* @return bool Whether any generic connection used for reads was highly "lagged"
+ * @since 1.28
+ */
+ public function laggedReplicaUsed() {
+ return $this->laggedReplicaMode;
+ }
+
+ /**
+ * @return bool
* @since 1.27
+ * @deprecated Since 1.28; use laggedReplicaUsed()
*/
public function laggedSlaveUsed() {
- return $this->laggedSlaveMode;
+ return $this->laggedReplicaUsed();
}
/**
public function getReadOnlyReason( $wiki = false, DatabaseBase $conn = null ) {
if ( $this->readOnlyReason !== false ) {
return $this->readOnlyReason;
- } elseif ( $this->getLaggedSlaveMode( $wiki ) ) {
- if ( $this->slavesDownMode ) {
+ } elseif ( $this->getLaggedReplicaMode( $wiki ) ) {
+ if ( $this->allReplicasDownMode ) {
return 'The database has been automatically locked ' .
- 'until the slave database servers become available';
+ 'until the replica database servers become available';
} else {
return 'The database has been automatically locked ' .
- 'while the slave database servers catch up to the master.';
+ 'while the replica database servers catch up to the master.';
}
} elseif ( $this->masterRunningReadOnly( $wiki, $conn ) ) {
return 'The database master is running in read-only mode.';
}
/**
- * Get the hostname and lag time of the most-lagged slave
+ * Call a function with each open replica DB connection object
+ * @param callable $callback
+ * @param array $params
+ * @since 1.28
+ */
+ public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
+ foreach ( $this->mConns as $connsByServer ) {
+ foreach ( $connsByServer as $i => $serverConns ) {
+ if ( $i === $this->getWriterIndex() ) {
+ continue; // skip master
+ }
+ foreach ( $serverConns as $conn ) {
+ $mergedParams = array_merge( [ $conn ], $params );
+ call_user_func_array( $callback, $mergedParams );
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the hostname and lag time of the most-lagged replica DB
*
* This is useful for maintenance scripts that need to throttle their updates.
- * May attempt to open connections to slaves on the default DB. If there is
+ * May attempt to open connections to replica DBs on the default DB. If there is
* no lag, the maximum lag will be reported as -1.
*
* @param bool|string $wiki Wiki ID, or false for the default database
}
/**
- * Wait for a slave DB to reach a specified master position
+ * Wait for a replica DB to reach a specified master position
*
* This will connect to the master to get an accurate position if $pos is not given
*
- * @param IDatabase $conn Slave DB
+ * @param IDatabase $conn Replica DB
* @param DBMasterPos|bool $pos Master position; default: current position
* @param integer $timeout Timeout in seconds
* @return bool Success
* @since 1.27
*/
public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
- if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'slave' ) ) {
- return true; // server is not a slave DB
+ if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'replica' ) ) {
+ return true; // server is not a replica DB
}
$pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
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 );
+ }
+ );
+ }
}
/**
* Abstract base class for update jobs that do something with some secondary
* data extracted from article.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- * a transaction will automatically be wrapped around the update. If need be,
- * subclasses can override the beginTransaction() and commitTransaction() methods.
*/
abstract class DataUpdate implements DeferrableUpdate {
/** @var mixed Result from LBFactory::getEmptyTransactionTicket() */
$this->ticket = $ticket;
}
- /**
- * Begin an appropriate transaction, if any.
- * This default implementation does nothing.
- */
- public function beginTransaction() {
- // noop
- }
-
- /**
- * Commit the transaction started via beginTransaction, if any.
- * This default implementation does nothing.
- */
- public function commitTransaction() {
- // noop
- }
-
- /**
- * Abort / roll back the transaction started via beginTransaction, if any.
- * This default implementation does nothing.
- */
- public function rollbackTransaction() {
- // noop
- }
-
/**
* Convenience method, calls doUpdate() on every DataUpdate in the array.
*
- * This methods supports transactions logic by first calling beginTransaction()
- * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
- * then calling commitTransaction() on each update. If an error occurs,
- * rollbackTransaction() will be called on any update object that had beginTransaction()
- * called but not yet commitTransaction().
- *
- * This allows for limited transactional logic across multiple backends for storing
- * secondary data.
- *
* @param DataUpdate[] $updates A list of DataUpdate instances
* @param string $mode Use "enqueue" to use the job queue when possible [Default: run]
- * @throws Exception|null
+ * @throws Exception
+ * @deprecated Since 1.28 Use DeferredUpdates::execute()
*/
public static function runUpdates( array $updates, $mode = 'run' ) {
- if ( $mode === 'enqueue' ) {
- // When possible, push updates as jobs instead of calling doUpdate()
- $updates = self::enqueueUpdates( $updates );
- }
-
- if ( !count( $updates ) ) {
- return; // nothing to do
- }
-
- $open_transactions = [];
- $exception = null;
-
- try {
- // begin transactions
- foreach ( $updates as $update ) {
- $update->beginTransaction();
- $open_transactions[] = $update;
- }
-
- // do work
- foreach ( $updates as $update ) {
- $update->doUpdate();
- }
-
- // commit transactions
- while ( count( $open_transactions ) > 0 ) {
- $trans = array_pop( $open_transactions );
- $trans->commitTransaction();
- }
- } catch ( Exception $ex ) {
- $exception = $ex;
- wfDebug( "Caught exception, will rethrow after rollback: " .
- $ex->getMessage() . "\n" );
- }
-
- // rollback remaining transactions
- while ( count( $open_transactions ) > 0 ) {
- $trans = array_pop( $open_transactions );
- $trans->rollbackTransaction();
- }
-
- if ( $exception ) {
- throw $exception; // rethrow after cleanup
- }
- }
-
- /**
- * Enqueue jobs for every DataUpdate that support enqueueUpdate()
- * and return the remaining DataUpdate objects (those that do not)
- *
- * @param DataUpdate[] $updates A list of DataUpdate instances
- * @return DataUpdate[]
- * @since 1.27
- */
- protected 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;
+ DeferredUpdates::execute( $updates, $mode, DeferredUpdates::ALL );
}
}
*
* @file
*/
+use MediaWiki\MediaWikiServices;
/**
* Class for managing the deferred updates
* run synchronously. If such an update works via queueing, it will be more likely to complete by
* the time the client makes their next request after this one.
*
- * In CLI mode, updates are only deferred until the current wiki has no DB write transaction
- * active within this request.
+ * In CLI mode, updates run immediately if no DB writes are pending. Otherwise, they run when:
+ * - a) Any waitForReplication() call if no writes are pending on any DB
+ * - b) A commit happens on Maintenance::getDB( DB_MASTER ) if no writes are pending on any DB
+ * - c) EnqueueableDataUpdate tasks may enqueue on commit of Maintenance::getDB( DB_MASTER )
+ * - d) At the completion of Maintenance::execute()
*
* When updates are deferred, they use a FIFO queue (one for pre-send and one for post-send).
*
/** @var DeferrableUpdate[] Updates to be deferred until after request end */
private static $postSendUpdates = [];
- const ALL = 0; // all updates
+ const ALL = 0; // all updates; in web requests, use only after flushing the output buffer
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;
+
+ /** @var array|null Information about the current execute() call or null if not running */
+ private static $executeContext;
+
/**
- * Add an update to the deferred list
+ * Add an update to the deferred list to be run later by execute()
+ *
+ * In CLI mode, callback magic will also be used to run updates when safe
*
* @param DeferrableUpdate $update Some object that implements doUpdate()
- * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+ * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
*/
- public static function addUpdate( DeferrableUpdate $update, $type = self::POSTSEND ) {
- if ( $type === self::PRESEND ) {
+ public static function addUpdate( DeferrableUpdate $update, $stage = self::POSTSEND ) {
+ global $wgCommandLineMode;
+
+ // This is a sub-DeferredUpdate, run it right after its parent update
+ if ( self::$executeContext && self::$executeContext['stage'] >= $stage ) {
+ self::$executeContext['subqueue'][] = $update;
+ return;
+ }
+
+ if ( $stage === self::PRESEND ) {
self::push( self::$preSendUpdates, $update );
} else {
self::push( self::$postSendUpdates, $update );
}
+
+ // Try to run the updates now if in CLI mode and no transaction is active.
+ // This covers scripts that don't/barely use the DB but make updates to other stores.
+ if ( $wgCommandLineMode ) {
+ self::tryOpportunisticExecute( 'run' );
+ }
}
/**
* @see MWCallableUpdate::__construct()
*
* @param callable $callable
- * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+ * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
* @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28)
*/
public static function addCallableUpdate(
- $callable, $type = self::POSTSEND, IDatabase $dbw = null
+ $callable, $stage = self::POSTSEND, IDatabase $dbw = null
) {
- self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $type );
+ self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $stage );
}
/**
* Do any deferred updates and clear the list
*
* @param string $mode Use "enqueue" to use the job queue when possible [Default: "run"]
- * @param integer $type DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
+ * @param integer $stage DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
*/
- public static function doUpdates( $mode = 'run', $type = self::ALL ) {
- if ( $type === self::ALL || $type == self::PRESEND ) {
- self::execute( self::$preSendUpdates, $mode );
+ public static function doUpdates( $mode = 'run', $stage = self::ALL ) {
+ $stageEffective = ( $stage === self::ALL ) ? self::POSTSEND : $stage;
+
+ if ( $stage === self::ALL || $stage === self::PRESEND ) {
+ self::execute( self::$preSendUpdates, $mode, $stageEffective );
}
- if ( $type === self::ALL || $type == self::POSTSEND ) {
- self::execute( self::$postSendUpdates, $mode );
+ if ( $stage === self::ALL || $stage == self::POSTSEND ) {
+ self::execute( self::$postSendUpdates, $mode, $stageEffective );
}
}
+ /**
+ * @param DeferrableUpdate[] $queue
+ * @param DeferrableUpdate $update
+ */
private static function push( array &$queue, DeferrableUpdate $update ) {
- global $wgCommandLineMode;
-
if ( $update instanceof MergeableUpdate ) {
$class = get_class( $update ); // fully-qualified class
if ( isset( $queue[$class] ) ) {
} else {
$queue[] = $update;
}
-
- // CLI scripts may forget to periodically flush these updates,
- // so try to handle that rather than OOMing and losing them entirely.
- // Try to run the updates as soon as there is no current wiki transaction.
- static $waitingOnTrx = false; // de-duplicate callback
- if ( $wgCommandLineMode && !$waitingOnTrx ) {
- $lb = wfGetLB();
- $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
- // Do the update as soon as there is no transaction
- if ( $dbw && $dbw->trxLevel() ) {
- $waitingOnTrx = true;
- $dbw->onTransactionIdle( function() use ( &$waitingOnTrx ) {
- DeferredUpdates::doUpdates();
- $waitingOnTrx = false;
- } );
- } else {
- self::doUpdates();
- }
- }
}
- public static function execute( array &$queue, $mode ) {
- $stats = \MediaWiki\MediaWikiServices::getInstance()->getStatsdDataFactory();
+ /**
+ * @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects
+ * @param string $mode Use "enqueue" to use the job queue when possible
+ * @param integer $stage Class constant (PRESEND, POSTSEND) (since 1.28)
+ * @throws ErrorPageError Happens on top-level calls
+ * @throws Exception Happens on second-level calls
+ */
+ public static function execute( array &$queue, $mode, $stage ) {
+ $services = MediaWikiServices::getInstance();
+ $stats = $services->getStatsdDataFactory();
+ $lbFactory = $services->getDBLoadBalancerFactory();
$method = RequestContext::getMain()->getRequest()->getMethod();
- $updates = $queue; // snapshot of queue
- // Keep doing rounds of updates until none get enqueued
- while ( count( $updates ) ) {
+ /** @var ErrorPageError $reportableError */
+ $reportableError = null;
+ /** @var DeferrableUpdate[] $updates Snapshot of queue */
+ $updates = $queue;
+
+ // Keep doing rounds of updates until none get enqueued...
+ while ( $updates ) {
$queue = []; // clear the queue
- /** @var DataUpdate[] $dataUpdates */
- $dataUpdates = [];
- /** @var DeferrableUpdate[] $otherUpdates */
- $otherUpdates = [];
- foreach ( $updates as $update ) {
- if ( $update instanceof DataUpdate ) {
- $dataUpdates[] = $update;
- } else {
- $otherUpdates[] = $update;
+
+ if ( $mode === 'enqueue' ) {
+ try {
+ // Push enqueuable updates to the job queue and get the rest
+ $updates = self::enqueueUpdates( $updates );
+ } catch ( Exception $e ) {
+ // Let other updates have a chance to run if this failed
+ MWExceptionHandler::rollbackMasterChangesAndLog( $e );
}
+ }
- $name = $update instanceof DeferrableCallback
- ? get_class( $update ) . '-' . $update->getOrigin()
- : get_class( $update );
+ // Order will be DataUpdate followed by generic DeferrableUpdate tasks
+ $updatesByType = [ 'data' => [], 'generic' => [] ];
+ foreach ( $updates as $du ) {
+ $updatesByType[$du instanceof DataUpdate ? 'data' : 'generic'][] = $du;
+ $name = ( $du instanceof DeferrableCallback )
+ ? get_class( $du ) . '-' . $du->getOrigin()
+ : get_class( $du );
$stats->increment( 'deferred_updates.' . $method . '.' . $name );
}
- // Delegate DataUpdate execution to the DataUpdate class
- try {
- DataUpdate::runUpdates( $dataUpdates, $mode );
- } catch ( Exception $e ) {
- // Let the other updates occur if these had to rollback
- MWExceptionHandler::logException( $e );
- }
- // Execute the non-DataUpdate tasks
- foreach ( $otherUpdates as $update ) {
- try {
- $update->doUpdate();
- wfGetLBFactory()->commitMasterChanges( __METHOD__ );
- } catch ( Exception $e ) {
- // We don't want exceptions thrown during deferred updates to
- // be reported to the user since the output is already sent
- if ( !$e instanceof ErrorPageError ) {
- MWExceptionHandler::logException( $e );
+ // Execute all remaining tasks...
+ foreach ( $updatesByType as $updatesForType ) {
+ foreach ( $updatesForType as $update ) {
+ self::$executeContext = [
+ 'update' => $update,
+ 'stage' => $stage,
+ 'subqueue' => []
+ ];
+ /** @var DeferrableUpdate $update */
+ $guiError = self::runUpdate( $update, $lbFactory, $stage );
+ $reportableError = $reportableError ?: $guiError;
+ // Do the subqueue updates for $update until there are none
+ while ( self::$executeContext['subqueue'] ) {
+ $subUpdate = reset( self::$executeContext['subqueue'] );
+ $firstKey = key( self::$executeContext['subqueue'] );
+ unset( self::$executeContext['subqueue'][$firstKey] );
+
+ $guiError = self::runUpdate( $subUpdate, $lbFactory, $stage );
+ $reportableError = $reportableError ?: $guiError;
}
- // Make sure incomplete transactions are not committed and end any
- // open atomic sections so that other DB updates have a chance to run
- wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
+ self::$executeContext = null;
}
}
$updates = $queue; // new snapshot of queue (check for new entries)
}
+
+ if ( $reportableError ) {
+ throw $reportableError; // throw the first of any GUI errors
+ }
+ }
+
+ /**
+ * @param DeferrableUpdate $update
+ * @param LBFactory $lbFactory
+ * @param integer $stage
+ * @return ErrorPageError|null
+ */
+ private static function runUpdate( DeferrableUpdate $update, LBFactory $lbFactory, $stage ) {
+ $guiError = null;
+ try {
+ $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+ $lbFactory->beginMasterChanges( $fnameTrxOwner );
+ $update->doUpdate();
+ $lbFactory->commitMasterChanges( $fnameTrxOwner );
+ } catch ( Exception $e ) {
+ // Reporting GUI exceptions does not work post-send
+ if ( $e instanceof ErrorPageError && $stage === self::PRESEND ) {
+ $guiError = $e;
+ }
+ MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+ }
+
+ return $guiError;
+ }
+
+ /**
+ * 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' ) {
+ // execute() loop is already running
+ if ( self::$executeContext ) {
+ return false;
+ }
+
+ // Avoiding running updates without them having outer scope
+ 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();
+ }
+
+ /**
+ * 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 );
}
/**
self::$preSendUpdates = [];
self::$postSendUpdates = [];
}
+
+ /**
+ * @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;
+ }
}
*
* @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 */
/** @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]
* @throws MWException
*/
function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
- parent::__construct( false ); // no implicit transaction
+ parent::__construct();
$this->page = $page;
if ( $pageId ) {
}
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 );
+
+ if ( $this->ticket ) {
+ // 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->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 ],
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() ]
);
}
}
// 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',
$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',
[
'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 ],
// 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() ]
);
}
}
}
- // Commit and release the lock
+ // Commit and release the lock (if set)
ScopedCallback::consume( $scopedLock );
}
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 = [];
}
}
}
+ 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 ],
* @file
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Class the manages updates of *_link tables as well as similar extension-managed tables
*
*
* 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 */
*/
private $user;
+ /** @var IDatabase */
+ private $db;
+
/**
* Constructor
*
* @throws MWException
*/
function __construct( Title $title, ParserOutput $parserOutput, $recursive = true ) {
- // Implicit transactions are disabled as they interfere with batching
- parent::__construct( false );
+ parent::__construct();
$this->mTitle = $title;
$this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
* @note: this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
*/
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 );
+ if ( $this->ticket ) {
+ // 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->getDB(), $this->mId );
+ }
Hooks::run( 'LinksUpdate', [ &$this ] );
$this->doIncrementalUpdate();
- // Commit and release the lock
+ // Commit and release the lock (if set)
ScopedCallback::consume( $scopedLock );
// Run post-commit hooks without DBO_TRX
- $this->mDb->onTransactionIdle( function() {
+ $this->getDB()->onTransactionIdle( function() {
Hooks::run( 'LinksUpdateComplete', [ &$this ] );
} );
}
* @param array $cats
*/
function invalidateCategories( $cats ) {
- PurgeJobUtils::invalidatePages( $this->mDb, NS_CATEGORY, array_keys( $cats ) );
+ PurgeJobUtils::invalidatePages( $this->getDB(), NS_CATEGORY, array_keys( $cats ) );
}
/**
* @param array $images
*/
function invalidateImageDescriptions( $images ) {
- PurgeJobUtils::invalidatePages( $this->mDb, NS_FILE, array_keys( $images ) );
+ PurgeJobUtils::invalidatePages( $this->getDB(), NS_FILE, array_keys( $images ) );
}
/**
* @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';
foreach ( $deletionBatches as $deletionBatch ) {
$deleteWheres[] = [
$fromField => $this->mId,
- $this->mDb->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
+ $this->getDB()->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
];
}
} else {
}
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() ]
);
}
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,
'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,
* @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] ) ) {
* @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] ) ) {
* @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;
* @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;
* @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;
* @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;
* 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] ) ) {
* @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;
/**
* 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 = [
}
return [
- 'wiki' => $this->mDb->getWikiID(),
+ 'wiki' => $this->getDB()->getWikiID(),
'job' => new JobSpecification(
'refreshLinksPrioritized',
[
*/
public static function cacheUpdate( $dbw ) {
global $wgActiveUserDays;
- $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+ $dbr = wfGetDB( DB_REPLICA, 'vslow' );
# Get non-bot users than did some recent action other than making accounts.
# If account creation is included, the number gets inflated ~20+ fold on enwiki.
$activeUsers = $dbr->selectField(
*/
/**
- * Abstract base class for update jobs that put some secondary data extracted
- * from article content into the database.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- * 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 */
protected $mDb;
-
/** @var array SELECT options to be used (array) */
protected $mOptions = [];
- /** @var bool Whether a transaction is open on this object (internal use only!) */
- private $mHasTransaction;
-
- /** @var bool Whether this update should be wrapped in a transaction */
- protected $mUseTransaction;
-
- /**
- * Constructor
- *
- * @param bool $withTransaction Whether this update should be wrapped in a
- * transaction (default: true). A transaction is only started if no
- * transaction is already in progress, see beginTransaction() for details.
- */
- public function __construct( $withTransaction = true ) {
+ public function __construct() {
parent::__construct();
$this->mDb = wfGetLB()->getLazyConnectionRef( DB_MASTER );
-
- $this->mWithTransaction = $withTransaction;
- $this->mHasTransaction = false;
- }
-
- /**
- * Begin a database transaction, if $withTransaction was given as true in
- * the constructor for this SqlDataUpdate.
- *
- * Because nested transactions are not supported by the Database class,
- * this implementation checks Database::trxLevel() and only opens a
- * transaction if none is already active.
- */
- public function beginTransaction() {
- if ( !$this->mWithTransaction ) {
- return;
- }
-
- // NOTE: nested transactions are not supported, only start a transaction if none is open
- if ( $this->mDb->trxLevel() === 0 ) {
- $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
- $this->mHasTransaction = true;
- }
- }
-
- /**
- * Commit the database transaction started via beginTransaction (if any).
- */
- public function commitTransaction() {
- if ( $this->mHasTransaction ) {
- $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
- $this->mHasTransaction = false;
- }
- }
-
- /**
- * Abort the database transaction started via beginTransaction (if any).
- */
- public function abortTransaction() {
- if ( $this->mHasTransaction ) { // XXX: actually... maybe always?
- $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
- $this->mHasTransaction = false;
- }
}
}
*/
public function deletedLink( $id ) {
if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow( 'archive', '*',
[ 'ar_rev_id' => $id ],
__METHOD__ );
RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
) {
// Look for an unpatrolled change corresponding to this diff
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
$change = RecentChange::newFromConds(
[
'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
}
// Load tags information for both revisions
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $this->mOldid !== false ) {
$this->mOldTags = $dbr->selectField(
'tag_summary',
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* Handler class for MWExceptions
* @param Exception|Throwable $e
*/
public static function rollbackMasterChangesAndLog( $e ) {
- $factory = wfGetLBFactory();
- if ( $factory->hasMasterChanges() ) {
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ if ( $lbFactory->hasMasterChanges() ) {
$logger = LoggerFactory::getInstance( 'Bug56269' );
$logger->warning(
'Exception thrown with an uncommited database transaction: ' .
self::getLogMessage( $e ),
self::getLogContext( $e )
);
- $factory->rollbackMasterChanges( __METHOD__ );
}
+ $lbFactory->rollbackMasterChanges( __METHOD__ );
}
/**
}
/**
- * Get a slave database connection for the specified cluster
+ * Get a replica DB connection for the specified cluster
*
* @param string $cluster Cluster name
* @return IDatabase
wfDebug( "writable external store\n" );
}
- $db = $lb->getConnection( DB_SLAVE, [], $wiki );
+ $db = $lb->getConnection( DB_REPLICA, [], $wiki );
$db->clearFlag( DBO_TRX ); // sanity
return $db;
}
/**
- * Helper function for self::batchFetchBlobs for merging master/slave results
+ * Helper function for self::batchFetchBlobs for merging master/replica DB results
* @param array &$ret Current self::batchFetchBlobs return value
* @param array &$ids Map from blob_id to requested itemIDs
* @param mixed $res DB result from Database::select
*
* a) Create a new file in storage with the contents of a string
* @code
- * array(
+ * [
* 'op' => 'create',
* 'dst' => <storage path>,
* 'content' => <string of new file contents>,
* 'overwrite' => <boolean>,
* 'overwriteSame' => <boolean>,
* 'headers' => <HTTP header name/value map> # since 1.21
- * );
+ * ]
* @endcode
*
* b) Copy a file system file into storage
* @code
- * array(
+ * [
* 'op' => 'store',
* 'src' => <file system path, FSFile, or TempFSFile>,
* 'dst' => <storage path>,
* 'overwrite' => <boolean>,
* 'overwriteSame' => <boolean>,
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* c) Copy a file within storage
* @code
- * array(
+ * [
* 'op' => 'copy',
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'overwriteSame' => <boolean>,
* 'ignoreMissingSource' => <boolean>, # since 1.21
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* d) Move a file within storage
* @code
- * array(
+ * [
* 'op' => 'move',
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'overwriteSame' => <boolean>,
* 'ignoreMissingSource' => <boolean>, # since 1.21
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* e) Delete a file within storage
* @code
- * array(
+ * [
* 'op' => 'delete',
* 'src' => <storage path>,
* 'ignoreMissingSource' => <boolean>
- * )
+ * ]
* @endcode
*
* f) Update metadata for a file within storage
* @code
- * array(
+ * [
* 'op' => 'describe',
* 'src' => <storage path>,
* 'headers' => <HTTP header name/value map>
- * )
+ * ]
* @endcode
*
* g) Do nothing (no-op)
* @code
- * array(
+ * [
* 'op' => 'null',
- * )
+ * ]
* @endcode
*
* Boolean flags for operations (operation-specific):
*
* a) Create a new file in storage with the contents of a string
* @code
- * array(
+ * [
* 'op' => 'create',
* 'dst' => <storage path>,
* 'content' => <string of new file contents>,
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* b) Copy a file system file into storage
* @code
- * array(
+ * [
* 'op' => 'store',
* 'src' => <file system path, FSFile, or TempFSFile>,
* 'dst' => <storage path>,
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* c) Copy a file within storage
* @code
- * array(
+ * [
* 'op' => 'copy',
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'ignoreMissingSource' => <boolean>, # since 1.21
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* d) Move a file within storage
* @code
- * array(
+ * [
* 'op' => 'move',
* 'src' => <storage path>,
* 'dst' => <storage path>,
* 'ignoreMissingSource' => <boolean>, # since 1.21
* 'headers' => <HTTP header name/value map> # since 1.21
- * )
+ * ]
* @endcode
*
* e) Delete a file within storage
* @code
- * array(
+ * [
* 'op' => 'delete',
* 'src' => <storage path>,
* 'ignoreMissingSource' => <boolean>
- * )
+ * ]
* @endcode
*
* f) Update metadata for a file within storage
* @code
- * array(
+ * [
* 'op' => 'describe',
* 'src' => <storage path>,
* 'headers' => <HTTP header name/value map>
- * )
+ * ]
* @endcode
*
* g) Do nothing (no-op)
* @code
- * array(
+ * [
* 'op' => 'null',
- * )
+ * ]
* @endcode
*
* @par Boolean flags for operations (operation-specific):
if ( $path === null ) {
return; // invalid storage path
}
- $age = time() - wfTimestamp( TS_UNIX, $val['mtime'] );
- $ttl = min( 7 * 86400, max( 300, floor( .1 * $age ) ) );
+ $mtime = wfTimestamp( TS_UNIX, $val['mtime'] );
+ $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, .1 );
$key = $this->fileCacheKey( $path );
// Set the cache unless it is currently salted.
$this->memCache->set( $key, $val, $ttl );
* @return array Translated paths in same order
*/
public function getBackendPaths( array $paths, $latest = true ) {
- $db = $this->getDB( $latest ? DB_MASTER : DB_SLAVE );
+ $db = $this->getDB( $latest ? DB_MASTER : DB_REPLICA );
// @TODO: batching
$resolved = [];
* @param array $items An array of titles, or an array of findFile() options with
* the "title" option giving the title. Example:
*
- * $findItem = array( 'title' => $title, 'private' => true );
- * $findBatch = array( $findItem );
+ * $findItem = [ 'title' => $title, 'private' => true ];
+ * $findBatch = [ $findItem ];
* $repo->findFiles( $findBatch );
*
* No title should appear in $items twice, as the result use titles as keys
*
* Example config:
*
- * $wgForeignFileRepos[] = array(
+ * $wgForeignFileRepos[] = [
* 'class' => 'ForeignAPIRepo',
* 'name' => 'shared',
* 'apibase' => 'https://en.wikipedia.org/w/api.php',
* 'fetchDescription' => true, // Optional
* 'descriptionCacheExpiry' => 3600,
- * );
+ * ];
*
* @ingroup FileRepo
*/
* @return IDatabase
*/
function getSlaveDB() {
- return wfGetDB( DB_SLAVE, [], $this->wiki );
+ return wfGetDB( DB_REPLICA, [], $this->wiki );
}
/**
}
/**
- * Get a connection to the slave DB
+ * Get a connection to the replica DB
* @return DatabaseBase
*/
function getSlaveDB() {
- return wfGetDB( DB_SLAVE );
+ return wfGetDB( DB_REPLICA );
}
/**
}
/**
- * Get a callback to get a DB handle given an index (DB_SLAVE/DB_MASTER)
+ * Get a callback to get a DB handle given an index (DB_REPLICA/DB_MASTER)
* @return Closure
*/
protected function getDBFactory() {
if ( !$this->title || $this->title->getNamespace() == NS_FILE ) {
$this->dataLoaded = true; // set it here, to have also true on miss
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow(
'filearchive',
self::selectFields(),
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
*
* @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) */
* @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 );
}
/**
*/
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();
if ( $type == 'text' ) {
return $this->user_text;
- } elseif ( $type == 'id' ) {
+ } else { // id
return (int)$this->user;
}
}
$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 );
* @return FileRepoStatus
*/
public function execute() {
+ /** @var Language */
global $wgLang;
$repo = $this->file->getRepo();
// Update article count statistics (T42009)
// The normal counting logic in WikiPage->doEditUpdates() is designed for
// one-revision-at-a-time editing, not bulk imports. In this situation it
- // suffers from issues of slave lag. We let WikiPage handle the total page
+ // suffers from issues of replica DB lag. We let WikiPage handle the total page
// and revision count, and we implement our own custom logic for the
// article (content page) count.
$page = WikiPage::factory( $title );
"config-mysql-myisam": "MyISAM",
"config-mysql-myisam-dep": "<strong>Warnung:</strong> Es wurde MyISAM als Speichersubsystem für das Datenbanksystem MySQL ausgewählt. Aus folgenden Gründen wird es nicht für den Einsatz mit MediaWiki empfohlen:\n* Es unterstützt aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen.\n* Es ist anfälliger für Datenprobleme.\n* Es wird von MediaWiki nicht immer adäquat unterstützt.\n\nSofern die vorhandene MySQL-Installation das Speichersubsystem InnoDB unterstützt, wird deren Verwendung eindringlich empfohlen.\nSofern sie es nicht unterstützt, sollte nunmehr eine entsprechende Aktualisierung in Erwägung gezogen werden.",
"config-mysql-only-myisam-dep": "<strong>Warnung:</strong> MyISAM ist das einzige verfügbare Speichersubsystem für das Datenbanksystem MySQL auf diesem Server. Es wird nicht für die Verwendung mit MediaWiki empfohlen, da es\n* aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen unterstützt,\n* anfälliger für Datenprobleme ist und\n* von MediaWiki nicht immer adäquat unterstützt wird.\n\nDeine MySQL-Installation unterstützt nicht das Speichersubsystem InnoDB. Eine Aktualisierung wird nunmehr empfohlen.",
- "config-mysql-engine-help": "'''InnoDB''' ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n'''MyISAM''' ist in Einzelnutzerumgebungen sowie bei schreibgeschützten Wikis schneller.\nBei MyISAM-Datenbanken treten tendenziell häufiger Fehler auf als bei InnoDB-Datenbanken.",
+ "config-mysql-engine-help": "<strong>InnoDB</strong> als Speichersubsystem für das Datenbanksystem MySQL ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n<strong>MyISAM</strong> als Speichersubsystem für das Datenbanksystem MySQL ist hingegen in Einzelnutzerumgebungen oder bei schreibgeschützten Wikis schneller.\nDatenbanken, die MyISAM verwenden, sind indes tendenziell fehleranfälliger als solche, die InnoDB verwenden.",
"config-mysql-charset": "Datenbankzeichensatz:",
"config-mysql-binary": "binär",
"config-mysql-utf8": "UTF-8",
"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",
"config-type-sqlite": "SQLite",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
- "config-support-info": "MediaWiki supporte ces systèmes de bases de données :\n\n$1\n\nSi vous ne voyez pas le système de base de données que vous essayez d'utiliser ci-dessous, alors suivez les instructions ci-dessus (voir liens) pour activer le support.",
+ "config-support-info": "MediaWiki prend en charge ces systèmes de bases de données :\n\n$1\n\nSi vous ne voyez pas le système de base de données que vous essayez d’utiliser ci-dessous, alors suivez les instructions ci-dessus (voir liens) pour activer la prise en charge.",
"config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mariadb-url}} MariaDB] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MySQL. ([http://www.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec le support MySQL])",
"config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire et ''open source'' qui peut être une alternative à MySQL. Son support peut contenir quelques bogues mineurs et n'est pas recommandé dans un environnement de production. ([http://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec le support de PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien supporté. ([http://www.php.net/manual/en/pdo.installation.php Comment compiler PHP avec le support de SQLite], en utilisant PDO)",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien pris en charge. ([http://www.php.net/manual/fr/pdo.installation.php Comment compiler PHP avec la prise en charge de SQLite], en utilisant PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] est un système commercial de gestion de base de données d’entreprise. ([http://www.php.net/manual/en/oci8.installation.php Comment compiler PHP avec le support OCI8])",
"config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec le support de SQLSRV])",
"config-header-mysql": "Paramètres de MySQL",
"config-mysql-engine": "Moteur de stockage :",
"config-mysql-innodb": "InnoDB",
"config-mysql-myisam": "MyISAM",
- "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n'est pas recommandé pour une utilisation avec MediaWiki, parce que:\n * il supporte à peine la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le codebase MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL supporte InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne supporte pas les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
- "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il supporte très peu les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne supporte pas InnoDB ; il est peut-être temps de la mettre à jour.",
- "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il supporte bien les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule. \nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d'InnoDB.",
+ "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n’est pas recommandé pour une utilisation avec MediaWiki, parce que :\n * il prend à peine en charge la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le code de base MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL prenden charge InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne prend pas en charge les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
+ "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il prend très peu en charge les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne prend pas en charge InnoDB ; il est peut-être temps de la mettre à jour.",
+ "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il prend bien en charge les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule.\nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d’InnoDB.",
"config-mysql-charset": "Jeu de caractères de la base de données :",
"config-mysql-binary": "Binaire",
"config-mysql-utf8": "UTF-8",
"config-help": "aide",
"config-help-tooltip": "cliquer pour agrandir",
"config-nofile": "Le fichier « $1 » est introuvable. A-t-il été supprimé ?",
- "config-extension-link": "Saviez-vous que votre wiki supporte [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
+ "config-extension-link": "Saviez-vous que votre wiki prend en charge [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
"mainpagetext": "<strong>MediaWiki a été installé.</strong>",
"mainpagedocfooter": "Consultez le [https://meta.wikimedia.org/wiki/Help:Contents/fr Guide de l’utilisateur] pour plus d’informations sur l’utilisation de ce logiciel de wiki.\n\n== Pour démarrer ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste des paramètres de configuration]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/fr Questions courantes sur MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Liste de discussion sur les distributions de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Adaptez MediaWiki dans votre langue]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Apprendre comment lutter contre le pourriel dans votre wiki]"
}
"सरोज कुमार ढकाल",
"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": "विकि भाषाहरू",
"config-subscribe-help": "thông báo an ninh.\nBạn nên đồng ý với nó và cập nhật bản cài đặt MediaWiki của bạn khi phiên bản mới xuất hiện.",
"config-subscribe-noemail": "Bạn đã cố gắng để đăng ký vào danh sách thư thông báo phát hành mà không cung cấp một địa chỉ thư điện tử nào cả.\nVui lòng cung cấp một địa chỉ thư điện tử nếu bạn muốn đăng ký vào danh sách thư.",
"config-pingback": "Chia sẻ dữ liệu về bản cài đặt này với nhóm phát triển MediaWiki.",
+ "config-pingback-help": "Nếu bạn kích hoạt chức năng này, MediaWiki sẽ thỉnh thoảng gửi cho https://www.mediawiki.org/ dữ liệu cơ bản về bản cài đặt MediaWiki này. Dữ liệu này có chẳng hạn loại máy, phiên bản PHP, và phía sau cơ sở dữ liệu đã chọn. Quỹ Wikimedia chia sẻ dữ liệu này với các nhà phát triển MediaWiki để giúp họ trong việc phát triển phần mềm vào tương lai. Dữ liệu sau sẽ được gửi từ hệ thống này:\n<pre>$1</pre>",
"config-almost-done": "Bạn gần như đã hoàn tất!\nBây giờ bạn có thể bỏ qua cấu hình còn lại và cài đặt wiki ngay bây giờ.",
"config-optional-continue": "Hỏi tôi về thêm chi tiết.",
"config-optional-skip": "Chán quá, cài đặt wiki rỗi.",
* and tree storage backends (SQL, CDB, and plain PHP arrays).
*
* All information is loaded on creation when called by $this->fetch( $prefix ).
- * All work is done on slave, because this should *never* change (except during
+ * All work is done on replica DB, because this should *never* change (except during
* schema updates etc, which aren't wiki-related)
*
* @since 1.28
$this->objectCache->makeKey( 'interwiki', $prefix ),
$this->objectCacheExpiry,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix ) {
- $dbr = wfGetDB( DB_SLAVE ); // TODO: inject LoadBalancer
+ $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
$setOpts += Database::getCacheSetOptions( $dbr );
* @return array[] Interwiki rows
*/
private function getAllPrefixesDB( $local ) {
- $db = wfGetDB( DB_SLAVE ); // TODO: inject DB LoadBalancer
+ $db = wfGetDB( DB_REPLICA ); // TODO: inject DB LoadBalancer
$where = [];
}
/**
- * Wait for any slaves or backup servers to catch up.
+ * Wait for any replica DBs or backup servers to catch up.
*
* This does nothing for certain queue classes.
*
}
// Build the full list of job rows to insert
$rows = array_merge( $rowList, array_values( $rowSet ) );
- // Insert the job rows in chunks to avoid slave lag...
+ // Insert the job rows in chunks to avoid replica DB lag...
foreach ( array_chunk( $rows, 50 ) as $rowBatch ) {
$dbw->insert( 'job', $rowBatch, $method );
}
*/
protected function getSlaveDB() {
try {
- return $this->getDB( DB_SLAVE );
+ return $this->getDB( DB_REPLICA );
} catch ( DBConnectionError $e ) {
throw new JobQueueConnectionError( "DBConnectionError:" . $e->getMessage() );
}
}
/**
- * @param int $index (DB_SLAVE/DB_MASTER)
+ * @param int $index (DB_REPLICA/DB_MASTER)
* @return DBConnRef
*/
protected function getDB( $index ) {
}
/**
- * Wait for any slaves or backup queue servers to catch up.
+ * Wait for any replica DBs or backup queue servers to catch up.
*
* This does nothing for certain queue classes.
*
protected $logger;
const MAX_ALLOWED_LAG = 3; // abort if more than this much DB lag is present
- const LAG_CHECK_PERIOD = 1.0; // check slave lag this many seconds
+ const LAG_CHECK_PERIOD = 1.0; // check replica DB lag this many seconds
const ERROR_BACKOFF_TTL = 1; // seconds to back off a queue due to errors
/**
// This check should not block as we want to try other wiki queues.
list( , $maxLag ) = wfGetLB( wfWikiID() )->getMaxLag();
if ( $maxLag >= self::MAX_ALLOWED_LAG ) {
- $response['reached'] = 'slave-lag-limit';
+ $response['reached'] = 'replica-lag-limit';
return $response;
}
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$lbFactory->commitAll( __METHOD__ );
- // Catch huge single updates that lead to slave lag
+ // Catch huge single updates that lead to replica DB lag
$trxProfiler = Profiler::instance()->getTransactionProfiler();
$trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
$trxProfiler->setExpectations( $wgTrxProfilerLimits['JobRunner'], __METHOD__ );
$jobsPopped = 0;
$timeMsTotal = 0;
$startTime = microtime( true ); // time since jobs started running
- $lastCheckTime = 1; // timestamp of last slave check
+ $lastCheckTime = 1; // timestamp of last replica DB check
do {
// Sync the persistent backoffs with concurrent runners
$backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
break;
}
- // Don't let any of the main DB slaves get backed up.
+ // Don't let any of the main DB replica DBs get backed up.
// This only waits for so long before exiting and letting
// other wikis in the farm (on different masters) get a chance.
$timePassed = microtime( true ) - $lastCheckTime;
'timeout' => self::MAX_ALLOWED_LAG
] );
} catch ( DBReplicationWaitError $e ) {
- $response['reached'] = 'slave-lag-limit';
+ $response['reached'] = 'replica-lag-limit';
break;
}
$lastCheckTime = microtime( true );
}
- // Don't let any queue slaves/backups fall behind
+ // Don't let any queue replica DBs/backups fall behind
if ( $jobsPopped > 0 && ( $jobsPopped % 100 ) == 0 ) {
$group->waitForBackups();
}
// Commit all outstanding connections that are in a transaction
// to get a fresh repeatable read snapshot on every connection.
- // Note that jobs are still responsible for handling slave lag.
- $lbFactory->commitAll( __METHOD__ );
+ // Note that jobs are still responsible for handling replica DB lag.
+ $lbFactory->flushReplicaSnapshots( __METHOD__ );
// Clear out title cache data from prior snapshots
LinkCache::singleton()->clear();
$timeMs = intval( ( microtime( true ) - $jobStartTime ) * 1000 );
/**
* Issue a commit on all masters who are currently in a transaction and have
* made changes to the database. It also supports sometimes waiting for the
- * local wiki's slaves to catch up. See the documentation for
+ * local wiki's replica DBs to catch up. See the documentation for
* $wgJobSerialCommitThreshold for more.
*
* @param Job $job
$dbwSerial = false;
}
} else {
- // There are no slaves or writes are all to foreign DB (we don't handle that)
+ // There are no replica DBs or writes are all to foreign DB (we don't handle that)
$dbwSerial = false;
}
// This will trigger a rollback in the main loop
throw new DBError( $dbwSerial, "Timed out waiting on commit queue." );
}
- // Wait for the slave DBs to catch up
+ // Wait for the replica DBs to catch up
$pos = $lb->getMasterPos();
if ( $pos ) {
$lb->waitForAll( $pos );
return false;
}
- $dbr = wfGetDB( DB_SLAVE, [ 'recentchanges' ] );
- // Wait till the slave is caught up so that jobs for this page see each others' changes
+ $dbr = wfGetDB( DB_REPLICA, [ 'recentchanges' ] );
+ // Wait till the replica DB is caught up so that jobs for this page see each others' changes
if ( !wfGetLB()->safeWaitForMasterPos( $dbr ) ) {
- $this->setLastError( "Timed out while waiting for slave to catch up" );
+ $this->setLastError( "Timed out while waiting for replica DB to catch up" );
return false;
}
// Clear any stale REPEATABLE-READ snapshot
* @code
* $ php maintenance/eval.php
* > $queue = JobQueueGroup::singleton();
- * > $job = new NullJob( Title::newMainPage(), array( 'lives' => 10 ) );
+ * > $job = new NullJob( Title::newMainPage(), [ 'lives' => 10 ] );
* > $queue->push( $job );
* @endcode
* You can then confirm the job has been enqueued by using the showJobs.php
);
if ( $rcIds ) {
$dbw->delete( 'recentchanges', [ 'rc_id' => $rcIds ], __METHOD__ );
- // There might be more, so try waiting for slaves
+ // There might be more, so try waiting for replica DBs
try {
$factory->commitAndWaitForReplication(
__METHOD__, $ticket, [ 'timeout' => 3 ]
const PARSE_THRESHOLD_SEC = 1.0;
/** @var integer Lag safety margin when comparing root job times to last-refresh times */
const CLOCK_FUDGE = 10;
- /** @var integer How many seconds to wait for slaves to catch up */
+ /** @var integer How many seconds to wait for replica DBs to catch up */
const LAG_WAIT_TIMEOUT = 15;
function __construct( Title $title, array $params ) {
// Job to update all (or a range of) backlink pages for a page
if ( !empty( $this->params['recursive'] ) ) {
- // When the base job branches, wait for the slaves to catch up to the master.
+ // When the base job branches, wait for the replica DBs to catch up to the master.
// From then on, we know that any template changes at the time the base job was
// enqueued will be reflected in backlink page parses when the leaf jobs run.
if ( !isset( $params['range'] ) ) {
$skewedTimestamp = $this->params['rootJobTimestamp'];
if ( $opportunistic ) {
- // Neither clock skew nor DB snapshot/slave lag matter much for such
+ // Neither clock skew nor DB snapshot/replica DB lag matter much for such
// updates; focus on reusing the (often recently updated) cache
} else {
// For transclusion updates, the template changes must be reflected
* @since 1.26
*/
class SamplingStatsdClient extends StatsdClient {
+ protected $samplingRates = [];
+
+ /**
+ * Sampling rates as an associative array of patterns and rates.
+ * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+ * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+ * @param array $samplingRates
+ * @since 1.28
+ */
+ public function setSamplingRates( array $samplingRates ) {
+ $this->samplingRates = $samplingRates;
+ }
+
/**
* Sets sampling rate for all items in $data.
* The sample rate specified in a StatsdData entity overrides the sample rate specified here.
* {@inheritDoc}
*/
public function appendSampleRate( $data, $sampleRate = 1 ) {
- if ( $sampleRate < 1 ) {
- array_walk( $data, function( $item ) use ( $sampleRate ) {
+ $samplingRates = $this->samplingRates;
+ if ( !$samplingRates && $sampleRate !== 1 ) {
+ $samplingRates = [ '*' => $sampleRate ];
+ }
+ if ( $samplingRates ) {
+ array_walk( $data, function( $item ) use ( $samplingRates ) {
/** @var $item StatsdData */
- if ( $item->getSampleRate() === 1 ) {
- $item->setSampleRate( $sampleRate );
+ foreach ( $samplingRates as $pattern => $rate ) {
+ if ( fnmatch( $pattern, $item->getKey(), FNM_NOESCAPE ) ) {
+ $item->setSampleRate( $item->getSampleRate() * $rate );
+ break;
+ }
}
} );
}
}
// add sampling
- if ( $sampleRate < 1 ) {
- $data = $this->appendSampleRate( $data, $sampleRate );
- }
+ $data = $this->appendSampleRate( $data, $sampleRate );
$data = $this->sampleData( $data );
$data = array_map( 'strval', $data );
--- /dev/null
+<?php
+/**
+ * Wait loop that reaches a condition or times out.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Aaron Schulz
+ */
+
+/**
+ * Wait loop that reaches a condition or times out
+ * @since 1.28
+ */
+class WaitConditionLoop {
+ /** @var callable */
+ private $condition;
+ /** @var callable[] */
+ private $busyCallbacks = [];
+ /** @var float Seconds */
+ private $timeout;
+ /** @var float Seconds */
+ private $lastWaitTime;
+
+ const CONDITION_REACHED = 1;
+ const CONDITION_CONTINUE = 0; // evaluates as falsey
+ const CONDITION_TIMED_OUT = -1;
+ const CONDITION_ABORTED = -2;
+
+ /**
+ * @param callable $condition Callback that returns a WaitConditionLoop::CONDITION_ constant
+ * @param float $timeout Timeout in seconds
+ * @param array &$busyCallbacks List of callbacks to do useful work (by reference)
+ */
+ public function __construct( callable $condition, $timeout = 5.0, &$busyCallbacks = [] ) {
+ $this->condition = $condition;
+ $this->timeout = $timeout;
+ $this->busyCallbacks =& $busyCallbacks;
+ }
+
+ /**
+ * Invoke the loop and continue until either:
+ * - a) The condition callback does not return either CONDITION_CONTINUE or true
+ * - b) The timeout is reached
+ * This a condition callback can return true (stop) or false (continue) for convenience.
+ * In such cases, the halting result of "true" will be converted to CONDITION_REACHED.
+ *
+ * Exceptions in callbacks will be caught and the callback will be swapped with
+ * one that simply rethrows that exception back to the caller when invoked.
+ *
+ * @return integer WaitConditionLoop::CONDITION_* constant
+ * @throws Exception Any error from the condition callback
+ */
+ public function invoke() {
+ $elapsed = 0.0; // seconds
+ $sleepUs = 0; // microseconds to sleep each time
+ $lastCheck = false;
+ $finalResult = self::CONDITION_TIMED_OUT;
+ do {
+ $checkStartTime = $this->getWallTime();
+ // Check if the condition is met yet
+ $realStart = $this->getWallTime();
+ $cpuStart = $this->getCpuTime();
+ $checkResult = call_user_func( $this->condition );
+ $cpu = $this->getCpuTime() - $cpuStart;
+ $real = $this->getWallTime() - $realStart;
+ // Exit if the condition is reached
+ if ( (int)$checkResult !== self::CONDITION_CONTINUE ) {
+ $finalResult = is_int( $checkResult ) ? $checkResult : self::CONDITION_REACHED;
+ break;
+ } elseif ( $lastCheck ) {
+ break; // timeout
+ }
+ // Detect if condition callback seems to block or if justs burns CPU
+ $conditionUsesInterrupts = ( $real > 0.100 && $cpu <= $real * .03 );
+ if ( !$this->popAndRunBusyCallback() && !$conditionUsesInterrupts ) {
+ // 10 queries = 10(10+100)/2 ms = 550ms, 14 queries = 1050ms
+ $sleepUs = min( $sleepUs + 10 * 1e3, 1e6 ); // stop incrementing at ~1s
+ $this->usleep( $sleepUs );
+ }
+ $checkEndTime = $this->getWallTime();
+ // The max() protects against the clock getting set back
+ $elapsed += max( $checkEndTime - $checkStartTime, 0.010 );
+ // Do not let slow callbacks timeout without checking the condition one more time
+ $lastCheck = ( $elapsed >= $this->timeout );
+ } while ( true );
+
+ $this->lastWaitTime = $elapsed;
+
+ return $finalResult;
+ }
+
+ /**
+ * @return float Seconds
+ */
+ public function getLastWaitTime() {
+ return $this->lastWaitTime;
+ }
+
+ /**
+ * @param integer $microseconds
+ */
+ protected function usleep( $microseconds ) {
+ usleep( $microseconds );
+ }
+
+ /**
+ * @return float
+ */
+ protected function getWallTime() {
+ return microtime( true );
+ }
+
+ /**
+ * @return float Returns 0.0 if not supported (Windows on PHP < 7)
+ */
+ protected function getCpuTime() {
+ $time = 0.0;
+
+ if ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
+ $ru = getrusage( 2 /* RUSAGE_THREAD */ );
+ } else {
+ $ru = getrusage( 0 /* RUSAGE_SELF */ );
+ }
+ if ( $ru ) {
+ $time += $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
+ $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
+ }
+
+ return $time;
+ }
+
+ /**
+ * Run one of the callbacks that does work ahead of time for another caller
+ *
+ * @return bool Whether a callback was executed
+ */
+ private function popAndRunBusyCallback() {
+ if ( $this->busyCallbacks ) {
+ reset( $this->busyCallbacks );
+ $key = key( $this->busyCallbacks );
+ /** @var callable $workCallback */
+ $workCallback =& $this->busyCallbacks[$key];
+ try {
+ $workCallback();
+ } catch ( Exception $e ) {
+ $workCallback = function () use ( $e ) {
+ throw $e;
+ };
+ }
+ unset( $this->busyCallbacks[$key] ); // consume
+
+ return true;
+ }
+
+ return false;
+ }
+}
abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
/** @var array[] Lock tracking */
protected $locks = [];
-
/** @var integer ERR_* class constant */
protected $lastError = self::ERR_NONE;
-
/** @var string */
protected $keyspace = 'local';
-
/** @var LoggerInterface */
protected $logger;
-
/** @var callback|null */
protected $asyncHandler;
+ /** @var integer Seconds */
+ protected $syncTimeout;
/** @var bool */
private $debugMode = false;
-
/** @var array */
private $duplicateKeyLookups = [];
-
/** @var bool */
private $reportDupes = false;
-
/** @var bool */
private $dupeTrackScheduled = false;
+ /** @var callable[] */
+ protected $busyCallbacks = [];
+
/** @var integer[] Map of (ATTR_* class constant => QOS_* class constant) */
protected $attrMap = [];
* In CLI mode, it should run the task immediately.
* - reportDupes: Whether to emit warning log messages for all keys that were
* requested more than once (requires an asyncHandler).
+ * - syncTimeout: How long to wait with WRITE_SYNC in seconds.
* @param array $params
*/
public function __construct( array $params = [] ) {
if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
$this->reportDupes = true;
}
+
+ $this->syncTimeout = isset( $params['syncTimeout'] ) ? $params['syncTimeout'] : 3;
}
/**
$this->lastError = $err;
}
+ /**
+ * Let a callback be run to avoid wasting time on special blocking calls
+ *
+ * The callbacks may or may not be called ever, in any particular order.
+ * They are likely to be invoked when something WRITE_SYNC is used used.
+ * They should follow a caching pattern as shown below, so that any code
+ * using the word will get it's result no matter what happens.
+ * @code
+ * $result = null;
+ * $workCallback = function () use ( &$result ) {
+ * if ( !$result ) {
+ * $result = ....
+ * }
+ * return $result;
+ * }
+ * @endcode
+ *
+ * @param callable $workCallback
+ * @since 1.28
+ */
+ public function addBusyCallback( callable $workCallback ) {
+ $this->busyCallbacks[] = $workCallback;
+ }
+
/**
* Modify a cache update operation array for EventRelayer::notify()
*
/**
* A cache class that directs writes to one set of servers and reads to
- * another. This assumes that the servers used for reads are setup to slave
+ * another. This assumes that the servers used for reads are setup to replica DB
* those that writes go to. This can easily be used with redis for example.
*
* In the WAN scenario (e.g. multi-datacenter case), this is useful when
* - writeFactory : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
* This object will be used for writes (e.g. the master DB).
* - readFactory : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
- * This object will be used for reads (e.g. a slave DB).
+ * This object will be used for reads (e.g. a replica DB).
*
* @param array $params
* @throws InvalidArgumentException
*
* Example usage:
* @code
- * $dbr = wfGetDB( DB_SLAVE );
+ * $dbr = wfGetDB( DB_REPLICA );
* $setOpts = Database::getCacheSetOptions( $dbr );
* // Fetch the row from the DB
* $row = $dbr->selectRow( ... );
* @param integer $ttl Seconds to live. Special values are:
* - WANObjectCache::TTL_INDEFINITE: Cache forever
* @param array $opts Options map:
- * - lag : Seconds of slave lag. Typically, this is either the slave lag
- * before the data was read or, if applicable, the slave lag before
+ * - lag : Seconds of replica DB lag. Typically, this is either the replica DB lag
+ * before the data was read or, if applicable, the replica DB lag before
* the snapshot-isolated transaction the data was read from started.
* Default: 0 seconds
* - since : UNIX timestamp of the data in $value. Typically, this is either
* Keys using it via get(), getMulti(), or getWithSetCallback() will
* be invalidated. It is treated as being HOLDOFF_TTL seconds in the future
* by those methods to avoid race conditions where dependent keys get updated
- * with stale values (e.g. from a DB slave).
+ * with stale values (e.g. from a DB replica DB).
*
* This is typically useful for keys with hardcoded names or in some cases
* dynamically generated names where a low number of combinations exist.
* $cache::TTL_MINUTE,
* // Function that derives the new key value
* function ( $oldValue, &$ttl, array &$setOpts ) {
- * $dbr = wfGetDB( DB_SLAVE );
- * // Account for any snapshot/slave lag
+ * $dbr = wfGetDB( DB_REPLICA );
+ * // Account for any snapshot/replica DB lag
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* return $dbr->selectRow( ... );
* $cache::TTL_DAY,
* // Function that derives the new key value
* function ( $oldValue, &$ttl, array &$setOpts ) {
- * $dbr = wfGetDB( DB_SLAVE );
- * // Account for any snapshot/slave lag
+ * $dbr = wfGetDB( DB_REPLICA );
+ * // Account for any snapshot/replica DB lag
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* return CatConfig::newFromRow( $dbr->selectRow( ... ) );
* // Function that derives the new key value
* function ( $oldValue, &$ttl, array &$setOpts ) {
* // Determine new value from the DB
- * $dbr = wfGetDB( DB_SLAVE );
- * // Account for any snapshot/slave lag
+ * $dbr = wfGetDB( DB_REPLICA );
+ * // Account for any snapshot/replica DB lag
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* return CatState::newFromResults( $dbr->select( ... ) );
* 10,
* // Function that derives the new key value
* function ( $oldValue, &$ttl, array &$setOpts ) {
- * $dbr = wfGetDB( DB_SLAVE );
- * // Account for any snapshot/slave lag
+ * $dbr = wfGetDB( DB_REPLICA );
+ * // Account for any snapshot/replica DB lag
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* // Start off with the last cached list
* - pcTTL: Process cache the value in this PHP instance for this many seconds. This avoids
* network I/O when a key is read several times. This will not cache when the callback
* returns false, however. Note that any purges will not be seen while process cached;
- * since the callback should use slave DBs and they may be lagged or have snapshot
+ * since the callback should use replica DBs and they may be lagged or have snapshot
* isolation anyway, this should not typically matter.
* Default: WANObjectCache::TTL_UNCACHEABLE.
* - version: Integer version number. This allows for callers to make breaking changes to
$this->getDateCond( $year, $month );
$this->mTagFilter = $tagFilter;
- $this->mDb = wfGetDB( DB_SLAVE, 'logpager' );
+ $this->mDb = wfGetDB( DB_REPLICA, 'logpager' );
}
public function getDefaultQuery() {
if ( is_null( $usertitle ) ) {
return;
}
+ // Normalize username first so that non-existent users used
+ // in maintenance scripts work
+ $name = $usertitle->getText();
/* Fetch userid at first, if known, provides awesome query plan afterwards */
$userid = User::idFromName( $name );
if ( !$userid ) {
' != ' . LogPage::SUPPRESSED_USER;
}
- $this->performer = $usertitle->getText();
+ $this->performer = $name;
}
/**
* Purpose: Ephemeral global storage.
* Stored centrally within the primary data-center.
* Changes are applied there first and replicated to other DCs (best-effort).
- * To retrieve the latest value (e.g. not from a slave), use BagOStuff::READ_LATEST.
+ * To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST.
* This store may be subject to LRU style evictions.
*
* - ObjectCache::getInstance( $cacheType )
try {
if ( $this->getMasterLinkStatus( $conn ) === 'down' ) {
// If the master cannot be reached, fail-over to the next server.
- // If masters are in data-center A, and slaves in data-center B,
+ // If masters are in data-center A, and replica DBs in data-center B,
// this helps avoid the case were fail-over happens in A but not
// to the corresponding server in B (e.g. read/write mismatch).
continue;
}
/**
- * Check the master link status of a Redis server that is configured as a slave.
+ * Check the master link status of a Redis server that is configured as a replica DB.
* @param RedisConnRef $conn
* @return string|null Master link status (either 'up' or 'down'), or null
- * if the server is not a slave.
+ * if the server is not a replica DB.
*/
protected function getMasterLinkStatus( RedisConnRef $conn ) {
$info = $conn->info();
/** @var string */
protected $tableName = 'objectcache';
/** @var bool */
- protected $slaveOnly = false;
+ protected $replicaOnly = false;
/** @var int */
protected $syncTimeout = 3;
* required to hold the largest shard index. Data will be
* distributed across all tables by key hash. This is for
* MySQL bugs 61735 and 61736.
- * - slaveOnly: Whether to only use slave DBs and avoid triggering
+ * - slaveOnly: Whether to only use replica DBs and avoid triggering
* garbage collection logic of expired items. This only
* makes sense if the primary DB is used and only if get()
* calls will be used. This is used by ReplicatedBagOStuff.
- * - syncTimeout: Max seconds to wait for slaves to catch up for WRITE_SYNC.
+ * - syncTimeout: Max seconds to wait for replica DBs to catch up for WRITE_SYNC.
*
* @param array $params
*/
if ( isset( $params['syncTimeout'] ) ) {
$this->syncTimeout = $params['syncTimeout'];
}
- $this->slaveOnly = !empty( $params['slaveOnly'] );
+ $this->replicaOnly = !empty( $params['slaveOnly'] );
}
protected function getSeparateMainLB() {
$db = DatabaseBase::factory( $type, $info );
$db->clearFlag( DBO_TRX );
} else {
- $index = $this->slaveOnly ? DB_SLAVE : DB_MASTER;
+ $index = $this->replicaOnly ? DB_REPLICA : DB_MASTER;
if ( $this->getSeparateMainLB() ) {
$db = $this->getSeparateMainLB()->getConnection( $index );
$db->clearFlag( DBO_TRX ); // auto-commit mode
public function set( $key, $value, $exptime = 0, $flags = 0 ) {
$ok = $this->setMulti( [ $key => $value ], $exptime );
if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
- $ok = $ok && $this->waitForSlaves();
+ $ok = $this->waitForReplication() && $ok;
}
return $ok;
public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
$ok = $this->mergeViaCas( $key, $callback, $exptime, $attempts );
if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
- $ok = $ok && $this->waitForSlaves();
+ $ok = $this->waitForReplication() && $ok;
}
return $ok;
}
protected function garbageCollect() {
- if ( !$this->purgePeriod || $this->slaveOnly ) {
+ if ( !$this->purgePeriod || $this->replicaOnly ) {
// Disabled
return;
}
return !$this->serverInfos;
}
- protected function waitForSlaves() {
- if ( $this->usesMainDB() ) {
- $lb = $this->getSeparateMainLB()
- ?: MediaWikiServices::getInstance()->getDBLoadBalancer();
- // Return if there are no slaves
- if ( $lb->getServerCount() <= 1 ) {
- return true;
- }
- // Main LB is used; wait for any slaves to catch up
- try {
- $pos = $lb->getMasterPos();
- if ( $pos ) {
- return $lb->waitForAll( $pos, 3 );
- }
- } catch ( DBReplicationWaitError $e ) {
- return false;
- }
+ protected function waitForReplication() {
+ if ( !$this->usesMainDB() ) {
+ // Custom DB server list; probably doesn't use replication
+ return true;
}
- // Custom DB server list; probably doesn't use replication
- return true;
+ $lb = $this->getSeparateMainLB()
+ ?: MediaWikiServices::getInstance()->getDBLoadBalancer();
+
+ if ( $lb->getServerCount() <= 1 ) {
+ return true; // no replica DBs
+ }
+
+ // Main LB is used; wait for any replica DBs to catch up
+ $masterPos = $lb->getMasterPos();
+
+ $loop = new WaitConditionLoop(
+ function () use ( $lb, $masterPos ) {
+ return $lb->waitForAll( $masterPos, 1 );
+ },
+ $this->syncTimeout,
+ $this->busyCallbacks
+ );
+
+ return ( $loop->invoke() === $loop::CONDITION_REACHED );
}
}
return false;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$oldestRevisionTimestamp = $dbr->selectField(
'revision',
'MIN( rev_timestamp )',
if ( !$rc ) {
// Don't cache: This can be hit if the page gets accessed very fast after
- // its creation / latest upload or in case we have high slave lag. In case
+ // its creation / latest upload or in case we have high replica DB lag. In case
// the revision is too old, we will already return above.
return false;
}
// This, as a side-effect, also makes sure that the following query isn't being run for
// pages with a larger history, unless the user has the 'bigdelete' right
// (and is about to delete this page).
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$revisions = $edits = (int)$dbr->selectField(
'revision',
'COUNT(rev_page)',
[
'action' => 'revert',
'oldimage' => $img,
- 'wpEditToken' => $user->getEditToken( $img )
]
);
}
* @return ResultWrapper
*/
protected function queryImageLinks( $target, $limit ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
return $dbr->select(
[ 'imagelinks', 'page' ],
*
* @param int $id Article ID to load
* @param string|int $from One of the following values:
- * - "fromdb" or WikiPage::READ_NORMAL to select from a slave database
+ * - "fromdb" or WikiPage::READ_NORMAL to select from a replica DB
* - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database
*
* @return WikiPage|null
}
$from = self::convertSelectType( $from );
- $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA );
$row = $db->selectRow(
'page', self::selectFields(), [ 'page_id' => $id ], __METHOD__ );
if ( !$row ) {
* @since 1.20
* @param object $row Database row containing at least fields returned by selectFields().
* @param string|int $from Source of $data:
- * - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
+ * - "fromdb" or WikiPage::READ_NORMAL: from a replica DB
* - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
* - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
* @return WikiPage
*
* @param object|string|int $from One of the following:
* - A DB query result object.
- * - "fromdb" or WikiPage::READ_NORMAL to get from a slave DB.
+ * - "fromdb" or WikiPage::READ_NORMAL to get from a replica DB.
* - "fromdbmaster" or WikiPage::READ_LATEST to get from the master DB.
* - "forupdate" or WikiPage::READ_LOCKING to get from the master DB
* using SELECT FOR UPDATE.
$data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
if ( !$data
- && $index == DB_SLAVE
+ && $index == DB_REPLICA
&& wfGetLB()->getServerCount() > 1
&& wfGetLB()->hasOrMadeRecentMasterChanges()
) {
$data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
}
} else {
- // No idea from where the caller got this data, assume slave database.
+ // No idea from where the caller got this data, assume replica DB.
$data = $from;
$from = self::READ_NORMAL;
}
* @since 1.20
* @param object|bool $data DB row containing fields returned by selectFields() or false
* @param string|int $from One of the following:
- * - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
+ * - "fromdb" or WikiPage::READ_NORMAL if the data comes from a replica DB
* - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
* - "forupdate" or WikiPage::READ_LOCKING if the data comes from
* the master DB using SELECT FOR UPDATE
*/
public function getOldestRevision() {
- // Try using the slave database first, then try the master
+ // Try using the replica DB first, then try the master
$continue = 2;
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
$revSelectFields = Revision::selectFields();
$row = null;
$flags = Revision::READ_LOCKING;
} elseif ( $this->mDataLoadedFrom == self::READ_LATEST ) {
// Bug T93976: if page_latest was loaded from the master, fetch the
- // revision from there as well, as it may not exist yet on a slave DB.
+ // revision from there as well, as it may not exist yet on a replica DB.
// Also, this keeps the queries in the same REPEATABLE-READ snapshot.
$flags = Revision::READ_LATEST;
} else {
// links.
$hasLinks = (bool)count( $editInfo->output->getLinks() );
} else {
- $hasLinks = (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
+ $hasLinks = (bool)wfGetDB( DB_REPLICA )->selectField( 'pagelinks', 1,
[ 'pl_from' => $this->getId() ], __METHOD__ );
}
}
}
// Query the redirect table
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow( 'redirect',
[ 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ],
[ 'rd_from' => $this->getId() ],
public function getContributors() {
// @todo FIXME: This is expensive; cache this info somewhere.
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $dbr->implicitGroupby() ) {
$realNameField = 'user_real_name';
$baseRevId = null;
if ( $edittime && $sectionId !== 'new' ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
// Try the master if this thread may have just added it.
// This could be abstracted into a Revision method, but we don't want
// We get here if vary-revision is set. This means that this page references
// itself (such as via self-transclusion). In this case, we need to make sure
// that any such self-references refer to the newly-saved revision, and not
- // to the previous one, which could otherwise happen due to slave lag.
+ // to the previous one, which could otherwise happen due to replica DB lag.
$oldCallback = $edit->popts->getCurrentRevisionCallback();
$edit->popts->setCurrentRevisionCallback(
function ( Title $title, $parser = false ) use ( $revision, &$oldCallback ) {
$protectDescription = '';
foreach ( array_filter( $limit ) as $action => $restrictions ) {
- # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
+ # $action is one of $wgRestrictionTypes = [ 'create', 'edit', 'move', 'upload' ].
# All possible message keys are listed here for easier grepping:
# * restriction-create
# * restriction-edit
# * restriction-move
# * restriction-upload
$actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
- # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
+ # $restrictions is one of $wgRestrictionLevels = [ '', 'autoconfirmed', 'sysop' ],
# with '' filtered out. All possible message keys are listed below:
# * protect-level-autoconfirmed
# * protect-level-sysop
$bitfield |= Revision::DELETED_COMMENT;
$bitfield |= Revision::DELETED_USER;
$bitfield |= Revision::DELETED_RESTRICTED;
+ $deletionFields = [ $dbw->addQuotes( $bitfield ) . ' AS deleted' ];
} else {
- $bitfield = 'rev_deleted';
+ $deletionFields = [ 'rev_deleted AS deleted' ];
}
// For now, shunt the revision data into the archive table.
// the rev_deleted field, which is reserved for this purpose.
// Get all of the page revisions
+ $fields = array_diff( Revision::selectFields(), [ 'rev_deleted' ] );
$res = $dbw->select(
'revision',
- Revision::selectFields(),
+ array_merge( $fields, $deletionFields ),
[ 'rev_page' => $id ],
__METHOD__,
'FOR UPDATE'
'ar_flags' => '',
'ar_len' => $row->rev_len,
'ar_page_id' => $id,
- 'ar_deleted' => $bitfield,
+ 'ar_deleted' => $row->deleted,
'ar_sha1' => $row->rev_sha1,
];
if ( $wgContentHandlerUseDB ) {
if ( $title->getNamespace() == NS_CATEGORY ) {
// Load the Category object, which will schedule a job to create
- // the category table row if necessary. Checking a slave is ok
+ // the category table row if necessary. Checking a replica DB is ok
// here, in the worst case it'll run an unnecessary recount job on
// a category that probably doesn't have many members.
Category::newFromTitle( $title )->getID();
return TitleArray::newFromResult( new FakeResultWrapper( [] ) );
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select( 'categorylinks',
[ 'cl_to AS page_title, ' . NS_CATEGORY . ' AS page_namespace' ],
// Have to do that since DatabaseBase::fieldNamesWithAlias treats numeric indexes
return [];
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select( [ 'categorylinks', 'page_props', 'page' ],
[ 'cl_to' ],
[ 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
}
$this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
- # Let the subclass set the DB here; otherwise use a slave DB for the current wiki
- $this->mDb = $this->mDb ?: wfGetDB( DB_SLAVE );
+ # Let the subclass set the DB here; otherwise use a replica DB for the current wiki
+ $this->mDb = $this->mDb ?: wfGetDB( DB_REPLICA );
$index = $this->getIndexField(); // column to sort on
$extraSort = $this->getExtraSortFields(); // extra columns to sort on for query planning
$output = $this->parent->getOutput();
$linkRenderer = $this->parent->getLinkRenderer();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
# Sort by namespace
ksort( $this->internals );
if ( !$linkBatch->isEmpty() ) {
// construct query
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$fields = array_merge(
LinkCache::getSelectFields(),
[ 'page_namespace', 'page_title' ]
* @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
) {
/**
}
}
+ # 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,
$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() ) ) {
* @since 1.24
* @param Title $title
* @param Parser|bool $parser
- * @return Revision
+ * @return Revision|bool False if missing
*/
- public static function statelessFetchRevision( $title, $parser = false ) {
- return Revision::newFromTitle( $title );
+ public static function statelessFetchRevision( Title $title, $parser = false ) {
+ $pageId = $title->getArticleID();
+ $revId = $title->getLatestRevID();
+
+ $rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $pageId, $revId );
+ if ( $rev ) {
+ $rev->setTitle( $title );
+ }
+
+ return $rev;
}
/**
*/
public function fetchScaryTemplateMaybeFromCache( $url ) {
global $wgTranscludeCacheExpiry;
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$tsCond = $dbr->timestamp( time() - $wgTranscludeCacheExpiry );
$obj = $dbr->selectRow( 'transcache', [ 'tc_time', 'tc_contents' ],
[ 'tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ] );
/** @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 = ''
) {
}
/**
- * 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 ) ),
// Or else Database*::select() will explode, plus it's cheaper!
return;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$skin = $context->getSkin();
$lang = $context->getLanguage();
*
* @since 1.26
* @param ResourceLoaderContext $context
- * @param array $modules List of ResourceLoaderModule objects
+ * @param string[] $modules List of known module names
* @return string Hash
*/
- public function getCombinedVersion( ResourceLoaderContext $context, array $modules ) {
- if ( !$modules ) {
+ public function getCombinedVersion( ResourceLoaderContext $context, array $moduleNames ) {
+ if ( !$moduleNames ) {
return '';
}
$hashes = array_map( function ( $module ) use ( $context ) {
return $this->getModule( $module )->getVersionHash( $context );
- }, $modules );
- return self::makeHash( implode( $hashes ) );
+ }, $moduleNames );
+ return self::makeHash( implode( '', $hashes ) );
+ }
+
+ /**
+ * Get the expected value of the 'version' query parameter.
+ *
+ * This is used by respond() to set a short Cache-Control header for requests with
+ * information newer than the current server has. This avoids pollution of edge caches.
+ * Typically during deployment. (T117587)
+ *
+ * This MUST match return value of `mw.loader#getCombinedVersion()` client-side.
+ *
+ * @since 1.28
+ * @param ResourceLoaderContext $context
+ * @param string[] $modules List of module names
+ * @return string Hash
+ */
+ public function getExpectedVersionQuery( ResourceLoaderContext $context ) {
+ // As of MediaWiki 1.28, the server and client use the same algorithm for combining
+ // version hashes. There is no technical reason for this to be same, and for years the
+ // implementations differed. If getCombinedVersion in PHP (used for StartupModule and
+ // E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
+ // query parameter), then this method must continue to match the JS one.
+ $moduleNames = [];
+ foreach ( $context->getModules() as $name ) {
+ if ( !$this->getModule( $name ) ) {
+ // If a versioned request contains a missing module, the version is a mismatch
+ // as the client considered a module (and version) we don't have.
+ return '';
+ }
+ $moduleNames[] = $name;
+ }
+ return $this->getCombinedVersion( $context, $moduleNames );
}
/**
*/
protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
$rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
- // If a version wasn't specified we need a shorter expiry time for updates
- // to propagate to clients quickly
- // If there were errors, we also need a shorter expiry time so we can recover quickly
- if ( is_null( $context->getVersion() ) || $errors ) {
+ // Use a short cache expiry so that updates propagate to clients quickly, if:
+ // - No version specified (shared resources, e.g. stylesheets)
+ // - There were errors (recover quickly)
+ // - Version mismatch (T117587, T47877)
+ if ( is_null( $context->getVersion() )
+ || $errors
+ || $context->getVersion() !== $this->getExpectedVersionQuery( $context )
+ ) {
$maxage = $rlMaxage['unversioned']['client'];
$smaxage = $rlMaxage['unversioned']['server'];
// If a version was specified we can use a longer expiry time since changing
$good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
if ( !$good ) {
try { // RL always hits the DB on file cache miss...
- wfGetDB( DB_SLAVE );
+ wfGetDB( DB_REPLICA );
} catch ( DBConnectionError $e ) { // ...check if we need to fallback to cache
$good = $fileCache->isCacheGood(); // cache existence check
}
// Try in-object cache first
if ( !isset( $this->fileDeps[$vary] ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$deps = $dbr->selectField( 'module_deps',
'md_deps',
[
/**
* Get the Database object used in getTitleInfo().
*
- * Defaults to the local slave DB. Subclasses may want to override this to return a foreign
+ * Defaults to the local replica DB. Subclasses may want to override this to return a foreign
* database object, or null if getTitleInfo() shouldn't access the database.
*
* NOTE: This ONLY works for getTitleInfo() and isKnownEmpty(), NOT FOR ANYTHING ELSE.
* @return IDatabase|null
*/
protected function getDB() {
- return wfGetDB( DB_SLAVE );
+ return wfGetDB( DB_REPLICA );
}
/**
}
public static function suggestTarget( $target, array $ids ) {
- $result = wfGetDB( DB_SLAVE )->select( 'logging',
+ $result = wfGetDB( DB_REPLICA )->select( 'logging',
'log_type',
[ 'log_id' => $ids ],
__METHOD__,
* @return bool|mixed
*/
public static function checkRevisionExistence( $title, $revid ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$exists = $dbr->selectField( 'revision', '1',
[ 'rev_id' => $revid ], __METHOD__ );
if ( $db ) {
$this->db = $db;
} else {
- $this->db = wfGetDB( DB_SLAVE );
+ $this->db = wfGetDB( DB_REPLICA );
}
}
} elseif ( $configType !== null ) {
$class = $configType;
} else {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$class = $dbr->getSearchEngine();
}
* Do not index this field, just store it.
*/
const FLAG_NO_INDEX = 8;
+
/**
* Get mapping for specific search engine
* @param SearchEngine $engine
if ( is_null( self::$mMinSearchLength ) ) {
$sql = "SHOW GLOBAL VARIABLES LIKE 'ft\\_min\\_word\\_len'";
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->query( $sql, __METHOD__ );
$row = $result->fetchObject();
$result->free();
protected function loadSites() {
$this->sites = new SiteList();
- $dbr = $this->dbLoadBalancer->getConnection( DB_SLAVE );
+ $dbr = $this->dbLoadBalancer->getConnection( DB_REPLICA );
$res = $dbr->select(
'sites',
$s = '';
}
- if ( wfGetLB()->getLaggedSlaveMode() ) {
+ if ( wfGetLB()->getLaggedReplicaMode() ) {
$s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
}
$out = $this->getOutput();
$this->initPage( $out );
- $tpl = $this->prepareQuickTemplate( $out );
+ $tpl = $this->prepareQuickTemplate();
// execute template
$res = $tpl->execute();
* @return IDatabase
*/
protected function getDB() {
- return wfGetDB( DB_SLAVE );
+ return wfGetDB( DB_REPLICA );
}
/**
* @return IDatabase
*/
function getRecacheDB() {
- return wfGetDB( DB_SLAVE, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
+ return wfGetDB( DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
}
/**
* @since 1.18
*/
public function fetchFromCache( $limit, $offset = false ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$options = [];
if ( $limit !== false ) {
$options['LIMIT'] = intval( $limit );
public function getCachedTimestamp() {
if ( is_null( $this->cachedTimestamp ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$fname = get_class( $this ) . '::getCachedTimestamp';
$this->cachedTimestamp = $dbr->selectField( 'querycache_info', 'qci_timestamp',
[ 'qci_type' => $this->getName() ], $fname );
// Mention the level of cache staleness...
$cacheText = '';
- $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
+ $dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
$rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
if ( $rcMax ) {
$cTime = $dbr->selectField( 'querycache_info',
list( $namespace, $fromKey, $from ) = $fromList;
list( , $toKey, $to ) = $toList;
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$filterConds = [ 'page_namespace' => $namespace ];
if ( $hideredirects ) {
$filterConds['page_is_redirect'] = 0;
case Block::TYPE_IP:
case Block::TYPE_RANGE:
list( $start, $end ) = IP::parseRange( $target );
- $conds[] = wfGetDB( DB_SLAVE )->makeList(
+ $conds[] = wfGetDB( DB_REPLICA )->makeList(
[
'ipb_address' => $target,
Block::getRangeCond( $start, $end )
} else {
$linkRenderer = $this->getLinkRenderer();
- $dbr = BotPassword::getDB( DB_SLAVE );
+ $dbr = BotPassword::getDB( DB_REPLICA );
$res = $dbr->select(
'bot_passwords',
[ 'bp_app_id' ],
}
public function getQueryInfo() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
return [
'tables' => [
if ( !$pager->getNumRows() ) {
$out->addWikiMsg( 'nocontribs', $target );
} else {
- # Show a message about slave lag, if applicable
+ # Show a message about replica DB lag, if applicable
$lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
if ( $lag > 0 ) {
$out->showLagWarning( $lag );
return;
}
- # Show a message about slave lag, if applicable
+ # Show a message about replica DB lag, if applicable
$lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
if ( $lag > 0 ) {
$out->showLagWarning( $lag );
function reallyGetQueryInfo( $namespace = null, $title = null ) {
$limitToTitle = !( $namespace === null && $title === null );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$retval = [
'tables' => [
'ra' => 'redirect',
// get a little more detail about each individual entry quickly
// using the filter of reallyGetQueryInfo.
if ( $result && !isset( $result->nsb ) ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$qi = $this->reallyGetQueryInfo(
$result->namespace,
$result->title
// 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 );
}
/* Ok, let's get to it... */
if ( $history == WikiExporter::CURRENT ) {
$lb = false;
- $db = wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_REPLICA );
$buffer = WikiExporter::BUFFER;
} else {
// Use an unbuffered query; histories may be very long!
$lb = wfGetLBFactory()->newMainLB();
- $db = $lb->getConnection( DB_SLAVE );
+ $db = $lb->getConnection( DB_REPLICA );
$buffer = WikiExporter::STREAM;
// This might take a while... :D
$name = $title->getDBkey();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
[ 'page', 'categorylinks' ],
[ 'page_namespace', 'page_title' ],
$maxPages = $this->getConfig()->get( 'ExportPagelistLimit' );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'page',
[ 'page_namespace', 'page_title' ],
* @return array
*/
private function getLinks( $inputPages, $pageSet, $table, $fields, $join ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
foreach ( $inputPages as $page ) {
$title = Title::newFromText( $page );
*/
static function mungeQuery( $query, $prot ) {
$field = 'el_index';
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $query === '*' && $prot !== '' ) {
// Allow queries like 'ftp://*' to find all ftp links
}
public function getQueryInfo() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// strip everything past first wildcard, so that
// index-based-only lookup would be done
list( $this->mungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
* Special:BrokenRedirects also rely on this.
*/
public function getQueryInfo() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$fakeTitle = $dbr->buildConcat( [
'img_media_type',
$dbr->addQuotes( ';' ),
( $oldTalk->exists()
|| ( $oldTitleTalkSubpages && $canMoveSubpage ) );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $this->getConfig()->get( 'FixDoubleRedirects' ) ) {
$hasRedirects = $dbr->selectField( 'redirect', '1',
[
$out->addWikiMsg( 'movesubpagetext', $this->getLanguage()->formatNum( $count ) );
$out->addHTML( "<ul>\n" );
+ $linkBatch = new LinkBatch( $subpages );
+ $linkBatch->setCaller( __METHOD__ );
+ $linkBatch->execute();
+
$linkRenderer = $this->getLinkRenderer();
foreach ( $subpages as $subpage ) {
$link = $linkRenderer->makeLink( $subpage );
$opts['OFFSET'] = $offset;
}
- $res = wfGetDB( DB_SLAVE )->select(
+ $res = wfGetDB( DB_REPLICA )->select(
'page_props',
'pp_propname',
'',
# ## @todo FIXME: Should complain if $fromNs != $namespace
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds = [
'page_namespace' => $namespace,
'join_conds' => [
'log_search' => [
'LEFT JOIN', [
- 'ls_field' => 'pr_id', 'ls_value = pr_id'
+ 'ls_field' => 'pr_id', 'ls_value = ' . $this->mDb->buildStringCast( 'pr_id' )
]
],
'logging' => [
]
];
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$minClTime = $this->getTimestampOffset( $rand );
if ( $minClTime ) {
$qi['conds'][] = 'cl_timestamp ' . $op . ' ' .
* @throws MWException If category has no entries.
*/
protected function getMinAndMaxForCat( Title $category ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->selectRow(
'categorylinks',
[
* @return array Info for the title selected.
*/
private function selectRandomPageFromDB( $rand, $offset, $up, $fname = __METHOD__ ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$query = $this->getQueryInfo( $rand, $offset, $up );
$res = $dbr->select(
}
private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$query = $this->getQueryInfo( $randstr );
$res = $dbr->select(
public function __construct() {
parent::__construct( 'Randomrootpage' );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->extra[] = 'page_title NOT ' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
}
}
protected function getDB() {
- return wfGetDB( DB_SLAVE, 'recentchanges' );
+ return wfGetDB( DB_REPLICA, 'recentchanges' );
}
public function outputFeedLinks() {
* expects only one result set so we use UNION instead.
*/
- $dbr = wfGetDB( DB_SLAVE, 'recentchangeslinked' );
+ $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
$id = $title->getArticleID();
$ns = $title->getNamespace();
$dbkey = $title->getDBkey();
'log_user_text',
];
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Gets the nested SQL statement which
// returns timestamp of the log with the given log ID
// continuing with this, as the user is just going to end up getting sent
// somewhere else. Additionally, if we keep going here, we end up
// populating the memcache of tag data (see ChangeTags::listDefinedTags)
- // with out-of-date data from the slave, because the slave hasn't caught
+ // with out-of-date data from the replica DB, because the replica DB hasn't caught
// up to the fact that a new tag has been created as part of an implicit,
// as yet uncommitted transaction on master.
if ( $out->getRedirect() !== '' ) {
* @return ResultWrapper
*/
public static function listAllPages() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
return self::listPages( $dbr, '' );
}
* @return ResultWrapper
*/
public static function listPagesByPrefix( $prefix ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$title = Title::newFromText( $prefix );
if ( $title ) {
* @return ResultWrapper
*/
function listRevisions() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$tables = [ 'archive' ];
return null;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
return $dbr->select(
'filearchive',
ArchivedFile::selectFields(),
* @return Revision|null
*/
function getRevision( $timestamp ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$fields = [
'ar_rev_id',
* @return Revision|null Null when there is no previous revision
*/
function getPreviousRevision( $timestamp ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Check the previous deleted revision...
$row = $dbr->selectRow( 'archive',
}
// New-style: keyed to the text storage backend.
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$text = $dbr->selectRow( 'text',
[ 'old_text', 'old_flags' ],
[ 'old_id' => $row->ar_text_id ],
* @return string|null
*/
function getLastRevisionText() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$row = $dbr->selectRow( 'archive',
[ 'ar_text', 'ar_flags', 'ar_text_id' ],
[ 'ar_namespace' => $this->title->getNamespace(),
* @return bool
*/
function isDeleted() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
[ 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ],
$minor = $rev->isMinor() ? ChangesList::flag( 'minor' ) : '';
- $tags = wfGetDB( DB_SLAVE )->selectField(
+ $tags = wfGetDB( DB_REPLICA )->selectField(
'tag_summary',
'ts_tags',
[ 'ts_rev_id' => $rev->getId() ],
* Scale a file (probably with a locally installed imagemagick, or similar)
* and output it to STDOUT.
* @param File $file
- * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+ * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
* @param int $flags Scaling flags ( see File:: constants )
* @throws MWException|UploadStashFileNotFoundException
* @return bool Success
* client to cache it forever.
*
* @param File $file
- * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+ * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
* @param int $flags Scaling flags ( see File:: constants )
* @throws MWException
* @return bool Success
* @return string
*/
public static function softwareInformation() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Put the software in an array of form 'name' => 'version'. All messages should
// be loaded here, so feel free to use wfMessage in the 'name'. Raw HTML or
* @return IDatabase
*/
protected function getDB() {
- return wfGetDB( DB_SLAVE, 'watchlist' );
+ return wfGetDB( DB_REPLICA, 'watchlist' );
}
/**
$user = $this->getUser();
$output = $this->getOutput();
- # Show a message about slave lag, if applicable
+ # Show a message about replica DB lag, if applicable
$lag = wfGetLB()->safeGetLag( $dbr );
if ( $lag > 0 ) {
$output->showLagWarning( $lag );
*/
function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
$out = $this->getOutput();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$hidelinks = $this->opts->getValue( 'hidelinks' );
$hideredirs = $this->opts->getValue( 'hideredirs' );
'join_conds' => [ 'langlinks' => [ 'LEFT JOIN', 'll_from = page_id' ] ]
];
if ( $this->prefix ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() );
}
/**
* Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
- * Returns array( 'pages' => ..., 'talks' => ... ), where the subarrays have
+ * Returns [ 'pages' => ..., 'talks' => ... ], where the subarrays have
* an entry for each existing page, with the key being the message name and
* value arbitrary.
*
public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) {
// FIXME: This function should be moved to Language:: or something.
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select( 'page',
[ 'page_namespace', 'page_title' ],
[ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ],
$month = isset( $options['month'] ) ? $options['month'] : false;
$this->getDateCond( $year, $month );
- // Most of this code will use the 'contributions' group DB, which can map to slaves
+ // Most of this code will use the 'contributions' group DB, which can map to replica DBs
// with extra user based indexes or partioning by user. The additional metadata
- // queries should use a regular slave since the lookup pattern is not all by user.
- $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random slave
- $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+ // queries should use a regular replica DB since the lookup pattern is not all by user.
+ $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random replica DB
+ $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
}
function getDefaultQuery() {
}
$this->target = $target;
$this->namespace = $namespace;
- $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+ $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
}
function getDefaultQuery() {
$nt = Title::newFromText( $this->mSearch );
if ( $nt ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->mQueryConds[] = 'LOWER(img_name)' .
$dbr->buildLike( $dbr->anyString(),
strtolower( $nt->getDBkey() ), $dbr->anyString() );
if ( $this->mSearch !== '' ) {
$nt = Title::newFromText( $this->mSearch );
if ( $nt ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds[] = 'LOWER(' . $prefix . '_name)' .
$dbr->buildLike( $dbr->anyString(),
strtolower( $nt->getDBkey() ), $dbr->anyString() );
}
unset( $field );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $dbr->implicitGroupby() ) {
$options = [ 'GROUP BY' => 'img_name' ];
} else {
$this->title = $source;
$this->articleID = $source->getArticleID();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$maxtimestamp = $dbr->selectField(
'revision',
'MIN(rev_timestamp)',
$likeVal = $opts->getValue( 'like' );
if ( !$this->getConfig()->get( 'MiserMode' ) && $likeVal !== '' ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$likeObj = Title::newFromText( $likeVal );
if ( $likeObj instanceof Title ) {
$like = $dbr->buildLike(
* @return array
*/
function getQueryInfo() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$conds = [];
// Don't show hidden names
}
// Lookup groups for all the users
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$groupRes = $dbr->select(
'user_groups',
[ 'ug_user', 'ug_group' ],
// fileprops cache
protected $fileProps = [];
- // current user
- protected $user, $userId, $isLoggedIn;
+ // current user info
+ protected $userId, $isLoggedIn;
/**
* Represents a temporary filestore, with metadata in the database.
* (should replace it eventually).
*
* @param FileRepo $repo
- * @param User $user (default null)
+ * @param User $user
*/
- public function __construct( FileRepo $repo, $user = null ) {
+ public function __construct( FileRepo $repo, User $user ) {
// this might change based on wiki's configuration.
$this->repo = $repo;
- // if a user was passed, use it. otherwise, attempt to use the global.
- // this keeps FileRepo from breaking when it creates an UploadStash object
- if ( $user ) {
- $this->user = $user;
- } else {
- global $wgUser;
- $this->user = $wgUser;
- }
-
- if ( is_object( $this->user ) ) {
- $this->userId = $this->user->getId();
- $this->isLoggedIn = $this->user->isLoggedIn();
- }
+ // We only need the logged in status and user id.
+ $this->userId = $user->getId();
+ $this->isLoggedIn = $user->isLoggedIn();
}
/**
* Helper function: do the actual database query to fetch file metadata.
*
* @param string $key
- * @param int $readFromDB Constant (default: DB_SLAVE)
+ * @param int $readFromDB Constant (default: DB_REPLICA)
* @return bool
*/
- protected function fetchFileMetadata( $key, $readFromDB = DB_SLAVE ) {
+ protected function fetchFileMetadata( $key, $readFromDB = DB_REPLICA ) {
// populate $fileMetadata[$key]
$dbr = null;
if ( $readFromDB === DB_MASTER ) {
/**
* Get a database connection for the bot passwords database
- * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_SLAVE.
+ * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_REPLICA.
* @return DatabaseBase
*/
public static function getDB( $db ) {
}
$audience = $this->checkAudience( $audience );
- $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
$options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
? [ 'LOCK IN SHARE MODE' ]
: [];
}
$audience = $this->checkAudience( $audience );
- $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+ $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
$options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
? [ 'LOCK IN SHARE MODE' ]
: [];
* @throws MWException On unexpected database errors
*/
protected function getUsersByEmail( $email ) {
- $res = wfGetDB( DB_SLAVE )->select(
+ $res = wfGetDB( DB_REPLICA )->select(
'user',
User::selectFields(),
[ 'user_email' => $email ],
$this->getCacheKey( $cache ),
$cache::TTL_HOUR,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
- $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
wfDebug( "User: cache miss for user {$this->mId}\n" );
$this->loadFromDatabase( self::READ_NORMAL );
public static function newFromConfirmationCode( $code, $flags = 0 ) {
$db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$id = $db->selectField(
'user',
$conds[] = 'ug_user > ' . (int)$after;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$ids = $dbr->selectFieldValues(
'user_groups',
'ug_user',
if ( is_null( $this->mGroups ) ) {
$db = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $db->select( 'user_groups',
[ 'ug_group' ],
[ 'ug_user' => $this->mId ],
/**
* Get blocking information
- * @param bool $bFromSlave Whether to check the slave database first.
- * To improve performance, non-critical checks are done against slaves.
+ * @param bool $bFromSlave Whether to check the replica DB first.
+ * To improve performance, non-critical checks are done against replica DBs.
* Check when actually saving should be done against master.
*/
private function getBlockedStatus( $bFromSlave = true ) {
/**
* Check if user is blocked
*
- * @param bool $bFromSlave Whether to check the slave database instead of
+ * @param bool $bFromSlave Whether to check the replica DB instead of
* the master. Hacked from false due to horrible probs on site.
* @return bool True if blocked, false otherwise
*/
/**
* Get the block affecting the user, or null if the user is not blocked
*
- * @param bool $bFromSlave Whether to check the slave database instead of the master
+ * @param bool $bFromSlave Whether to check the replica DB instead of the master
* @return Block|null
*/
public function getBlock( $bFromSlave = true ) {
* Check if user is blocked from editing a particular article
*
* @param Title $title Title to check
- * @param bool $bFromSlave Whether to check the slave database instead of the master
+ * @param bool $bFromSlave Whether to check the replica DB instead of the master
* @return bool
*/
public function isBlockedFrom( $title, $bFromSlave = false ) {
return [];
}
$utp = $this->getTalkPage();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Get the "last viewed rev" timestamp from the oldest message notification
$timestamp = $dbr->selectField( 'user_newtalk',
'MIN(user_last_timestamp)',
* @return bool True if the user has new messages
*/
protected function checkNewtalk( $field, $id ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
if ( is_null( $this->mFormerGroups ) ) {
$db = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $db->select( 'user_former_groups',
[ 'ufg_group' ],
[ 'ufg_user' => $this->mId ],
if ( $this->mEditCount === null ) {
/* Populate the count, if it has not been populated yet */
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// check if the user_editcount field has been initialized
$count = $dbr->selectField(
'user', 'user_editcount',
// Only update the timestamp if the page is being watched.
// The query to find out if it is watched is cached both in memcached and per-invocation,
- // and when it does have to be executed, it can be on a slave
+ // and when it does have to be executed, it can be on a replica DB
// If this is the user's newtalk page, we always update the timestamp
$force = '';
if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
// Get a new user_touched that is higher than the old one.
// This will be used for a CAS check as a last-resort safety
- // check against race conditions and slave lag.
+ // check against race conditions and replica DB lag.
$newTouched = $this->newTouchedTimestamp();
$dbw = wfGetDB( DB_MASTER );
// Maybe the problem was a missed cache update; clear it to be safe
$this->clearSharedCache( 'refresh' );
// User was changed in the meantime or loaded with stale data
- $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'slave';
+ $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
throw new MWException(
"CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
" the version of the user to be saved is older than the current version."
$db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
? [ 'LOCK IN SHARE MODE' ]
if ( $this->getId() == 0 ) {
return false; // anons
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$time = $dbr->selectField( 'revision', 'rev_timestamp',
[ 'rev_user' => $this->getId() ],
__METHOD__,
// Lazy initialization check...
if ( $dbw->affectedRows() == 0 ) {
// Now here's a goddamn hack...
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $dbr !== $dbw ) {
- // If we actually have a slave server, the count is
+ // If we actually have a replica DB server, the count is
// at least one behind because the current transaction
// has not been committed and replicated.
$this->mEditCount = $this->initEditCount( 1 );
} else {
- // But if DB_SLAVE is selecting the master, then the
+ // But if DB_REPLICA is selecting the master, then the
// count we just read includes the revision that was
// just added in the working transaction.
$this->mEditCount = $this->initEditCount();
} else {
if ( $this->mEditCount === null ) {
$this->getEditCount();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
} else {
$this->mEditCount++;
* @return int Number of edits
*/
protected function initEditCount( $add = 0 ) {
- // Pull from a slave to be less cruel to servers
+ // Pull from a replica DB to be less cruel to servers
// Accuracy isn't the point anyway here
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$count = (int)$dbr->selectField(
'revision',
'COUNT(rev_user)',
// Load from database
$dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $dbr->select(
'user_properties',
* Get a new instance of this user that was loaded from the master via a locking read
*
* Use this instead of the main context User when updating that user. This avoids races
- * where that user was loaded from a slave or even the master but without proper locks.
+ * where that user was loaded from a replica DB or even the master but without proper locks.
*
* @return User|null Returns null if the user was not found in the DB
* @since 1.27
// Database::select() doesn't like empty arrays
return new ArrayIterator( [] );
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'user',
User::selectFields(),
// Database::select() doesn't like empty arrays
return new ArrayIterator( [] );
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$res = $dbr->select(
'user',
User::selectFields(),
public static function search( $audience, $search, $limit, $offset = 0 ) {
$user = User::newFromName( $search );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$prefix = $user ? $user->getName() : '';
$tables = [ 'user' ];
$cond = [ 'user_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ) ];
* method of batch updating rows in a database. To use create a class
* implementing the RowUpdateGenerator interface and configure the
* BatchRowIterator and BatchRowWriter for access to the correct table.
- * The components will handle reading, writing, and waiting for slaves
+ * The components will handle reading, writing, and waiting for replica DBs
* while the generator implementation handles generating update arrays
* for singular rows.
*
* updated value within the database row.
*
* Sample Response:
- * return array(
+ * return [
* 'some_col' => 'new value',
* 'other_col' => 99,
- * );
+ * ];
*
* @param stdClass $row A row from the database
* @return array Map of column names to updated value within the
"tog-hideminor": "Hȳdan lytela adihtunga in nīwra andwendinga getæle",
"tog-hidepatrolled": "Hȳdan weardoda adihtunga in nīwra andwendinga getæle",
"tog-newpageshidepatrolled": "Hȳdan weardode trametas in nīwra andwendinga getæle",
+ "tog-hidecategorization": "Behȳd trameta floccas",
"tog-extendwatchlist": "Sprǣdan behealdungtæl tō īwenne ealla andwendinga, nā synderlīce þā nīwostan",
"tog-usenewrc": "Settan andwendunga on hēapas on trametum on nīwra andwendunga getæle and behealdungtæle",
"tog-numberheadings": "Settan rīm on forecwidas selflīce",
"tog-watchdefault": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic adihte.",
"tog-watchmoves": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic wege.",
"tog-watchdeletion": "Ēacnian mīn behealdungæl mid trametum and ymelum þā ic forlēose.",
+ "tog-watchuploads": "Eacne nīwa ymelan to mīnum weardgetæle",
"tog-minordefault": "Mearcian ealla adihtunga lytela tō gewunan",
"tog-previewontop": "Īwan forebysene ofer adihtunge mearce",
"tog-previewonfirst": "Īwan forebysene on forman adihtunge",
"علاء",
"Hhaboh162002",
"بدارين",
- "باسم"
+ "باسم",
+ "Moud hosny"
]
},
"tog-underline": "سطر تحت الوصلات:",
"filerevert-submit": "استرجع",
"filerevert-success": "'''[[Media:$1|$1]]''' تم استرجاعها [$4 للنسخة بتاريخ $3، $2].",
"filerevert-badversion": "لا توجد نسخة محلية سابقة لهذا الملف بالتاريخ المعطى.",
+ "filerevert-identical": "الإصدار الحالي من الملف بالفعل مطابق للإصدار المحدد.",
"filedelete": "احذف $1",
"filedelete-legend": "احذف الملف",
"filedelete-intro": "أنت على وشك حذف الملف '''[[Media:$1|$1]]''' مع كل تاريخه.",
"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ميجابيت لكل ثانية",
"rollbacklinkcount-morethan": "revertir más de $1 {{PLURAL:$1|edición|ediciones}}",
"rollbackfailed": "Falló la reversión",
"rollback-missingparam": "Faltan parámetros riquíos na solicitú.",
+ "rollback-missingrevision": "Nun pueden cargase los datos de la revisión.",
"cantrollback": "Nun se pue revertir la edición; el postrer collaborador ye l'únicu autor d'esta páxina.",
"alreadyrolled": "Nun se pue revertir la postrer edición de [[:$1]] fecha por [[User:$2|$2]] ([[User talk:$2|alderique]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\ndaquién más yá editó o revirtió la páxina.\n\nLa postrer edición foi fecha por [[User:$3|$3]] ([[User talk:$3|alderique]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
"editcomment": "El resume de la edición yera: <em>$1</em>.",
"linkaccounts-submit": "Enllazar cuentes",
"unlinkaccounts": "Desenllazar cuentes",
"unlinkaccounts-success": "Desenllazóse la cuenta.",
- "authenticationdatachange-ignored": "Nun se xestionó'l cambéu de los datos d'autentificacion. ¿Seique, nun se configuró un fornidor?"
+ "authenticationdatachange-ignored": "Nun se xestionó'l cambéu de los datos d'autentificacion. ¿Seique, nun se configuró un fornidor?",
+ "userjsispublic": "Atención: les subpáxines JavaScript nun tendríen de contener datos acutaos porque son visibles pa otros usuarios.",
+ "usercssispublic": "Atención: les subpáxines CSS nun tendríen de contener datos acutaos porque son visibles pa otros usuarios."
}
"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-allbytes": "Агульны памер усіх файлаў: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2).",
"mediastatistics-table-mimetype": "MIME-тып",
"mediastatistics-table-extensions": "Магчымыя пашырэньні",
"mediastatistics-table-count": "Колькасьць файлаў",
"mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
"mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
"mw-widgets-titleinput-description-redirect": "перанакіраваньне на $1",
+ "sessionprovider-generic": "$1 сэсіі",
"randomrootpage": "Выпадковая карэнная старонка",
"log-action-filter-block": "Тып блякаваньня:",
"log-action-filter-delete": "Тып выдаленьня:",
"missingarticle-rev": "(সংস্করণ#: $1)",
"missingarticle-diff": "(পার্থক্য: $1, $2)",
"readonly_lag": "ডাটাবেজ স্বয়ংক্রিয়ভাবে বন্ধ করে দেয়া হয়েছে, যাতে অধীন ডাটাবেজ সার্ভারগুলি প্রধান ডাটাবেজ সার্ভারের অবস্থায় আসতে পারে।",
+ "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP শিরলেখে পাঠানো হয়েছিল কিন্তু অনুরোধটি একটি API লিখন মডিউলে ছিল।",
"internalerror": "আভ্যন্তরীণ ত্রুটি",
"internalerror_info": "আভ্যন্তরীণ ত্রুটি: $1",
"internalerror-fatal-exception": "\"$1\" ধরনের মারাত্মক ব্যতিক্রম",
"createacct-email-ph": "আপনার ইমেইল ঠিকানা যোগ করুন",
"createacct-another-email-ph": "আপনার ইমেইল ঠিকানা প্রবেশ করান",
"createaccountmail": "একটি র্যান্ডম পাসওয়ার্ড নির্বাচন করুন এবং নির্ধারিত ইমেইল ঠিকানায় পাঠিয়ে দিন",
+ "createaccountmail-help": "পাসওয়ার্ড জানা ছাড়াই অন্য ব্যক্তির জন্য অ্যাকাউন্ট তৈরি করতে ব্যবহার করা যেতে পারে।",
"createacct-realname": "আসল নাম (ঐচ্ছিক)",
"createaccountreason": "কারণ:",
"createacct-reason": "কারণ",
"nocookiesnew": "ব্যবহারকারীর অ্যাকাউন্টটি সৃষ্টি করা হয়েছে, কিন্তু আপনি এখনও অ্যাকাউন্টে প্রবেশ করেননি। {{SITENAME}}-তে কুকি ব্যবহার করে ব্যবহারকারীদের অ্যাকাউন্টে প্রবেশ করানো হয়। আপনার ব্রাউজারে কুকিগুলি নিষ্ক্রিয় করা আছে। অনুগ্রহ করে কুকিগুলি সক্রিয় করুন এবং আপনার নতুন ব্যবহারকারী নাম ও পাসওয়ার্ড ব্যবহার করে অ্যাকাউন্টে প্রবেশ করুন।",
"nocookieslogin": "ব্যবহারকারীদের প্রবেশ সম্পন্ন করতে {{SITENAME}} কুকি ব্যবহার করে। আপনার ব্রাউজারে কুকি নিষ্ক্রিয় করা আছে। কুকি চালু করে আবার চেষ্টা করুন।",
"nocookiesfornew": "ব্যবহারকারীর অ্যাকাউন্ট তৈরি হয়নি, কারণ এর উৎস সম্পর্কে আমরা নিশ্চিত নই।\nনিশ্চিত করুন আপনার কুকি সক্রিয় রয়েছে, পাতাটি পুনরায় লোড করে আবার চেষ্টা করুন।",
+ "createacct-loginerror": "অ্যাকাউন্ট সফলভাবে তৈরি করা হয়েছে কিন্তু আপনি স্বয়ংক্রিয়ভাবে প্রবেশ করতে পারবেন না। দয়া করে [[Special:UserLogin|ম্যানুয়াল প্রবেশ]] করতে এগিয়ে যান।",
"noname": "আপনি সঠিক ব্যবহারকারী নাম নির্দিষ্ট করেননি।",
"loginsuccesstitle": "প্রবেশ করেছেন",
"loginsuccess": "<strong>আপনি এইমাত্র \"$1\" নামে {{SITENAME}}-তে প্রবেশ করেছেন।</strong>",
"uploadscripted": "এই ফাইলে এমন HTML বা স্ক্রিপ্ট কোড আছে যা একটি ওয়েব ব্রাউজার ভুল বুঝতে পারে।",
"uploaded-script-svg": "আপলোডকৃত SVG ফাইলে স্ক্রিপ্টযোগ্য উপাদান \"$1\" পাওয়া গেছে।",
"uploaded-hostile-svg": "আপলোড করা SVG ফাইলের শৈলী উপাদানে অনিরাপদ সিএসএস পাওয়া গেছে।",
+ "uploaded-href-unsafe-target-svg": "অনিরাপদ উপাত্তে href পাওয়া গেছে: আপলোডকৃত SVG ফাইলে URI লক্ষ্য ছিল <code><$1 $2=\"$3\"></code>।",
"uploaded-image-filter-svg": "আপলোডকৃত SVG ফাইলে URL: <code><$1 $2=\"$3\"></code> সহ ছবি পরিশোধক পাওয়া গেছে।",
"uploadscriptednamespace": "এই SVG ফাইলে অবৈধ নামস্থান \"$1\" রয়েছে",
"uploadinvalidxml": "আপলোডকৃত ফাইলে XML পার্স করা যাবে না।",
"upload-form-label-own-work": "এটি আমার নিজের কাজ",
"upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
"upload-form-label-infoform-date": "তারিখ",
+ "upload-form-label-own-work-message-generic-local": "আমি নিশ্চিত করছি যে আমি {{SITENAME}}-এর পরিষেবা এবং লাইসেন্সকরণ নীতির শর্তাবলী অনুসরণ করে এই ফাইল আপলোড করছি।",
"upload-form-label-not-own-work-local-generic-local": "এছাড়াও আপনি [[Special:Upload|ডিফল্ট আপলোডের পাতা]] চেষ্টা করতে পারেন।",
"upload-form-label-not-own-work-local-generic-foreign": "এছাড়াও আপনি [[Special:Upload|{{SITENAME}}-এর আপলোডের পাতা]] ব্যবহার করার চেষ্টা করতে পারেন, যদি এই ফাইলটি তাদের নীতিমালা অধীনে সেখানে আপলোড করা যায়।",
"backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
"backend-fail-read": "$1 ফাইলটি ওপেন করা যাচ্ছে না।",
"backend-fail-create": "$1 ফাইলটি তৈরী করা যাচ্ছে না।",
"backend-fail-maxsize": "\"$1\" ফাইলে লেখা যাচ্ছে না কারণ এটি {{PLURAL:$2|এক বাইট|$2 বাইট}} থেকে বড়।",
- "backend-fail-readonly": "\"$1\" সà§\8dà¦\9fà§\8bরà§\87à¦\9c বà§\8dযাà¦\95à¦\8fনà§\8dড থà§\87à¦\95à§\87 বরà§\8dতমানà§\87 লà§\87à¦\96া যাà¦\9aà§\8dà¦\9bà§\87 না। à¦\95ারণ: \"''$2''\"",
+ "backend-fail-readonly": "\"$1\" সà§\8dà¦\9fà§\8bরà§\87à¦\9c বà§\8dযাà¦\95à¦\8fনà§\8dড থà§\87à¦\95à§\87 বরà§\8dতমানà§\87 শà§\81ধà§\81-পঠনà§\87 রয়à§\87à¦\9bà§\87। পà§\8dরদতà§\8dত à¦\95ারণ: <em>$2</em>",
"backend-fail-synced": "\"$1\" ফাইলটি ইন্টারনাল স্টোরেজ ব্যকএন্ডের সাথে অসামঞ্জস্যপূর্ণ",
"backend-fail-connect": "স্টোরেজ ব্যাকেন্ড \"$1\" এর সাথে যোগাযোগ করা যাচ্ছে না।",
"backend-fail-internal": "\"$1\" স্টোরেজ ব্যাকেন্ডে কোনো অজানা ত্রুটি হয়েছে।",
"uploadstash-errclear": "ফাইলগুলো পরিষ্কারকরণ ব্যর্থ হয়েছে।",
"uploadstash-refresh": "ফাইলের তালিকা রিফ্রেশ করুন",
"uploadstash-thumbnail": "থাম্বনেইল দেখুন",
+ "uploadstash-exception": "স্টাসে আপলোড সঞ্চয় করা যায়নি ($1): \"$2\"।",
"invalid-chunk-offset": "ত্রুটিপূর্ণ চাংক অফসেট",
"img-auth-accessdenied": "প্রবেশাধিকার নাই",
"img-auth-nopathinfo": "PATH_INFO পাওয়া যাচ্ছে না।\nআপনার সার্ভার থেকে এই তথ্য পাঠানোর জন্য কনফিগার করা হয়নি।\nএটি হয়তো CGI ভিত্তিক এবং img_auth সমর্থন করে না।\nবিস্তারিত দেখুন https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization।",
"apisandbox-fullscreen": "প্যানেল সম্প্রসারণ করুন",
"apisandbox-fullscreen-tooltip": "ব্রাউজারের উইন্ডো পূরণ করতে খেলাঘরের প্যানেল প্রসারিত করুন।",
"apisandbox-unfullscreen": "পাতা দেখাও",
+ "apisandbox-unfullscreen-tooltip": "খেলাঘরের প্যানেল হ্রাস করুন, তাহলে মিডিয়াউইকি পরিভ্রমণ করার সংযোগগুলি পাওয়া যাবে।",
"apisandbox-submit": "অনুরোধ রাখুন",
"apisandbox-reset": "পরিস্কার",
"apisandbox-retry": "পুনঃচেষ্টা করুন",
"listgrouprights-removegroup-self-all": "নিজের অ্যাকাউন্ট থেকে সকল দল অপসারণ",
"listgrouprights-namespaceprotection-header": "নামস্থান নিষেধাজ্ঞাসমূহ",
"listgrouprights-namespaceprotection-namespace": "নামস্থান",
+ "listgrants": "কার্যভার",
+ "listgrants-summary": "নিম্নে ব্যবহারকারী অধিকারের সাথে যুক্ত প্রবেশাধিকারসহ তাদের কার্যভারের একটি তালিকা দেয়া হয়েছে। ব্যবহারকারীরা তাদের অ্যাকাউন্ট ব্যবহার করতে অ্যাপ্লিকেশনকে অনুমোদন দিতে পারে, কিন্তু কার্যভারের উপর ভিত্তি করে সীমিত অনুমতি ব্যবহারকারীরা অ্যাপ্লিকেশনকে দিতে পারবেন। মূলত, একটি অ্যাপ্লিকেশন একজন ব্যবহারকারীর দেয়া অধিকারের অতিরিক্ত অধিকার ব্যবহার করতে পারবে না। পৃথক অধিকার সম্পর্কে [[{{MediaWiki:Listgrouprights-helppage}}|অতিরিক্ত তথ্য]] দেখুন।",
+ "listgrants-grant": "কার্যভার",
"listgrants-rights": "অধিকারসমূহ",
"trackingcategories": "বিষয়শ্রেণীসমূহ অনুসরণ করা হচ্ছে",
"trackingcategories-msg": "বিষয়শ্রেণী অনুসরণ করা হচ্ছে",
"restricted-displaytitle-ignored": "উপেক্ষিত প্রদর্শন শিরোনামসহ পাতা",
"restricted-displaytitle-ignored-desc": "পাতাটি একটি <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> উপেক্ষা করেছে কারণ এটা পাতাটির আসল শিরোনামের সাথে সমতুল্য নয়।",
"broken-file-category-desc": "এই পাতায় একটি ভাঙ্গা ফাইলের লিঙ্ক রয়েছে (একটি ফাইল এম্বেড করার জন্য একটি লিঙ্ক যখন ফাইলটির অস্তিত্ব নেই)",
+ "hidden-category-category-desc": "এই বিষয়শ্রেণীটির পাতার বিষয়বস্তুর মধ্যে <code><nowiki>__HIDDENCAT__</nowiki></code> উপস্থিত রয়েছে, যা পূর্ব-নির্ধারিতভাবে পাতার বিষয়শ্রেণীর সংযোগে দেখায় না।",
"trackingcategories-nodesc": "কোন বর্ণনা নেই।",
"trackingcategories-disabled": "বিষয়শ্রেণীটি বিকল",
"mailnologin": "প্রাপকের ঠিকানা নেই",
"rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
"rollbacklinkcount-morethan": "$1টির বেশি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
"rollbackfailed": "রোলব্যাক ব্যর্থ",
+ "rollback-missingparam": "অনুরোধে প্রয়োজনীয় প্যারামিটারগুলি অনুপস্থিত।",
+ "rollback-missingrevision": "সংশোধনের উপাত্ত লোড করতে অক্ষম।",
"cantrollback": "পূর্বের সংস্করণে ফেরত যাওয়া সম্ভব হল না, সর্বশেষ সম্পাদনাকারী এই নিবন্ধটির একমাত্র লেখক।",
"alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) দ্বারা সম্পাদিত সর্বশেষ [[:$1]] সম্পাদনাটি পুনর্বহাল করা যাচ্ছে না;\nঅন্য কোন ব্যবহারকারী এই পাতা ইতিমধ্যে সম্পাদনা বা পুনর্বহাল করেছেন।\n\nএই পাতায় সর্বোশেষে [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) দ্বারা সম্পাদিত।",
"editcomment": "সম্পাদনা সারাংশ ছিল: \"''$1''\"।",
"undeletedrevisions": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} পুনরুদ্ধার করা হয়েছে",
"undeletedrevisions-files": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} এবং {{PLURAL:$2|১টি ফাইল|$2টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
"undeletedfiles": "{{PLURAL:$1|১টি ফাইল|$1টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
- "cannotundelete": "মà§\81à¦\9bà§\87 ফà§\87লা বাতিল করা যায়নি:\n$1",
+ "cannotundelete": "à¦\95িà¦\9bà§\81 বা সব পà§\81নরà§\81দà§\8dধার করা যায়নি:\n$1",
"undeletedpage": "'''$1 পুনরুদ্ধার করা হয়েছে'''\n\nসাম্প্রতিক মুছে ফেলা ও পুনরুদ্ধারের ঘটনাগুলির জন্য [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
"undelete-header": "সাম্প্রতিক সময়ে মুছে ফেলা পাতাগুলি দেখতে [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
"undelete-search-title": "অপসারিত পাতা অনুসন্ধান করো",
"sp-contributions-newbies-sub": "নতুন অ্যাকাউন্টের জন্য",
"sp-contributions-newbies-title": "নতুন অ্যাকাউন্টের ব্যবহারকারী অবদান",
"sp-contributions-blocklog": "বাধা দানের লগ",
- "sp-contributions-suppresslog": "মà§\81à¦\9bà§\87 ফà§\87লা বà§\8dযবহারà¦\95ারà§\80 অবদান",
- "sp-contributions-deleted": "মুছে ফেলা ব্যবহারকারী অবদান",
+ "sp-contributions-suppresslog": "à¦\97à§\8bপনà¦\95à§\83ত {{GENDER:$1|বà§\8dযবহারà¦\95ারà§\80র}} অবদান",
+ "sp-contributions-deleted": "মুছে ফেলা {{GENDER:$1|ব্যবহারকারীর}} অবদান",
"sp-contributions-uploads": "আপলোডসমূহ",
"sp-contributions-logs": "লগসমূহ",
"sp-contributions-talk": "আলোচনা",
"move-page-legend": "পাতা স্থানান্তর",
"movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
"movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
- "movepagetalktext": "পাতাà¦\9fির সাথà§\87 সাথà§\87 সà¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা পাতাà¦\9fিà¦\93 সà§\8dবয়à¦\82à¦\95à§\8dরিয়à¦à¦¾à¦¬à§\87 সরানà§\8b হবà§\87 '''যদি না:'''\n*à¦\96ালি নয় à¦\8fমন à¦\8fà¦\95à¦\9fি à¦\86লাপ পাতা নতà§\81ন শিরà§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 বিদà§\8dযমান থাà¦\95à§\87, à¦\85থবা\n*à¦\86পনি নিà¦\9aà§\87র বাà¦\95à§\8dসà¦\9fি থà§\87à¦\95à§\87 à¦\9fিà¦\95 সরিয়à§\87 নিতà§\87 পারà§\87ন।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
+ "movepagetalktext": "à¦\86পনি যদি à¦\8fà¦\87 বাà¦\95à§\8dসà§\87 à¦\9fিà¦\95 দà§\87ন, তাহলà§\87 পাতাà¦\9fির সাথà§\87 সà¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা পাতাà¦\9fিà¦\93 সà§\8dবয়à¦\82à¦\95à§\8dরিয়à¦à¦¾à¦¬à§\87 নতà§\81ন শিরà§\8bনামà§\87 সরানà§\8b হবà§\87 যদি না নতà§\81ন শিরà§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 à¦\8fà¦\95à¦\9fি বিদà§\8dযমান থাà¦\95à§\87।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
"moveuserpage-warning": "'''সতর্কতা:''' আপনি একটি ব্যবহারকারী পাতা স্থানান্তর করছেন। অনুগ্রহ করে লক্ষ্য করুন যে এর মাধ্যমে কেবলমাত্র পাতাটি স্থানান্তর হবে, কিন্তু পাতার নাম পরিবর্তন হবে ''না''।",
"movecategorypage-warning": "<strong>সতর্কীকরণ:</strong> আপনি একটি বিষয়শ্রেণীর পাতা স্থানান্তর করতে চলেছেন। দয়া করে মনে রাখবেন যে এতে শুধুমাত্র পাতাটি স্থানান্তরিত হবে এবং পুরাতন বিষয়শ্রেণীতে থাকা কোন পাতা নতুনটিতে পুনঃশ্রেণীকরণ করা হবে <em>না</em>।",
"movenologintext": "কোন পাতা সরিয়ে ফেলতে চাইলে আপনাকে অবশ্যই একজন নিবন্ধিত ব্যবহারকারী হতে হবে ও অ্যাকাউন্টে [[Special:UserLogin|প্রবেশ]] করতে হবে।",
"mediastatistics-header-office": "অফিস",
"mediastatistics-header-archive": "সংকুচিত বিন্যাস",
"mediastatistics-header-total": "সকল ফাইল",
+ "json-warn-trailing-comma": "JSON থেকে $1টি সর্বশেষ {{PLURAL:$1|কমা}} সরানো হয়েছে",
"json-error-unknown": "JSON-এ একটি সমস্যা রয়েছে। ত্রুটি: $1",
+ "json-error-depth": "সর্বাধিক স্ট্যাকের গভীরতা অতিক্রম হয়েছে",
"json-error-state-mismatch": "অকার্যকর বা ত্রুটিপূর্ণ JSON",
"json-error-ctrl-char": "অক্ষর নিয়ন্ত্রণ ত্রুটি, সম্ভবত ভুল এনকোডকৃত",
"json-error-syntax": "সিনট্যাক্স ত্রুটি",
"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-delete-revision": "সংশোধন অপসারণ",
"log-action-filter-import-interwiki": "আন্তঃউইকি আমদানি",
"log-action-filter-import-upload": "XML আপলোড কর্তৃক আমদানি",
+ "log-action-filter-managetags-create": "ট্যাগ সৃষ্টিকরণ",
+ "log-action-filter-managetags-delete": "ট্যাগ অপসারণ",
+ "log-action-filter-managetags-activate": "ট্যাগ সক্রিয়করণ",
+ "log-action-filter-managetags-deactivate": "ট্যাগ নিষ্ক্রিয়করণ",
"log-action-filter-newusers-create": "বেনামী ব্যবহারকারী দ্বারা সৃষ্টি",
"log-action-filter-newusers-create2": "নিবন্ধিত ব্যবহারকারী দ্বারা সৃষ্টি",
"log-action-filter-newusers-autocreate": "স্বয়ংক্রিয় সৃষ্টি",
"log-action-filter-upload-upload": "নতুন আপলোড",
"log-action-filter-upload-overwrite": "পুনঃআপলোড",
"authmanager-authn-no-primary": "সরবরাহকৃত পরিচয়পত্রের অনুমোদন যাচাই করা যায়নি।",
+ "authmanager-create-disabled": "অ্যাকাউন্ট সৃষ্টিকরণ নিষ্ক্রিয় করা হয়েছে।",
"authmanager-create-from-login": "আপনার একাউন্ট তৈরি করতে, নীচের ক্ষেত্রগুলি পূরণ করুন।",
"authmanager-authplugin-setpass-failed-title": "পাসওয়ার্ড পরিবর্তন ব্যর্থ হয়েছে",
"authmanager-authplugin-setpass-bad-domain": "অবৈধ ডোমেইন।",
"authprovider-confirmlink-success-line": "$1: সংযোগ করা সফল হয়েছে।",
"authprovider-resetpass-skip-label": "উপেক্ষা করো",
"authprovider-resetpass-skip-help": "পাসওয়ার্ড পুনঃস্থাপন করা উপেক্ষা করুন।",
+ "authform-newtoken": "টোকেন অনুপস্থিত। $1",
+ "authform-notoken": "টোকেন অনুপস্থিত",
"authform-wrongtoken": "ভুল টোকেন",
"specialpage-securitylevel-not-allowed-title": "অনুমতি নেই",
+ "specialpage-securitylevel-not-allowed": "দুঃখিত, আপনি এই পাতা ব্যবহার করতে অনুমতিপ্রাপ্ত নন কারণ আপনার পরিচয় যাচাই করা যায়নি।",
"cannotauth-not-allowed-title": "অনুমতি অস্বীকৃত",
"cannotauth-not-allowed": "আপনি এই পাতাটি ব্যবহার করতে অনুমতিপ্রাপ্ত নন।",
"changecredentials": "পরিচয়পত্র পরিবর্তন করুন",
"credentialsform-provider": "পরিচয়পত্রের ধরন:",
"credentialsform-account": "অ্যাকাউন্টের নাম:",
"linkaccounts": "অ্যাকাউন্ট সংযোগ করুন",
+ "linkaccounts-submit": "অ্যাকাউন্ট সংযোগ করুন",
+ "unlinkaccounts": "অ্যাকাউন্ট সংযোগ বিচ্ছিন্ন করুন",
+ "unlinkaccounts-success": "অ্যাকাউন্টের সংযোগ বিচ্ছিন্ন করা হয়েছে।",
"userjsispublic": "অনুগ্রহ করে লক্ষ্য করুন: জাভাস্ক্রিপ্টের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।",
"usercssispublic": "অনুগ্রহ করে লক্ষ্য করুন: সিএসএসের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।"
}
"minoredit": "Жима хийцам",
"watchthis": "ХӀара агӀо тергаме могӀанан юкъатоха",
"savearticle": "АгӀо дӀаязъян",
+ "savechanges": "Ӏалашбе хийцамаш",
+ "publishpage": "АгӀо кхолла",
"preview": "Хьалххе хьажар",
"showpreview": "Хьалха хьажар",
"showdiff": "Бина болу хийцамашка хьажар",
"searchprofile-articles-tooltip": "$1 чохь лахар",
"searchprofile-images-tooltip": "Файлаш лахар",
"searchprofile-everything-tooltip": "Массо агӀонашкахь лахар (дийцаре агӀонашца)",
- "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а йолÑ\83 Ñ\86Ó\80еÑ\80ийн анашкахь лахар",
+ "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а йолÑ\83 Ñ\86Ó\80еÑ\80ийн меÑ\82Ñ\82игашкахь лахар",
"search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешнаш}})",
"search-result-category-size": "$1 {{PLURAL:$1|юкъаяр}} ($2 {{PLURAL:$2|1=бухара категори|бухара категореш}}, $3 {{PLURAL:$3|1=файл|файлаш}}).",
"search-redirect": "(дӀасахьажийна $1)",
"search-showingresults": "{{PLURAL:$4|Карийна <strong>$1</strong> — цхьаъ агӀо|И дош карийна <strong>$3</strong> агӀонашкахь, царех гойту $2 агӀо}}",
"search-nonefound": "Дехаре терра цхьа хӀума ца карийна.",
"powersearch-legend": "Шуьйра лахар",
- "powersearch-ns": "ЦÓ\80еÑ\80ийн анашкахь лахар:",
+ "powersearch-ns": "ЦÓ\80еÑ\80ийн меÑ\82Ñ\82игашкахь лахар:",
"powersearch-togglelabel": "Билгалдан:",
"powersearch-toggleall": "Массо",
"powersearch-togglenone": "ХӀумма цаоьшу",
"tooltip-ca-nstab-category": "Категорешан агӀо",
"tooltip-minoredit": "Къастам бé хӀокху хийцамна кӀеззиг болуш санна",
"tooltip-save": "Хьан хийцамаш Ӏалашбой",
+ "tooltip-publish": "Гучубаха хьой бина хийцамаш",
"tooltip-preview": "Дехар до, агӀо Ӏалаш ярал хьалха хьажа муха ю из!",
"tooltip-diff": "Гайта долуш долу йозанах бина болу хийцам.",
"tooltip-compareselectedversions": "ХӀокху агӀона шина хаьржина версийн башхалле хьажар.",
"minoredit": "ئەمە دەستکارییەکی بچووکە",
"watchthis": "ئەم پەڕەیە بخە ژێر چاودێری",
"savearticle": "پەڕەکە پاشەکەوت بکە",
+ "savechanges": "پاشەکەوتکردنی گۆڕانکارییەکان",
"preview": "پێشبینین",
"showpreview": "پێشبینین نیشان بدە",
"showdiff": "گۆڕانکارییەکان نیشان بدە",
"minoredit": "Dette er en mindre ændring",
"watchthis": "Overvåg denne side",
"savearticle": "Gem side",
+ "savechanges": "Gem ændringer",
"publishpage": "Offentliggør side",
"publishchanges": "Offentliggør ændringer",
"preview": "Forhåndsvisning",
"rightslogtext": "Dette er en log over ændringer i brugeres rettigheder.",
"action-read": "se denne side",
"action-edit": "redigere denne side",
- "action-createpage": "oprette sider",
- "action-createtalk": "oprette diskussionssider",
+ "action-createpage": "oprette denne side",
+ "action-createtalk": "oprette denne diskussionsside",
"action-createaccount": "Oprette denne brugerkonto",
"action-history": "se historik for denne side",
"action-minoredit": "markere denne redigering som mindre",
"upload-form-label-infoform-title": "Detaljer",
"upload-form-label-infoform-name": "Navn",
"upload-form-label-infoform-description": "Beskrivelse",
+ "upload-form-label-usage-title": "Anvendelse",
"upload-form-label-usage-filename": "Filnavn",
"upload-form-label-infoform-categories": "Kategorier",
"upload-form-label-infoform-date": "Dato",
"log-title-wildcard": "Søg i titler som begynder med teksten",
"showhideselectedlogentries": "Vis/skjul de markerede loghændelser",
"log-edit-tags": "Rediger tags i valgte logposter",
+ "checkbox-all": "Alle",
+ "checkbox-none": "Ingen",
"allpages": "Alle sider",
"nextpage": "Næste side ($1)",
"prevpage": "Forrige side ($1)",
"listgrouprights-namespaceprotection-header": "Navnerumsbegrænsninger",
"listgrouprights-namespaceprotection-namespace": "Navnerum",
"listgrouprights-namespaceprotection-restrictedto": "Rettighed(er) der giver brugeren mulighed for at redigere",
+ "listgrants-rights": "Rettigheder",
"trackingcategories": "Sporingskategorier",
"trackingcategories-summary": "Denne side viser sporing af de kategorier, som er automatisk udfyldt af MediaWiki-softwaren. Deres navne kan ændres ved at ændre de relevante system-beskeder i {{ns:8}}-navnerummet.",
"trackingcategories-msg": "Sporingskategori",
"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",
"nextn-title": "{{PLURAL:$1|Folgendes Ergebnis|Folgende $1 Ergebnisse}}",
"shown-title": "Zeige $1 {{PLURAL:$1|Ergebnis|Ergebnisse}} pro Seite",
"viewprevnext": "Zeige ($1 {{int:pipe-separator}} $2) ($3)",
- "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse anzeigen.}}",
+ "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse:}}",
"searchmenu-new": "<strong>Erstelle die Seite „[[:$1]]“ in diesem Wiki.</strong> {{PLURAL:$2|0=|Siehe auch die über deine Suche gefundene Seite.|Siehe auch die gefundenen Suchergebnisse.}}",
"searchprofile-articles": "Inhaltsseiten",
"searchprofile-images": "Multimedia",
"filerevert-submit": "Zurücksetzen",
"filerevert-success": "'''[[Media:$1|$1]]''' wurde auf die [$4 Version vom $2, $3 Uhr] zurückgesetzt.",
"filerevert-badversion": "Es gibt keine Version der Datei zu dem angegebenen Zeitpunkt.",
+ "filerevert-identical": "Die aktuelle Version der Datei ist bereits mit der ausgewählten Version identisch.",
"filedelete": "Lösche „$1“",
"filedelete-legend": "Lösche Datei",
"filedelete-intro": "Du löschst die Datei '''„[[Media:$1|$1]]“''' inklusive ihrer Versionsgeschichte.",
"hidden-categories": "{{PLURAL:$1|Kategoriya nımıtiye|Kategoriyê nımıtey}}",
"hidden-category-category": "Kategoriyê nımıtey",
"category-subcat-count": "{{PLURAL:$2|Na kategoriya de $1 bınkategoriyay estê.|$2 kategoriyan ra $1 bınkategoriyay asenê.}} \n(K) Kategori (D) Dosya (P) Pela",
- "category-subcat-count-limited": "Na kategoriya de {{PLURAL:$1|ena kategoriya bınên est a|enê $1 kategoriyay bınêni est ê}}.",
+ "category-subcat-count-limited": "Na kategoriye de {{PLURAL:$1|na kategoriya bınêne esta|nê $1 kategoriyê bınêni estê}}.",
"category-article-count": "{{PLURAL:$2|Na kategoriye de teyna ena pele esta.|Ebe $2 ra pêro piya {{PLURAL:$1|ena pela na kategoriye dera|$1 enê peli na kategoriye derê.}}}}",
"category-article-count-limited": "{{PLURAL:$1|Pela cêrêne|$1 Pelê cêrêni}} na kategoriye derê.",
- "category-file-count": "<noinclude>{{PLURAL:$2|Na kategoriya tenya dosyanê cêrênan muhtewa kena.}}</noinclude>\n*Na kategoriya de $2 dosyan ra {{PLURAL:$1|yew dosya tenêka esta| $1 dosyay asenê}}.",
+ "category-file-count": "{{PLURAL:$2|Na kategori tenya dosya ya cêri muhtewa kena.|Na kategori de $2 ra pêro piya {{PLURAL:$1|1 dosya est a|$1 dosyey est ê}}.}}",
"category-file-count-limited": "{{PLURAL:$1|Dosya cêrêne|$1 Dosyê cêrêni}} na kategoriye derê.",
"listingcontinuesabbrev": "dewam...",
"index-category": "Pelê endeksıni",
"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": " 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>&nbsp; [[Category:+]] #REDIRECT[[+]] {{msg-mw|+|notext=1}} !!FUZZY!! ~~~~ <nowiki>+</nowiki></charinsert>\n<charinsert>ڈ ڑ ٹ </charinsert>\n<charinsert>ټ څ ځ ډ ړ ږ ښ ڼ ؤ ي ې ۍ ئ </charinsert>\n<charinsert>{{{+}}} {{+}} {{subst:+}} <noinclude>+</noinclude></charinsert>\n<charinsert><!-- + --> <br /></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-rc": "Vurriyayışê peyêni",
+ "prefs-user-pages": "Pelê karberi",
+ "prefs-personal": "Pela karberi",
+ "prefs-rc": "Peyën vıriyayışi",
"prefs-watchlist": "Lista seyrkerdışi",
"prefs-editwatchlist": "Lista seyrkerdışi bıvurne",
"prefs-editwatchlist-label": "Listey serkerdışanê cıkewtışi timar kerê",
"nchanges": "$1 {{PLURAL:$1|fın vurna|fıni vurna}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ra yok wazino}}",
"enhancedrc-history": "tarix",
- "recentchanges": "Vurriyayışê peyêni",
+ "recentchanges": "Peyën vıriyayışi",
"recentchanges-legend": "Tercihê vurnayışanê peyênan",
"recentchanges-summary": "Wiki sero vurriyayışê peyêni asenê.",
"recentchanges-noresult": "Goreyê kriteranê kıfşkerdeyan ra qet yew vurnayış nêvêniya.",
"recentchanges-legend-heading": "<strong>Kıtabekê Vurriyayışê peyêni:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} Şıma şenê ([[Special:NewPages|Listey peranê newan]] zi bıvinê)",
"recentchanges-legend-plusminus": "''(±123)''",
- "recentchanges-submit": "Bımocne",
+ "recentchanges-submit": "Bımosne",
"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ımosne",
"rcshowhideminor-hide": "Bınımne",
"rcshowhidebots": "botan $1",
- "rcshowhidebots-show": "Bıasne",
+ "rcshowhidebots-show": "Bımosne",
"rcshowhidebots-hide": "Bınımne",
"rcshowhideliu": "karberê qeydbiyayeyi $1",
- "rcshowhideliu-show": "Bımocne",
+ "rcshowhideliu-show": "Bımosne",
"rcshowhideliu-hide": "Bınımne",
"rcshowhideanons": "karberê bênameyi $1",
- "rcshowhideanons-show": "Bıasne",
+ "rcshowhideanons-show": "Bımosne",
"rcshowhideanons-hide": "Bınımne",
"rcshowhidepatr": "$1 vurnayışê ke dewriya geyrayê",
- "rcshowhidepatr-show": "Bımocne",
+ "rcshowhidepatr-show": "Bımosne",
"rcshowhidepatr-hide": "Bınımne",
"rcshowhidemine": "vurnayışanê mı $1",
- "rcshowhidemine-show": "Bımocne",
+ "rcshowhidemine-show": "Bımosne",
"rcshowhidemine-hide": "Bınımne",
"rcshowhidecategorization": "kategorizasyonê pele $1",
- "rcshowhidecategorization-show": "Bımocne",
+ "rcshowhidecategorization-show": "Bımosne",
"rcshowhidecategorization-hide": "Bınımne",
"rclinks": "Peyniya $2 rocan de $1 vurriyayışan ra <br />$3 asenê",
"diff": "ferq",
"hist": "verên",
"hide": "Bınımne",
- "show": "Bımocne",
+ "show": "Bımosne",
"minoreditletter": "q",
"newpageletter": "N",
"boteditletter": "b",
"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 Bıbarne",
+ "uploadbtn": "Dosya Bıbarne",
"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 Bıbarne",
"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",
"protectedpages-unknown-performer": "Karbero nêzanaye",
"protectedtitles": "Sernameyê pawıteyi",
"protectedtitlesempty": "pê ney parametreyan sernuşteyê pawite çinê",
- "protectedtitles-submit": "Sernaman bımocne",
+ "protectedtitles-submit": "Sernaman bımosne",
"listusers": "Listeyê Karberan",
"listusers-editsonly": "Teyna karberan bimucne ke ey nuştê",
"listusers-creationsort": "goreyê wextê vıraştışi rêz ker",
"emailsenttext": "e-mailê şıma erşawiya/ruşiya",
"emailuserfooter": "na e-posta hetê ıney ra $1 erşawiya $2 no/na karberi/e re. pê fonksiyonê \"Karberi/e re e-posta bıerşaw\" no {{SITENAME}} keyepeli erşawiya.",
"usermessage-summary": "Mesacê sistemi caverde.",
- "usermessage-editor": "Mesaj berdoxe sistemi",
+ "usermessage-editor": "Xeberdarê sistemi",
"usermessage-template": "MediaWiki:UserMessage",
"watchlist": "Lista seyrkerdışi",
"mywatchlist": "Lista seyrkerdışi",
"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-header-edits": "Veréna timar kerdışi",
"pageinfo-header-restrictions": "Sıtarkerdışê pele",
"pageinfo-header-properties": "Xısusiyetê pele",
- "pageinfo-display-title": "Sernuştey bımocne",
+ "pageinfo-display-title": "Sernuşteyo ke mosneyêno",
"pageinfo-default-sort": "Hesıbyaye mırfeyo kılm",
"pageinfo-length": "Derdeya pela (bayti heta)",
"pageinfo-article-id": "Kamiya pele",
"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",
"tag-filter-submit": "Avrêc",
"tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiket|Etiketi}}]]: $2)",
"tags-title": "Etiketan",
- "tags-intro": "Eno pel de listeyê eyiketî este ke belki software pê ey edit kenî.",
+ "tags-intro": "Ena per şena ju vurnayışa etiketinu maney nustekeni liste kena",
"tags-tag": "Nameyê etiketi",
"tags-display-header": "Listeyê vurnayîşî de esayîş",
"tags-description-header": "Tam arezekerdışê maneyê cı",
"tags-actions-header": "Kerdışi",
"tags-active-yes": "Eya",
"tags-active-no": "Nê",
- "tags-source-extension": "Teref dê yo dergeneki ra şınasiyayo",
+ "tags-source-extension": "Kışta ju dergeneki ra şınasêna",
"tags-edit": "bıvurne",
"tags-delete": "bestere",
"tags-activate": "Aktiv ke",
"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ı",
"रमेश सिंह बोहरा",
"राम प्रसाद जोशी",
"Macofe",
- "Matma Rex"
+ "Matma Rex",
+ "Nirajan pant"
]
},
"tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
"tog-watchlisthidebots": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
"tog-watchlisthideminor": "मेरा सम्पादनहरू ध्यान सूचीबाट लुकाउन्या",
"tog-watchlisthideliu": "प्रवेश गरेका प्रयोगकर्ताहरूको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
+ "tog-watchlistreloadautomatically": "जज्ज्याँलै फिल्टर बदेलिन्छ इच्छासूची आफुइ रिलोड अर: (जावास्क्रिप्ट चायीन्छ)",
"tog-watchlisthideanons": "अज्ञात प्रयोगकर्ताहरूबाट गरिएको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
"tog-watchlisthidepatrolled": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
"tog-watchlisthidecategorization": "पृष्ठहरूको श्रेणीकरण लुकौन्या",
"tagline": "{{SITENAME}}बाट",
"help": "सहायता",
"search": "खोज",
+ "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\n# * Everything from a \"#\" character to the end of the line is a comment.\n# * Every non-blank line is the exact title to ignore, case and everything.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
"searchbutton": "खोज",
"go": "जाने",
"searcharticle": "जाओ",
"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=नौला रैबारहरू}}",
"missingarticle-rev": "(संशोधन #: $1)",
"missingarticle-diff": "(भिन्नता: $1, $2)",
"readonly_lag": "डेटाबेस स्वतः बन्द गरिया छ जबकि अधिनस्थ डेटाबेस सर्वरले मूल पहिल्याउँनाछ।",
+ "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP हेडर पठाइयाथ्यो तर अनुरोध API write module खिलाइ थ्यो।",
"internalerror": "भित्रका गल्ती",
"internalerror_info": "भित्रका गल्ती: $1",
"internalerror-fatal-exception": "प्रकारको गम्भीर अपवाद \"$1\"",
"title-invalid-interwiki": "अनुरोध गरियाको शिर्षकमी अन्तर विकि लिङ्क छ जइलाई शिर्षकमी प्रयोग गद्द नाइपाइनो ।",
"title-invalid-talk-namespace": "निवेदन गरियाको पानाको शिर्षकले उपलब्ध नभएका कुरडी पानालाई सन्दर्भको रूपमी राख्याको छ ।",
"title-invalid-characters": "निवेदन गरियाको यै पानाको शिर्षकमी अवैध अक्षर रयाको छः \"$1\" ।",
+ "title-invalid-magic-tilde": "अनुरोध अरिया: पन्ना: शीर्षकमी अमान्य म्याजिक टिल्ड शृङ्खला छ (<nowiki>~~~</nowiki>)।",
+ "title-invalid-too-long": "अनुरोध अरिया: पन्ना: शीर्षक भौत लामु छ। यो UTF-8 इनकोडिङमी $1 {{PLURAL:$1|byte|bytes}} है लामु हुनु हुनैन।",
"title-invalid-leading-colon": "निवेदन गरिया पृष्ठको शिर्षकको शुरूमी अवैध कोलोन रया छ ।",
"perfcached": "तलका डाटाहरू क्याचमी रया कुराहरू हुन्। अपटुडेट नहुन लाई सक्दान। बर्ति {{PLURAL:$1|नतिजा|$1 नतिजाहरू}} क्याचमी उपलब्ध छ।",
"perfcachedts": "तलतिरको आँकडा क्याच हो र $1 पहिला अद्यतन गरिया थ्यो। येई क्याचमी उपलब्ध {{PLURAL:$4|एउटा कारण हो|$4 कारणहरू हुन्}}।",
+ "querypage-no-updates": "येइ पन्ना: अद्दतन कार्य ऐल़ निस्क्रिय अरीरैछ।\nयाँ को डाटा ऐलखिलाइ ताजो अरिन्या आथिन।",
"viewsource": "स्रोत हेर",
"viewsource-title": " $1 को स्रोत हेर",
"actionthrottled": "कार्य रोकिईयो",
"viewsourcetext": "तम ये पृष्ठको स्रोत हेद्दु सकुन्छौ और उईको नक्कल उताद्दु सकुन्छौ |",
"viewyourtext": "यै पानामी रह्याका '''तमरा सम्पादनहरू''' हेद्द या प्रतिलिपी गद्द सक्द्या हौ :",
"editinginterface": "<strong>चेतावनी:</strong> तमी यै पानालाई सम्पादन गद्द लाग्याछौ, जनले सफ्टवेयरको लागि \nइन्टरफेस सामग्रीहरू प्रदान गरन्छ।\nयै पानामी गरियाको परिवर्तनले यै विकिमी अरु प्रयोगकर्तानको इन्टरफेसको प्रदर्शनमी प्रभाव पडन्छ ।",
+ "translateinterface": "सप्पै विकिइनखिलाइ अनुवाद थप्दाइ या बदेल्लाइ, कृपया [https://translatewiki.net/ translatewiki.net]को प्रयोग अर:, मिडियाविकि क्षेत्रीयकरण परियोजना:।",
"namespaceprotected": "तमलाई '''$1''' नेमस्पेसमी रह्याका पानाहरू सम्पादन गद्या अनुमति छैन ।",
"customcssprotected": "तमलाई यो पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
"customjsprotected": "तमलाई यो जाभास्कृप्ट पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
"mypreferencesprotected": "तमसंग तमरो अभिरुचीहरू सम्पादन ग्द्दे अनुमती छैन |",
"ns-specialprotected": "विशेष पृष्ठहरू सम्पादन अद्दु नाइँ सकिनो।",
"titleprotected": "[[User:$1|$1]]द्वारा ये शीर्षक निर्माणहुनबठे जोगाइया छ।\nकारण <em>$2</em> हो ।",
- "filereadonlyerror": "फाइल \"$1\" लाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार \"$2\" केवल पढ्ने स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित गर्ने प्रवन्धकले यो कारण दियाकाछन् : ''$3''।",
+ "filereadonlyerror": "\"$1\" फाइललाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार \"$2\" केवल पड्ड्या स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित अद्द्या प्रवन्धकले यो कारण दीराइछ: ''$3''।",
+ "invalidtitle-knownnamespace": "\"$2\" नाउँबार रे \"$3\" पाठ भया: अमान्य शीर्षक",
+ "invalidtitle-unknownnamespace": "अपछ्याणो नाउँबार अङ्क $1 रे पाठ \"$2\" भया: अमान्य शीर्षक",
"exception-nologin": "प्रवेश (लग ईन) नगरिएको",
+ "exception-nologin-text": "येए पन्नालाई चलुनाइ या केयि काम खिलाइ सक्षम हुनाइ लगइन अर:।",
+ "virus-badscanner": "गट्टी मिलजुल: अपछ्याणो भाइरस खोज्ज्या: <em>$1</em>",
"virus-scanfailed": "जँचाई असफल(कोड $1)",
"virus-unknownscanner": "थानभया एन्टीभाइरस:",
"logouttext": "<strong>तमी अहिल बाहिर निस्क्याका छौ।</strong>\n\nयाद राख्या तमीले ब्राउजरको क्याच खालि नगर्यासम्म कुनै पानाहरूमी तमी अझैं प्रवेश गरिरख्याको धेकाउन सक्छ।",
"yourpasswordagain": "पासवर्ड फेरि टाईप गर",
"createacct-yourpasswordagain": "पासवर्ड निश्चित गर",
"createacct-yourpasswordagain-ph": "आजी पासवर्ड लेख",
+ "remembermypassword": "येइ ब्राउजर मी मेरो लगइन फाम अर: (जेदा है जेदा $1 {{PLURAL:$1|दिन|दिनन}})",
"userlogin-remembermypassword": "मुलाई अघाडी झान्या काम गराइराख्या",
"userlogin-signwithsecure": "सुक्षित जडान प्रयोग गद्द्या",
"cannotloginnow-title": "अईल भितर झान नाइँ पाईनो",
"userlogin-resetpassword-link": "पासवर्ड भुलिगया?",
"userlogin-helplink2": "प्रवेश गद्दलाई सहयोग",
"userlogin-loggedin": "तमी {{GENDER:$1|$1}}को रूपमी प्रवेश (लग इन) भइ सक्यौ ।\nअर्को प्रयोगकर्ताको रूपमी प्रवेश (लग इन) गर्न तलको फारम प्रयोग गर ।",
+ "userlogin-reauth": "तम {{GENDER:$1|$1}} हो भणिबर पुष्टि अद्दाइ दोसर्याँ लगइन अर:।",
"userlogin-createanother": "दोसरो खाता खोल",
"createacct-emailrequired": "इमेल ठेगाना",
"createacct-emailoptional": "इमेल ठेगाना (ऐच्छिक)",
"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": "आफ्नो इमेल ठेगाना परिवर्तन गद्द यो फारम भर । यैलाई पुष्टि गद्द तमीले आफ्नो पासवर्ड हाल्नु पडन्छ।",
"changeemail-password": "तमरो {{SITENAME}} पासवर्ड:",
"changeemail-submit": "इमेल परिवर्तन गद्या",
"changeemail-throttled": "तमले अलै भौत फेर प्रवेशका निम्ति प्रयास गरया छौ।\nकृपया $1 पर्खेर मात्र प्रयास गर।",
+ "changeemail-nochange": "कृपया नौलो जुदोइ इमेल ठेगाना राख:।",
"resettokens": "टोकन पूर्वरुपमी फर्काउन्या",
"resettokens-text": "जो टोकन तमरो खातासँग सम्बद्ध केहि विशिष्ट व्यक्तिगत जानकारी प्रदान गर्छन, तम त्यसलाई यहाँ रिसेट गद्द सक्द्या हौ।\n\nयदि तमले तिनलाई भुलवस कैकनै देखाईदिया छौ वा तमरो खाता ह्याक भइसक्याको छ भन्या तम यसलाई रिसेट गर्या ।",
"resettokens-no-tokens": "पूर्वरुमी फर्काउन्या कोई लै टोकन नाइथिन् ।",
"anontalkpagetext": "----''यो कुरडी पानो अज्ञात प्रयोगकर्ताको हो जनले अहिलसम्म खाता बनायाकै छैन, अथवा जनले यै पानाको उपयोग गर्दैन।\nयस कारण हामीले उनलाई उनरो आइ पी (IP) ठेगानाले चिन्न सकन्छौ। \nयस्तो आइ पी (IP) ठेगाना धेरै प्रयोगकर्तानको साझा हुनसकन्छ ।\nयदि तमी अज्ञात प्रयोगकर्ता हौ र तमलाई अचाहिँदो टिप्पणी भयाको अनुभव गद्दा छौ भण्या भविष्यमी अन्य अज्ञात प्रयोगकर्तासँगको भ्रमबाट बाँच्न कृपया [[Special:CreateAccount|खाता खोल]] अथवा [[Special:UserLogin|प्रवेश गर]] ''",
"noarticletext": "यै लेखमी अहिल क्यै पन पाठ नाइथी ।\nतमले और पृष्ठमी\n[[Special:Search/{{PAGENAME}}|यस पृष्ठको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}} यै पानालाई सम्पादन गद्या]</span>.",
"noarticletext-nopermission": "यै लेखमी अहिल केइ पन पाठ नाइथी ।\nतमले और पानामी\n[[Special:Search/{{PAGENAME}}|यै पानाको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज्न],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}} यै पानालाई सम्पादन गद्द] सकन्छौ</span>.",
+ "userpage-userdoesnotexist": "\"$1\" प्रयोगकर्ता खाता दर्ता अरीया: आथिन।\nयेइ पान्नो बनुन/सम्पादन अद्द चाहन्छ: भण्या विचार अर:।",
"userpage-userdoesnotexist-view": "प्रयोगकर्ता खाता \"$1\" दर्ता गरिया छैन।",
+ "blocked-notice-logextract": "यो प्रयोगकर्ता अच्याल प्रतिवन्धित छ।\nसब है पछा: प्रतिबन्ध लग प्रविष्टि सन्दर्भ खिलाइ तल्तिर दियीरैछ:",
+ "sitecsspreview": "<strong>येइ CSSलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
+ "sitejspreview": "<strong>येइ जावास्क्रिप्ट कोडलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
"userinvalidcssjstitle": "<strong>चेतावनी:</strong> यहाँ कोइपनि \"$1\" नामको खोल नाइथिन् ।\nप्रचलित .css तथा .js पानाहरूले निम्नपद शीर्षक प्रयोग गद्दान्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामी {{ns:user}}:Foo/vector.css",
"updated": "नौला",
"note": "'''सूचना:'''",
+ "previewnote": "<strong>फाम अर: कि यो यक पूर्वावलोकन मात्तरी हो।</strong>\nतमले अर्या फेरबदेली आँजि सङ्ग्रहित भया: आथिन!",
"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": "तमरा पाठहरू",
"template-protected": "(सुरक्षित)",
"template-semiprotected": "(अर्ध-सुरक्षित)",
"hiddencategories": "यो पानो निम्न {{PLURAL:$1|1 लुकाइयाको श्रेणी|$1 लुकाइयाका श्रेणीहरू}}को हिस्सादार(सदस्य) हो :",
+ "nocreate-loggedin": "नौला पन्ना बनुनाइ तम सित अधिकार आथिन।",
"sectioneditnotsupported-title": "खण्ड सम्पादन असमर्थित",
"sectioneditnotsupported-text": "ये पृष्ठमी खण्ड सम्पादन असमर्थित",
"permissionserrors": "अधिकारमी त्रुटी",
+ "permissionserrorstext": "तइ काम अद्दाइ तम सित अधिकार आथिन, यिन {{PLURAL:$1|कारण|कारणअन}}ले अद्दा:",
"permissionserrorstext-withaction": "$2 कि लेखा तमलाईँ अनुमति नाइथिन , यिन {{PLURAL:$1|कारणले|कारणहरुले}} गद्दा :",
"moveddeleted-notice": "पानो मेटियाको छ।\nमेटियाका और सारियाका पानाहरूको सूची तल्तिर सन्दर्भखी लेखा दियाको छ।",
"log-fulllog": "पूरा लग हेर",
"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": "तमलाई खाता बनौन लै लग इन अद्द हम हौसला अद्दाउ; काइकि, यो अनिवार्य नाइथी भण्या ।",
"noemail": "Nisûn indirés ed pôsta eletrônica registrê per l'utèint $1.",
"noemailcreate": "L'é necesâri dêr un 'indirés ed pôsta eletrônica vâlid.",
"passwordsent": "'Na nōva cêva 'd ingrès l'é stêda spidîda a l'indiré ed pôsta eletrônica per l'utèint \"$1\". Per piaşèir, fà un ingrès apèina 't la ricēv.",
- "blocked-mailpassword": "Per pervèder abûş, an n'é mìa permès druvêr la funsiòun \"spidés 'na nōva cêva 'd ingrès\" da un indirés IP bluchê.",
+ "blocked-mailpassword": "Al tó indirés IP l’è bluchê int la mudéfica. Per pervèder abûş, an n'é mìa permès druvêr la funsiòun ed recóper ed la cêva ‘d ingrès da cl’indirés IP ché.",
"eauthentsent": "Un mesâg ed cunfèirma l'é stê spidî a l'indirés ed pôsta eletrônica sgnê ché. L'utèint per prèir inviêr di mesâg ed pôsta eletrônica al dēv andêr a drē al j istrusiòun scréti, in môd da cunfermêr ch' l'é ló al legétim proprietâri 'd l'indirés.",
"throttled-mailpassword": "Un mesâg ed pôsta eletrônica 'd arnōv ed la cêva 'd ingrès l'é bèle stê inviê da mēno 'd {{PLURAL:$1|1 ōra|$1 ōri}}. Per pervèder abûş, la funziòun 'd arnōv ed la cêva 'd ingrès la pōl èser druvêda sōl 'na vôlta ògni {{PLURAL:$1|1 ōra|$1 ōri}}.",
"mailerror": "Erōr int la spedisiòun dal mesâg $1",
"createaccount-title": "Per fêr un inscrisiòun a {{SITENAME}}",
"createaccount-text": "Quelchidûn l'à fât un inscrisiòun a {{SITENAME}} ($4) a nòm ed $2 coleghê a cl'indirés ed pôsta eletrônica ché. La cêva 'd ingrès per l'utèint \"$2\" l'é impustêda a \"$3\". \nÉ necesâri fêr un ingrès préma ch' es pôl e cambiêr subét la cêva 'd ingrès. \nSe l'inscrisiòun l'é stêda fâta per şbâli, es pōl scanşlêr sté mesâg.",
"login-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
- "login-abort-generic": "An t'é mìa stê arcgnusû - Scanşlê",
+ "login-abort-generic": "Al tó ingrès an n’è mia stê fât – Al vîn scanşlê",
"login-migrated-generic": "La tó utèinsa l'é stêda spustêda, e al tó nòm utèint al gh'é mìa pió in sém a cla wiki ché.",
"loginlanguagelabel": "Léngva: $1",
"suspicious-userlogout": "La tó dmânda per destachêret l'é stēda rifiutêda perchè la sèmbra spidîda da un navigadōr ch' al funsiòuna mìa o da un proxy di caching.",
"newpassword": "Nōva cêva 'd ingrès:",
"retypenew": "Scrév incòra la nōva cêva 'd ingrès:",
"resetpass_submit": "Scrév la cêva 'd ingrès e và dèinter al sît",
- "changepassword-success": "La cêva 'd ingrès l'é stêda nudifichêda!",
+ "changepassword-success": "La tó cêva 'd ingrès l'é stêda mudifichêda!",
"changepassword-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
"resetpass_forbidden": "An 'né mìa pusébil mudifichêr la cêva 'd ingrès",
"resetpass-no-info": "Per andêr dèinter a cla pàgina ché 't gh'ê da fêr l'ingrès.",
"resetpass-submit-loggedin": "Câmbia la cêva 'd ingrès",
"resetpass-submit-cancel": "Scanşèla",
- "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór n'in pré èser stê dmandê 'na nōva pruvişôria.",
+ "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès an n'é mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór in pré èser stê dmandê 'na nōva pruvişôria.",
"resetpass-recycled": "Mèt dèinter 'na cêva 'd ingrès divêrsa da còla 'd adès.",
"resetpass-temp-emailed": "L'ingrès l'é stê fât cun un côdis pruvişôri. Per finîr la registrasiòun, l'é necesâri impustêr 'na nōva cêva 'd ingrès ché:",
"resetpass-temp-password": "Cêva 'd ingrès pruvişôria:",
"passwordreset-emailtext-ip": "Quelchidûn (prubabilmèint té, cun l'indirés IP $1) l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n \n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia.",
"passwordreset-emailtext-user": "L'utèint $1 ed {{SITENAME}} l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n\n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria ché la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia",
"passwordreset-emailelement": "Nòm utèint: \n$1\n.\nCêva 'd ingrès pruvişôria: \n$2",
- "passwordreset-emailsentemail": "É stê spidî un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès.",
- "changeemail": "Câmbia l'indirés ed la pôsta eletrônica",
- "changeemail-header": "Câmbia l'indirés ed la pôsta eletrônica 'd la tó inscrisiòun.",
+ "passwordreset-emailsentemail": "Se cl’indirés ed pôsta eletrônica ché l’è unî a la tó utèinsa, alōra a gnirà spidî ‘na lètra per per turnêr a impustêr la cêva ‘d ingrès.",
+ "changeemail": "Mudéfica o tó via l'indirés ed pôsta eletrônica",
+ "changeemail-header": "Finés cól fòj ché per cambiêr al tó indirés ed pôsta eletrônica, 'S an 't vō mia avèir nisûn indirés coleghê a la tó utèinsa lêsa vōd al spâsi per l'indirés nōv quând té spidés al fòj.",
"changeemail-no-info": "Per andêr dèinter diretamèint a cla pàgina ché 't gh'ê da fêr l'ingrès.",
"changeemail-oldemail": "L'indirés ed la pôsta eletrànica 'd adès.",
"changeemail-newemail": "Nōv indirés ed pàsta eletrônica:",
"sig_tip": "Fîrma cun la dâta e l'ōra",
"hr_tip": "Rîga spiâna (drōva cun giudési)",
"summary": "Ogèt:",
- "subject": "Argumèint (tétol):",
+ "subject": "Argumèint:",
"minoredit": "Còsta l'é 'na mudéfica céca",
"watchthis": "Tîn adrē a cla pàgina ché",
"savearticle": "Sêlva la pàgina",
"missingsummary": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda cun al mutîv vōd.",
"selfredirect": "<strong>Ateinti:</strong>t'é drē fêr un rinvéi a l'istèsa pàgina. Ét prés avèir şbaliê sgnêr al pôst dal rinvéi o t'é drē mudifichêr la pàgina şbaliêda. S'ét fê cléch incòra in sém a \"{{int:savearticle}}\", al rinvéi al gnirà fât in tót' al manēri.",
"missingcommenttext": "Scréver un cumèint ché sòta.",
- "missingcommentheader": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv/al tétol de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa tétol.",
+ "missingcommentheader": "<strong>Atensiòun:<strong> an n'é mìa stê precişê l'argumèint de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa.",
"summary-preview": "Guêrda préma sûnt:",
- "subject-preview": "Guêrda préma argumèint/tétol:",
+ "subject-preview": "Guêrda préma l'argumèint:",
"previewerrortext": "A gh'é stê 'n erōr mèinter a s'é serchê ed guardêr al lavōr préma 'd salvêrel.",
"blockedtitle": "Utèint bluchê",
"blockedtext": " '''Al tō nòm utèint o indirés IP l'é stê bluchê.'''\n\nAl blôch l'é stê fât da $1. Al mutîv dal blôch l'é còst: ''$2''.\n\n*Inési dal blôch: $8\n*Scadèinsa dal blôch: $6\n*Intervâl ed blôch: $7\n\nS' ét vō, l'é pusébil mètres in cuntât cun $1 o 'n êter [[{{MediaWiki:Grouppage-sysop}}|aministradōr]] per discóter dal blôch.\n\nGuêrda che la funsiòun 'Scrév a l'utèint' an n'é mìa in ôvra s' an n'é mìa stê registrtê un indirés ed pôsta eletrônica vâlid int al tō [[Special:Preferences| preferèinsi]] o se sté funsiòun l'é stêda bluchêda. L'indirés IP 'd adèsa l'é $3, al nóme ID dal blôch l'é #$5. T'é perghê ed precişêr tót j elemèint ed préma per ògni dmânda de spiegasiòun",
"accmailtext": "'Na cêva 'd ingrés l'è stêda fâta a chêş per [[User talk:$1|$1]] e l'è stêda spidîda a $2. Cla cêva 'd ingrès ché la pōl èser cambiêda int la pàgina per ''[[Special:ChangePassword|cambiêr la cêva 'd ingrès]]'' subét dôp avèir fât l'ingrès.",
"newarticle": "(Nōv)",
"newarticletext": "Al colegamèint apèina fât al cumbîna cun 'na pàgina ch' an n'é mìa incòra stêda fâta. S'ét vō fêr la pàgina adès, l'é asê cumincêr a scréver al tèst int la caşèla ché sòt (per vedèr infurmasiòun pió precîşi guêrda la [$1 pàgina 'd ajót]). Se al colegamèint l'é stê avêrt per erōr, l'é asê clichêr al pulsânt \"Indrē\" dal tó navigadōr.",
- "anontalkpagetext": "----'' Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla. Per arcgnòsrel l'è dòunca necesâri druvê al só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'è un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa tè, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr, 'd èser cunfûş cun 'd j êter utèint sèinsa nòm.''",
+ "anontalkpagetext": "----\n<em>Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla.</em> Per arcgnòsrel l'è dòunca necesâri druvê al nóme dal só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'é un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa té, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr, 'd èser cunfûş cun 'd j êter utèint sèinsa nòm.",
"noarticletext": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] opór [{{fullurl:{{FULLPAGENAME}}|action=edit}} mudifichêr la pàgina adèsa]</span>.",
"noarticletext-nopermission": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît o<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] <span>, mó an 't gh'ê mìa al permès ed fêr cla pàgina ché.",
"missing-revision": "La revişiòun #$1 'd la pagina \"{{FULLPAGENAME}}\" l' an gh'è mìa. Còst, ed sôlit, a sucēd mèint'r as va drē a 'n colegamèint a 'na pàgina scanşlêda, in 'na stòria, di lavōr fât, mìa arnuvêda. I particulêr a 's pōlen catêr int al [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} regéster dal scanşladûri].",
"previewnote": "'''Να θυμάστε ότι αυτή είναι μόνο μια προεπισκόπηση.'''\nΟι αλλαγές σας δεν έχουν ακόμη αποθηκευτεί!",
"continue-editing": "Μεταβείτε στην περιοχή επεξεργασίας",
"previewconflict": "Αυτή η προεπισκόπηση απεικονίζει το κείμενο στην επάνω περιοχή επεξεργασίας κειμένου, όπως θα εμφανιστεί εάν επιλέξετε να το αποθηκεύσετε.",
- "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
+ "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να [[Special:UserLogout|αποσυνδεθείτε]] και να συνδεθείτε πάλι.'''",
"session_fail_preview_html": "'''Λυπούμαστε! Δεν μπορέσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.'''\n\n''Επειδή το {{SITENAME}} επιτρέπει την εισαγωγή ακατέργαστου HTML, η προεπισκόπηση είναι κρυμμένη ως προφύλαξη ενάντια σε επιθέσεις με Javascript.''\n\n'''Αν αυτή είναι μια έγκυρη προσπάθεια επεξεργασίας, παρακαλώ προσπαθήστε ξανά. Αν πάλι δε δουλεύει, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
"token_suffix_mismatch": "'''Η επεξεργασία σας απορρίφθηκε γιατί το πρόγραμμα-πελάτη σας κατακρεούργησε τους χαρακτήρες στίξης στο κουπόνι επεξεργασίας. Η επεξεργασία απορρίφθηκε για να αποφευχθεί η παραφθορά του κειμένου της σελίδας.\nΑυτό μερικές φορές συμβαίνει όταν χρησιμοποιείται ένας ανώνυμος διακομιστής μεσολάβησης διαθέσιμος μέσω του παγκόσμιου ιστού με σφάλματα.'''",
"edit_form_incomplete": "'''Ορισμένα τμήματα της φόρμας επεξεργασίας δεν έφθασαν στο διακομιστή. Ελέγξτε ότι οι αλλαγές σας είναι άθικτες και προσπαθήστε ξανά.'''",
"uploadstash-summary": "Η σελίδα παρέχει πρόσβαση σε αρχεία που είναι επιφορτωμένα (ή στη διαδικασία της επιφόρτωσης) αλλά δεν έχει ακόμη δημοσιευθεί για το wiki. Αυτά τα αρχεία δεν είναι ορατά σε οποιονδήποτε, αλλά στο χρήστη που τα επιφόρτωσε.",
"uploadstash-clear": "Καθαρά διατηρημένα αρχεία",
"uploadstash-nofiles": "Δεν έχετε κρυμμένα αρχεία.",
- "uploadstash-badtoken": "Î\95κÏ\84ÎλεÏ\83η Ï\84ηÏ\82 εν λÏ\8cγÏ\89 ενÎÏ\81γειαÏ\82 ήÏ\84αν ανεÏ\80ιÏ\84Ï\85Ï\87ήÏ\82, ίÏ\83Ï\89Ï\82 εÏ\80ειδή Ï\84α διαÏ\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά εÏ\80εξεÏ\81γαÏ\83ίαÏ\82 Ï\83αÏ\82 ÎÏ\87οÏ\85ν λήξει. Î\94οκίμαστε ξανά.",
- "uploadstash-errclear": "Î\97 εκκαθάÏ\81ιÏ\83η Ï\84Ï\89ν αÏ\81Ï\87είÏ\89ν ήÏ\84αν ανεÏ\80ιÏ\84Ï\85Ï\87ήÏ\82.",
+ "uploadstash-badtoken": "Î\95κÏ\84ÎλεÏ\83η Ï\84ηÏ\82 εν λÏ\8cγÏ\89 ενÎÏ\81γειαÏ\82 αÏ\80ÎÏ\84Ï\85Ï\87ε, ίÏ\83Ï\89Ï\82 εÏ\80ειδή Ï\84α διαÏ\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά εÏ\80εξεÏ\81γαÏ\83ίαÏ\82 Ï\83αÏ\82 ÎÏ\87οÏ\85ν λήξει. ΠαÏ\81ακαλοÏ\8dμε δοκιμάστε ξανά.",
+ "uploadstash-errclear": "Î\97 εκκαθάÏ\81ιÏ\83η Ï\84Ï\89ν αÏ\81Ï\87είÏ\89ν αÏ\80ÎÏ\84Ï\85Ï\87ε.",
"uploadstash-refresh": "Ανανεώσετε τη λίστα των αρχείων",
"invalid-chunk-offset": "Άκυρο κομμάτι όφσετ",
"img-auth-accessdenied": "Δεν επετράπη η πρόσβαση",
"delete-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή τέτοιων σελίδων έχει περιοριστεί για την αποφυγή τυχαίας αναστάτωσης του {{SITENAME}}.",
"delete-warning-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή της μπορεί να αναστατώσει τη λειτουργία της βάσης δεδομένων του {{SITENAME}}. Συνιστούμε μεγάλη προσοχή.",
"deleteprotected": "Δεν μπορείτε να διαγράψετε αυτή τη σελίδα επειδή είναι προστατευόμενη.",
- "deleting-backlinks-warning": "\"'Προσοχή:\"' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
+ "deleting-backlinks-warning": "<strong>Προσοχή:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
"rollback": "Επαναφορά επεξεργασιών",
"rollbacklink": "αναστροφή",
"rollbacklinkcount": "Επαναφορά $1 {{PLURAL:$1|επεξεργασίας|επεξεργασιών}}",
"undeletedrevisions": "{{PLURAL:$1|τροποποίηση|τροποποιήσεις}} αποκαταστάθηκαν",
"undeletedrevisions-files": "$1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}} και $2 {{PLURAL:$2|αρχείο|αρχεία}} επαναφέρθηκαν",
"undeletedfiles": "$1 {{PLURAL:$1|αρχείο|αρχεία}} επαναφέρθηκαν",
- "cannotundelete": "Î\97 αναίÏ\81εÏ\83η διαγÏ\81αÏ\86ήÏ\82 αÏ\80ÎÏ\84Ï\85Ï\87ε: $1",
+ "cannotundelete": "Î\9cεÏ\81ικÎÏ\82 ή Ï\8cλεÏ\82 οι αναιÏ\81ÎÏ\83ειÏ\82 διαγÏ\81αÏ\86ήÏ\82 αÏ\80ÎÏ\84Ï\85Ï\87αν: $1",
"undeletedpage": "'''Η $1 έχει επαναφερθεί'''\n\nΣυμβουλευτείτε το [[Special:Log/delete|αρχείο καταγραφής διαγραφών]] για ένα μητρώο των πρόσφατων διαγραφών και επαναφορών.",
"undelete-header": "Δείτε [[Special:Log/delete|το αρχείο καταγραφής διαγραφών]] για πρόσφατα διαγεγραμμένες σελίδες.",
"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": "καταγραφές",
"watchlistedit-raw-done": "Η λίστα παρακολούθησής σας ενημερώθηκε.",
"watchlistedit-raw-added": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} προστέθηκαν:",
"watchlistedit-raw-removed": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} αφαιρέθηκαν:",
- "watchlistedit-clear-title": "Î\95κκαθαÏ\81ιÏ\83μÎνη λίÏ\83Ï\84α παρακολούθησης",
+ "watchlistedit-clear-title": "Î\95κκαθάÏ\81ιÏ\83η λίÏ\83Ï\84αÏ\82 παρακολούθησης",
"watchlistedit-clear-legend": "Εκκαθάριση λίστας παρακολούθησης",
"watchlistedit-clear-explain": "Όλοι οι τίτλοι θα αφαιρεθούν από τη λίστα παρακολούθησής σας",
"watchlistedit-clear-titles": "Τίτλοι:",
"version-libraries-license": "Άδεια χρήσης",
"version-libraries-description": "Περιγραφή",
"version-libraries-authors": "Δημιουργοί",
- "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα ή αναγνωριστικό αναθεώρησης",
+ "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα, αναγνωριστικό αναθεώρησης ή αρχείο καταγραφής ID",
"redirect-submit": "Μετάβαση",
"redirect-lookup": "Αναζήτηση:",
"redirect-value": "Τιμή:",
"tags-delete-submit": "Μη αναστρέψιμη διαγραφή αυτής της ετικέτας",
"tags-delete-not-found": "Η ετικέτα «$1» δεν υπάρχει.",
"tags-delete-too-many-uses": "Η ετικέτα «$1» εφαρμόζεται σε πάνω από {{PLURAL:$2|μία αναθεώρηση|$2 αναθεωρήσεις}}, που σημαίνει ότι δεν μπορεί να διαγραφεί.",
- "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε με επιτυχία, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
+ "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
"tags-delete-no-permission": "Δεν έχετε άδεια να διαχειριστείτε ετικέτες αλλαγής.",
"tags-activate-title": "Ενεργοποίηση ετικέτας",
"tags-activate-question": "Πρόκειται να ενεργοποιήσετε την ετικέτα «$1».",
"tags-edit-reason": "Αιτία:",
"tags-edit-revision-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτή την αναθεώρηση|$1 αναθεωρήσεις}}",
"tags-edit-logentry-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτήν την καταχώρηση|$1 καταχωρήσεις}} του αρχείου καταγραφής",
- "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν με επιτυχία.",
+ "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν.",
"tags-edit-failure": "Οι αλλαγές δεν ήταν δυνατόν να εφαρμοστούν:\n$1",
"tags-edit-nooldid-title": "Μη έγκυρη αναθεώρηση προορισμού",
"tags-edit-none-selected": "Παρακαλώ επιλέξτε τουλάχιστον μία ετικέτα για να προσθέσετε ή να αφαιρέσετε.",
"filerevert-submit": "Revert",
"filerevert-success": "<strong>[[Media:$1|$1]]</strong> has been reverted to the [$4 version as of $3, $2].",
"filerevert-badversion": "There is no previous local version of this file with the provided timestamp.",
+ "filerevert-identical": "The current version of the file is already identical to the selected one.",
"filedelete": "Delete $1",
"filedelete-legend": "Delete file",
"filedelete-intro": "You are about to delete the file <strong>[[Media:$1|$1]]</strong> along with all of its history.",
"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><b/></code> aŭ <code><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."
}
"action-import": "importar páginas desde otro wiki",
"action-importupload": "importar páginas mediante la carga de un archivo",
"action-patrol": "marcar ediciones de otros como verificadas",
- "action-autopatrol": "tener tus ediciones marcadas como verificadas",
+ "action-autopatrol": "marcar como verificadas tus propias ediciones",
"action-unwatchedpages": "ver la lista de páginas no vigiladas",
"action-mergehistory": "fusionar el historial de esta página",
"action-userrights": "modificar todos los permisos de usuario",
"logentry-delete-revision": "$1 {{GENDER:$2|modificó}} la visibilidad de {{PLURAL:$5|una revisión |$5 revisiones}} en la página $3: $4",
"logentry-delete-event-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de eventos del registro en $3",
"logentry-delete-revision-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de las revisiones en la página $3",
- "logentry-suppress-delete": "$1 {{GENDER:$2|borró}} la página $3",
+ "logentry-suppress-delete": "$1 {{GENDER:$2|suprimió}} la página $3",
"logentry-suppress-event": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|un evento|$5 eventos}} del registro en $3: $4",
"logentry-suppress-revision": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|una edición|$5 ediciones}} en la página $3: $4",
"logentry-suppress-event-legacy": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de los eventos del registro en $3",
"tog-watchdefault": "Lisa jälgimisloendisse minu muudetud leheküljed ja failid",
"tog-watchmoves": "Lisa jälgimisloendisse minu teisaldatud leheküljed ja failid",
"tog-watchdeletion": "Lisa jälgimisloendisse minu kustutatud leheküljed ja failid",
+ "tog-watchuploads": "Lisa jälgimisloendisse uued failid, mille olen üles laadinud",
"tog-watchrollback": "Lisa jälgimisloendisse leheküljed, kus olen muudatuse tühistanud",
"tog-minordefault": "Märgi kõik parandused vaikimisi pisiparandusteks",
"tog-previewontop": "Näita eelvaadet toimetamiskasti ees",
"minoredit": "See on pisiparandus",
"watchthis": "Jälgi seda lehekülge",
"savearticle": "Salvesta",
+ "savechanges": "Salvesta",
"publishpage": "Avalda lehekülg",
"publishchanges": "Avalda muudatused",
"preview": "Eelvaade",
"undeletedrevisions": "$1 {{PLURAL:$1|redaktsioon|redaktsiooni}} taastatud",
"undeletedrevisions-files": "{{PLURAL:$1|1 redaktsioon|$1 redaktsiooni}} ja {{PLURAL:$2|1 fail|$2 faili}} taastatud",
"undeletedfiles": "{{PLURAL:$1|1 fail|$1 faili}} taastatud",
- "cannotundelete": "Taastamine ebaõnnestus:\n$1",
+ "cannotundelete": "Taastamine ebaõnnestus osaliselt või täielikult:\n$1",
"undeletedpage": "'''$1 on taastatud'''\n\n[[Special:Log/delete|Kustutamise logist]] võib leida loendi viimastest kustutamistest ja taastamistest.",
"undelete-header": "Hiljuti kustutatud leheküljed leiad [[Special:Log/delete|kustutamislogist]].",
"undelete-search-title": "Kustutatud lehekülgede otsimine",
"sp-contributions-newbies-sub": "Uute kontode kaastöö",
"sp-contributions-newbies-title": "Uute kasutajate kaastöö",
"sp-contributions-blocklog": "blokeerimised",
- "sp-contributions-suppresslog": "varjatud kaastöö",
- "sp-contributions-deleted": "kustutatud kaastöö",
+ "sp-contributions-suppresslog": "{{GENDER:$1|varjatud}} kaastöö",
+ "sp-contributions-deleted": "{{GENDER:$1|kustutatud}} kaastöö",
"sp-contributions-uploads": "üleslaadimised",
"sp-contributions-logs": "logid",
"sp-contributions-talk": "arutelu",
"createacct-another-realname-tip": "Benetako izena hautazkoa da.\nEmatea erabakitzen baduzu hori erabiliko da lanaren atribuzioa egiterako garaian.",
"pt-login": "Saioa hasi",
"pt-login-button": "Saioa hasi",
+ "pt-login-continue-button": "Konexioa jarraitu",
"pt-createaccount": "Sortu kontua",
"pt-userlogout": "Saioa itxi",
"php-mail-error-unknown": "PHPren mail() funtzioan arazo ezezagun bat egon da.",
"resetpass_submit": "Pasahitza definitu eta saioa hasi",
"changepassword-success": "Zure pasahitza aldatu da!",
"changepassword-throttled": "Saioa hasteko saiakera gehiegi egin berri dituzu.\nBerriro saiatu aurretik $1 itxoin, mesedez.",
+ "botpasswords": "Bot pasahitzak",
+ "botpasswords-disabled": "Bot pasahitzak desgaituak daude.",
"botpasswords-label-appid": "Bot izena:",
"botpasswords-label-create": "Sortu",
"botpasswords-label-update": "Eguneratu",
"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.",
"userpage-userdoesnotexist-view": "\"$1\" erabiltzaile-kontua ez dago erregistraturik.",
"blocked-notice-logextract": "Erabiltzaile hau blokeatuta dago une honetan.\nAzken blokeoaren erregistroa ageri da behean, erreferentzia gisa:",
- "clearyourcache": "'''Oharra:''' Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* '''Firefox / Safari:''' ''Shift'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-Shift-R'' edo ''Crtl-F5'' sakatu (''⌘-R''' Mac batean)\n* '''Google Chrome:''' ''Ctrl-Shift-R'' sakatu (''⌘-Shift-R'' Mac batean)\n* '''Internet Explorer:''' ''Ctrl'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-F5'' sakatu\n* '''Opera''' erabiltzaileek ''Tresnak → Hobespenak'' atalera joan eta katxea garbitzeko aukera hautatu",
+ "clearyourcache": "<strong>Oharra:</strong> Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* <strong>Firefox / Safari:</strong> <em>Shift</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-Shift-R</em> edo <em>Crtl-F5</em> sakatu (<em>⌘-R</em> Mac batean)\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R </em> sakatu (<em>⌘-Shift-R</em> Mac batean)\n* <strong>Internet Explorer:</strong> <em>Ctrl</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-F5</em> sakatu\n* <strong>Opera</strong> erabiltzaileek <em>Tresnak → Hobespenak</em> atalera joan eta katxea garbitzeko aukera hautatu",
"usercssyoucanpreview": "'''Laguntza:''' Zure CSS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
"userjsyoucanpreview": "'''Laguntza:''' Zure JS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
"usercsspreview": "'''Ez ahaztu zure CSS kodea aurreikusten zabiltzala.'''\n'''Oraindik gorde gabe dago!'''",
"previewnote": "'''Gogoratu hau aurrikuspen bat dela.'''\nZure aldaketak ez dira oraindik gorde!",
"continue-editing": "Edizio-eremura joan",
"previewconflict": "Aurreikuspenak aldaketen koadroan idatzitako testua erakusten du, gorde ondoren agertuko den bezala.",
- "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu saioa amaitu eta berriz hasten.'''",
+ "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu [[Special:UserLogout|saioa amaitu]] eta berriz hasten.'''",
"session_fail_preview_html": "'''Sentitzen dugu! Ezin izan dugu zure aldaketa burutu, saio datu galera bat medio.'''\n\n''Wiki honek HTML kodea onartzen duenez, aurreikuspena ezgaituta dago JavaScript erasoak saihestu asmoz.''\n\n'''Aldaketa saiakera hau zuzena baldin bada, saiatu berriro mesedez. Arazoak jarraitzen badu, saiatu saioa itxi eta berriz hasten.'''",
"token_suffix_mismatch": "'''Zure aldaketa ezeztatua izan da zure bezeroak puntuazio-karaktereak itxuragabetu dituelako.\nAldaketa ezeztatua izan da testuaren galtzea galarazteko.\nHau batzuetan gertatzen da buggyan oinarritutako web proxy zerbitzua erabiltzean.'''",
"edit_form_incomplete": "'''Aldaketa formularioaren atal batzuk ez dira iritsi zerbitzarira; bi aldiz ziurtatu zure aldaketak osorik daudela eta berriro saiatu.'''",
"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:",
"undeletedrevisions": "{{PLURAL:$1|Berrikuspen 1 leheneratu da|$1 berrikuspen leheneratu dira}}",
"undeletedrevisions-files": "{{PLURAL:$1|berrikuspen|berrikuspen}} eta {{PLURAL:$2|fitxategi|fitxategi}} leheneratu dira",
"undeletedfiles": "{{PLURAL:$1|fitxategi|fitxategi}} leheneratu dira",
- "cannotundelete": "Ezabatutako birgaitzean akatsa: $1",
+ "cannotundelete": "Ezabatutako birgaitze betean edo hainbatetan akatsa: $1",
"undeletedpage": "'''«$1» leheneratu da'''\n\nAzken ezabatze eta leheneratzeak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
"undelete-header": "Berriki ezabatutako orriak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
"undelete-search-title": "Ezabatutako orrialdeak bilatu",
"sp-contributions-newbies-sub": "Hasiberrientzako",
"sp-contributions-newbies-title": "Lankideen ekarpenak lankide berrietn",
"sp-contributions-blocklog": "Blokeaketa erregistroa",
- "sp-contributions-suppresslog": "lankide-ekarpen ezabatuak",
+ "sp-contributions-suppresslog": "{{GENDER:$1|(r)en}} lankide-ekarpen ezabatuak",
"sp-contributions-deleted": "lankide-ekarpen ezabatuak",
"sp-contributions-uploads": "igoerak",
"sp-contributions-logs": "erregistroak",
"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",
"Mikahama",
"01miki10",
"Matma Rex",
- "BiscuitMan"
+ "BiscuitMan",
+ "Alluk."
]
},
"tog-underline": "Linkkien alleviivaus:",
"userlogin-createanother": "Luo toinen käyttäjätunnus",
"createacct-emailrequired": "Sähköpostiosoite",
"createacct-emailoptional": "Sähköpostiosoite (vapaaehtoinen)",
- "createacct-email-ph": "Anna sähköpostiosoitteesi",
+ "createacct-email-ph": "Kirjoita sähköpostiosoitteesi",
"createacct-another-email-ph": "Lisää sähköpostiosoite",
"createaccountmail": "Käytä satunnaista väliaikaissalasanaa ja lähetä se alla olevaan sähköpostiosoitteeseen",
"createaccountmail-help": "Voidaan käyttää luomaan tunnus toiselle käyttäjälle ilman salasanan tietämistä.",
"passwordtoopopular": "Tavanomaisen kaltaisia salasanoja ei saa käyttää. Valitse parempi ja yksilöllisempi salasana.",
"password-name-match": "Salasanasi täytyy olla eri kuin käyttäjätunnuksesi.",
"password-login-forbidden": "Tämän käyttäjänimen ja salasanan käyttö on estetty.",
- "mailmypassword": "Uudista salasana",
+ "mailmypassword": "Hanki uusi salasana",
"passwordremindertitle": "Uusi väliaikainen salasana {{GRAMMAR:elative|{{SITENAME}}}}",
"passwordremindertext": "Joku IP-osoitteesta $1 pyysi {{GRAMMAR:partitive|{{SITENAME}}}} ($4) lähettämään uuden salasanan. Väliaikainen salasana käyttäjälle $2 on nyt $3. Kirjaudu sisään ja vaihda salasana. Väliaikainen salasana vanhenee {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\n\nJos joku muu on tehnyt tämän pyynnön, tai jos olet muistanut salasanasi ja et halua vaihtaa sitä, voit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.",
"noemail": "Käyttäjälle $1 ei ole määritelty sähköpostiosoitetta.",
"botpasswords-label-update": "Päivitä",
"botpasswords-label-cancel": "Peru",
"botpasswords-label-delete": "Poista",
- "botpasswords-label-resetpassword": "Uudista salasana",
+ "botpasswords-label-resetpassword": "Hanki uusi salasana",
"botpasswords-label-grants": "Valittavissa olevat toimintaoikeudet:",
"botpasswords-label-restrictions": "Käyttörajoitukset:",
"botpasswords-label-grants-column": "Myönnetään",
"resetpass-expired": "Salasanasi on vanhentunut. Valitse uusi salasana, jotta pääset kirjautumaan sisään.",
"resetpass-expired-soft": "Salasanasi on vanhentunut ja se pitää uudistaa. Valitse uusi salasana nyt tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit uudistaa salasanan myöhemmin.",
"resetpass-validity-soft": "Salasanasi ei ole kelvollinen: $1\n\nValitse nyt uusi salasana tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit vaihtaa sen myöhemmin.",
- "passwordreset": "Salasanan uudistus",
+ "passwordreset": "Salasanan palauttaminen",
"passwordreset-text-one": "Täytä tämä lomake uudistaaksesi salasanasi.",
- "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi kentistä, jotta saat väliaikaisen salasanan sähköpostitse.}}",
+ "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi seuraavista kentistä, jolloin saat väliaikaisen salasanan sähköpostitse.}}",
"passwordreset-disabled": "Salasanojen uudistaminen ei ole mahdollista tässä wikissä.",
"passwordreset-emaildisabled": "Sähköpostitoiminnot on poistettu käytöstä tässä wikissä.",
"passwordreset-username": "Käyttäjätunnus:",
"Trial",
"Matma Rex",
"Dcausse",
- "Lucas"
+ "Lucas",
+ "Mabroukb"
]
},
"tog-underline": "Soulignement des liens :",
- "tog-hideminor": "Masquer les modifications mineures dans les changements récents",
+ "tog-hideminor": "Masquer les modifications mineures dans les modifications récentes",
"tog-hidepatrolled": "Masquer les modifications relues dans les modifications récentes",
"tog-newpageshidepatrolled": "Masquer les pages relues dans la liste des nouvelles pages",
"tog-hidecategorization": "Masquer la catégorisation des pages",
"previewerrortext": "Une erreur s’est produite lors de la tentative de prévisualisation de vos modifications.",
"blockedtitle": "L’utilisateur est bloqué.",
"blockedtext": "'''Votre compte utilisateur ou votre adresse IP a été bloqué.'''\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : ''$2''.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
- "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:''$2''\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
+ "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:<em>$2:</em>\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
"blockednoreason": "aucune raison donnée",
"whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
"confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].",
"missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique obsolète vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
"userpage-userdoesnotexist": "Le compte utilisateur « <nowiki>$1</nowiki> » n’est pas enregistré. Veuillez vérifier que vous voulez créer cette page.",
"userpage-userdoesnotexist-view": "Le compte utilisateur « $1 » n'est pas enregistré.",
- "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d’information :",
+ "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
"clearyourcache": "<strong>Note :</strong> après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* <strong>Firefox / Safari :</strong> maintenez la touche <em>Maj</em> (<em>Shift</em>) en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> sur un Mac) \n* <strong>Google Chrome :</strong> appuyez sur <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> sur un Mac) \n* <strong>Internet Explorer :</strong> maintenez la touche <em>Ctrl</em> en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> \n* <strong>Opera :</strong> allez dans <em>Menu → Settings</em> (<em>Opera → Préférences</em> sur un Mac) et ensuite à <em>Confidentialité & sécurité → Effacer les données d’exploration → Images et fichiers en cache</em>.",
"usercssyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l’enregistrer.",
"userjsyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l’enregistrer.",
"explainconflict": "Cette page a été changée après que vous ayez commencé à la modifier.\nLa zone de modification supérieure contient le texte tel qu’il est actuellement enregistré dans la base de données.\nVos modifications apparaissent dans la zone de modification inférieure.\nVous allez devoir fusionner vos modifications dans le texte existant.\n<strong>Seul</strong> le texte de la zone supérieure sera sauvegardé si vous cliquez sur « {{int:savearticle}} ».",
"yourtext": "Votre texte",
"storedversion": "La version enregistrée",
- "nonunicodebrowser": "<strong>Attention : votre navigateur ne supporte pas l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII sous forme hexadécimale dans la boîte de modification.",
+ "nonunicodebrowser": "<strong>Attention : votre navigateur ne prend pas en charge l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII sous forme hexadécimale dans la boîte de modification.",
"editingold": "<strong>Attention : vous êtes en train de modifier une ancienne version de cette page.</strong>\nSi vous la publiez, toutes les modifications effectuées depuis cette version seront perdues.",
"yourdiff": "Différences",
"copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). \nSi vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br /> \nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public ou d’une ressource libre similaire. \n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
"last": "diff",
"page_first": "première",
"page_last": "dernière",
- "histlegend": "Diff de sélection: cochez les boîtes radio des révisions à comparer et appuyez sur entrée ou sur le bouton en bas.<br />\nLégende: <strong>({{int:cur}})</strong> = différence avec la dernière révision, <strong>({{int:last}})</strong> = différence avec la précédente révision, <strong>{{int:minoreditletter}}</strong> = modification mineure.",
+ "histlegend": "Diff de sélection : cochez les boîtes radio des versions à comparer et appuyez sur entrée ou sur le bouton en bas.<br />\nLégende: <strong>({{int:cur}})</strong> = différence avec la dernière version, <strong>({{int:last}})</strong> = différence avec la précédente version, <strong>{{int:minoreditletter}}</strong> = modification mineure.",
"history-fieldset-title": "Naviguer dans l’historique",
"history-show-deleted": "Supprimés seulement",
"histfirst": "les plus anciennes",
"history-feed-description": "Historique des versions pour cette page sur le wiki",
"history-feed-item-nocomment": "$1 le $2",
"history-feed-empty": "La page demandée n'existe pas.\nElle a peut-être été effacée ou renommée.\nEssayez de [[Special:Search|rechercher sur le wiki]] pour trouver de nouvelles pages en rapport avec le sujet.",
- "history-edit-tags": "Modifier les balises des révisions sélectionnées",
+ "history-edit-tags": "Modifier les balises des versions sélectionnées",
"rev-deleted-comment": "(résumé de modification retiré)",
"rev-deleted-user": "(nom d'utilisateur retiré)",
"rev-deleted-event": "(détails de l’historique retirés)",
"large-file": "Les fichiers importés ne devraient pas dépasser $1 ; \nce fichier fait $2.",
"largefileserver": "La taille de ce fichier est supérieure au maximum autorisé par le serveur.",
"emptyfile": "Le fichier que vous voulez importer semble vide.\nCeci peut être dû à une erreur dans le nom du fichier.\nVeuillez vérifier que vous désirez vraiment importer ce fichier.",
- "windows-nonascii-filename": "Ce wiki ne supporte pas les noms de fichiers avec des caractères spéciaux.",
+ "windows-nonascii-filename": "Ce wiki ne prend pas en charge les noms de fichiers avec des caractères spéciaux.",
"fileexists": "Un fichier existe déjà sous ce nom.\nMerci de vérifier <strong>[[:$1]]</strong> si vous n'êtes pas certain{{GENDER:||e|}} de vouloir le remplacer.\n[[$1|thumb]]",
"filepageexists": "La page de description pour ce fichier a déjà été créée ici <strong>[[:$1]]</strong>, mais aucun fichier n'existe actuellement sous ce nom.\nLe résumé que vous allez spécifier n'apparaîtra pas sur la page de description.\nPour que ce soit le cas, vous devrez modifier manuellement la page. \n[[$1|thumb]]",
"fileexists-extension": "Un fichier existe avec un nom proche : [[$2|thumb]]\n* Nom du fichier à importer : <strong>[[:$1]]</strong>\n* Nom du fichier existant : <strong>[[:$2]]</strong>\nPeut-être voulez-vous utiliser un nom plus explicite ?",
"upload-form-label-not-own-work-local-generic-local": "Vous pouvez aussi essayer [[Special:Upload|la page de téléchargement par défaut]].",
"upload-form-label-own-work-message-generic-foreign": "Je comprends que je téléverse ce fichier vers un dépôt partagé. Je confirme agir en accord avec les conditions d’utilisation et les règles relatives aux licences de celui-ci.",
"upload-form-label-not-own-work-message-generic-foreign": "Si vous n’êtes pas en mesure de téléverser ce fichier de façon conforme aux règles de ce dépôt partagé, veuillez fermer cette boîte de dialogue et essayer une autre méthode.",
- "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si leur règles de site autorisent le téléversement du fichier.",
+ "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si leurs règles autorisent le téléversement du fichier.",
"backend-fail-stream": "Impossible de lire le fichier \"$1\".",
"backend-fail-backup": "Impossible de sauvegarder le fichier \"$1\".",
"backend-fail-notexists": "Le fichier $1 n’existe pas.",
"zip-file-open-error": "Une erreur s'est produite lors de l'ouverture du fichier ZIP pour contrôle.",
"zip-wrong-format": "Le fichier spécifié n'est pas une archive ZIP.",
"zip-bad": "Le fichier est une archive ZIP corrompue ou illisible.\nIl ne peut pas être correctement vérifié pour la sécurité.",
- "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non supportées par MediaWiki. \nSa sécurité ne peut pas être correctement vérifiée.",
+ "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non prises en charge par MediaWiki.\nSa sécurité ne peut pas être correctement vérifiée.",
"uploadstash": "Cache d’import",
"uploadstash-summary": "Cette page donne accès aux fichiers qui sont importés (ou en cours d’importation), mais ne sont pas encore publiés dans le wiki. Ces fichiers ne sont pas encore visibles, sauf pour l’utilisateur qui les a importés.",
"uploadstash-clear": "Effacer les fichiers en cache d'import",
"uploadstash-exception": "Impossible de stocker le téléchargement dans la réserve ($1) : « $2 ».",
"invalid-chunk-offset": "Offset de segment non valide",
"img-auth-accessdenied": "Accès refusé",
- "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n'est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne supporte pas img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
+ "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n’est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne prend pas en charge img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
"img-auth-notindir": "Le chemin demandé n'est pas le répertoire d'import configuré.",
"img-auth-badtitle": "Impossible de construire un titre valide à partir de « $1 ».",
"img-auth-nologinnWL": "Vous n'êtes pas connecté et « $1 » n'est pas dans la liste blanche.",
"filerevert-submit": "Rétablir",
"filerevert-success": "<strong>[[Media:$1|$1]]</strong> a été rétabli à [$4 la version du $2 à $3].",
"filerevert-badversion": "Il n'y a pas localement de version antérieure du fichier qui porte la date indiquée.",
+ "filerevert-identical": "La version actuelle du fichier est déjà identique à celle sélectionnée.",
"filedelete": "Supprimer $1",
"filedelete-legend": "Supprimer le fichier",
"filedelete-intro": "Vous êtes sur le point de supprimer <strong>[[Media:$1|$1]]</strong> ainsi que tout son historique.",
"undeletedrevisions": "$1 {{PLURAL:$1|version restaurée|versions restaurées}}",
"undeletedrevisions-files": "$1 version{{PLURAL:$1||s}} et $2 fichier{{PLURAL:$2||s}} restauré{{PLURAL:$2||s}}",
"undeletedfiles": "$1 {{PLURAL:$1|fichier restauré|fichiers restaurés}}",
- "cannotundelete": "Certaines ou toutes les restitutions ont échoué:\n$1",
+ "cannotundelete": "Certaines ou toutes les restaurations ont échoué :\n$1",
"undeletedpage": "<strong>La page $1 a été restaurée.</strong>\n\nConsultez le [[Special:Log/delete|journal des suppressions]] pour obtenir la liste des récentes suppressions et restaurations.",
"undelete-header": "Consultez le [[Special:Log/delete|journal des suppressions]] pour lister les pages récemment supprimées.",
"undelete-search-title": "Rechercher les pages supprimées",
"sp-contributions-logs": "journaux",
"sp-contributions-talk": "discuter",
"sp-contributions-userrights": "gérer les droits",
- "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. \nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
- "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
+ "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. \nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
+ "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
"sp-contributions-search": "Rechercher les contributions",
"sp-contributions-username": "Adresse IP ou nom d'utilisateur :",
"sp-contributions-toponly": "Ne montrer que les contributions qui sont les dernières des articles",
"blockipsuccesssub": "Blocage réussi",
"blockipsuccesstext": "[[Special:Contributions/$1|$1]] a été bloqué{{GENDER:$1||e}}.<br />\nConsultez la [[Special:BlockList|liste des blocages]] pour revoir les blocages.",
"ipb-blockingself": "Vous êtes sur le point de bloquer votre propre compte ! Êtes-vous certain{{GENDER:||e}} de vouloir faire cela ?",
- "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprime le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
+ "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprimera le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
"ipb-confirmaction": "Si vous êtes sûr{{GENDER:||e}} de vraiment vouloir le faire, veuillez cocher le champ « {{int:ipb-confirm}} » en bas.",
"ipb-edit-dropdown": "Modifier les motifs de blocage par défaut",
"ipb-unblock-addr": "Débloquer $1",
"ipb-blocklist": "Voir les blocages existants",
"ipb-blocklist-contribs": "Contributions pour {{GENDER:$1|$1}}",
"ipb-blocklist-duration-left": "$1 restant",
- "unblockip": "Débloquer un utilisateur ou une adresse IP",
+ "unblockip": "Débloquer un utilisateur",
"unblockiptext": "Utilisez le formulaire ci-dessous pour redonner les droits d’écriture à une adresse IP ou un nom d’utilisateur qui a été bloqué auparavant.",
"ipusubmit": "Supprimer ce blocage",
"unblocked": "[[User:$1|$1]] a été débloqué{{GENDER:$1||e}}",
"emaillink": "envoyer un courriel",
"autoblocker": "Vous avez été bloqué automatiquement parce que votre adresse IP a été récemment utilisée par « [[User:$1|$1]] ».\nLe motif fourni pour le blocage de $1 est « $2 »",
"blocklogpage": "Journal des blocages",
- "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. \nLe journal des blocages est disponible ci-dessous :",
- "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. \nLe journal des masquages est disponible ci-dessous :",
+ "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. \nLe journal des blocages est affiché ci-dessous pour référence :",
+ "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. \nLe journal des masquages est affiché ci-dessous pour référence :",
"blocklogentry": "a bloqué [[$1]] ; expiration : $2 $3",
"reblock-logentry": "a modifié les paramètres du blocage de [[$1]] avec une expiration au $2 $3",
"blocklogtext": "Ceci est le journal des actions de blocage et déblocage d’utilisateurs.\nLes adresses IP automatiquement bloquées ne sont pas listées.\nConsultez la [[Special:BlockList|liste des blocages]] pour voir les bannissements et blocages effectivement en cours.",
"movepagetalktext": "Si vous cochez cette case, la page de discussion associée sera automatiquement renommée, à moins qu’une page de discussion non vide existe déjà sous ce nouveau nom.\n\nDans ce cas, vous devrez renommer ou fusionner cette page de discussion manuellement si vous le désirez.",
"moveuserpage-warning": "<strong>Attention :</strong> Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur <em>ne</em> sera <em>pas</em> renommé.",
"movecategorypage-warning": "<strong>Avertissement :</strong> Vous êtes sur le point de renommer une page de catégorie. Veuillez noter que seule la catégorie sera renommée et <em>qu’aucune</em> des pages de l’ancienne catégorie ne sera transférée dans la nouvelle.",
- "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré et d'ancienneté suffisante.",
+ "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré.",
"movenotallowed": "Vous n'avez pas la permission de renommer les pages.",
"movenotallowedfile": "Vous n'avez pas la permission de renommer les fichiers.",
"cant-move-user-page": "Vous n’avez pas la permission de renommer les pages principales des utilisateurs (sauf les sous-pages).",
"cant-move-to-user-page": "Vous n’avez pas la permission de renommer une page vers une page utilisateur (mais vous pouvez le faire vers une sous-page utilisateur).",
- "cant-move-category-page": "Vous n'avez pas la permis de renommer les pages de catégorie.",
+ "cant-move-category-page": "Vous n'avez pas la permission de renommer les pages de catégorie.",
"cant-move-to-category-page": "Vous n'avez pas la permission de renommer une page vers une page de catégorie.",
"newtitle": "Nouveau titre :",
"move-watch": "Suivre les pages originale et nouvelle",
"movepagebtn": "Renommer la page",
"pagemovedsub": "Renommage réussi",
- "movepage-moved": "'''« $1 » a été renommée en « $2 »'''",
+ "movepage-moved": "<strong>« $1 » a été renommée en « $2 »</strong>",
"movepage-moved-redirect": "Une redirection depuis l'ancien nom a été créée.",
"movepage-moved-noredirect": "La création d'une redirection depuis l'ancien nom a été annulée.",
"articleexists": "Il existe déjà une page portant ce titre, ou le titre que vous avez choisi n'est pas correct.\nVeuillez en choisir un autre.",
"cantmove-titleprotected": "Vous ne pouvez pas déplacer une page vers cet emplacement car la création de page avec ce nouveau titre a été protégée.",
"movetalk": "Renommer aussi la page de discussion associée",
- "move-subpages": "Renommer les sous-pages (jusqu'à $1 {{PLURAL:$1|page|pages}})",
- "move-talk-subpages": "Renommer les sous-pages de la page de discussion (jusqu'à $1 pages)",
+ "move-subpages": "Renommer les sous-pages (maximum $1)",
+ "move-talk-subpages": "Renommer les sous-pages de la page de discussion (maximum $1)",
"movepage-page-exists": "La page $1 existe déjà et ne peut pas être écrasée automatiquement.",
"movepage-page-moved": "La page $1 a été renommée en $2.",
"movepage-page-unmoved": "La page $1 n'a pas pu être renommée en $2.",
"selfmove": "Les titres d'origine et de destination sont les mêmes ;\nimpossible de renommer une page sur elle-même.",
"immobile-source-namespace": "Vous ne pouvez pas renommer les pages dans l'espace de noms « $1 »",
"immobile-target-namespace": "Vous ne pouvez pas renommer des pages vers l’espace de noms « $1 ».",
- "immobile-target-namespace-iw": "Les destinations interwikis ne sont pas une cible valide pour les déplacements.",
+ "immobile-target-namespace-iw": "Un lien interwiki n’est pas une cible valide pour un renommage de page.",
"immobile-source-page": "Cette page n'est pas renommable.",
"immobile-target-page": "Il n'est pas possible de renommer la page vers ce titre.",
"bad-target-model": "La destination souhaitée utilise un autre modèle de contenu. Impossible de convertir de $1 vers $2.",
"imageinvalidfilename": "Le nom du fichier cible est incorrect",
"fix-double-redirects": "Mettre à jour les redirections pointant vers le titre original",
"move-leave-redirect": "Laisser une redirection vers le nouveau titre",
- "protectedpagemovewarning": "'''Attention :''' Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
- "semiprotectedpagemovewarning": "'''Note :''' Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
+ "protectedpagemovewarning": "<strong>Attention :</strong> Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
+ "semiprotectedpagemovewarning": "<strong>Note :</strong> Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
"move-over-sharedrepo": "[[:$1]] existe déjà sur un dépôt partagé. Renommer ce fichier rendra le fichier sur le dépôt partagé inaccessible.",
"file-exists-sharedrepo": "Le nom choisi est déjà utilisé par un fichier sur un dépôt partagé.\nChoisissez un autre nom.",
"export": "Exporter des pages",
- "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages ; le résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
+ "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages.\nLe résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
"exportall": "Exporter toutes les pages",
"exportcuronly": "Exporter uniquement la version courante, sans l’historique complet",
"exportnohistory": "----\n'''Note :''' l’exportation de l’historique complet des pages à l’aide de ce formulaire a été désactivée pour des raisons de performance.",
"export-submit": "Exporter",
"export-addcattext": "Ajouter les pages de la catégorie :",
"export-addcat": "Ajouter",
- "export-addnstext": "Ajouter des pages dans l'espace de noms :",
+ "export-addnstext": "Ajouter des pages de l'espace de noms :",
"export-addns": "Ajouter",
"export-download": "Enregistrer dans un fichier",
"export-templates": "Inclure les modèles",
- "export-pagelinks": "Inclure les pages liées à une profondeur de :",
+ "export-pagelinks": "Inclure les pages liées jusqu'à une profondeur de :",
"export-manual": "Ajouter des pages manuellement :",
"allmessages": "Messages système",
- "allmessagesname": "Nom du message",
+ "allmessagesname": "Nom",
"allmessagesdefault": "Message par défaut",
"allmessagescurrent": "Message actuel",
"allmessagestext": "Ceci est la liste des messages système disponibles dans l’espace de noms MediaWiki.\nVeuillez visiter la [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation Régionalisation de MediaWiki] et [https://translatewiki.net/ translatewiki.net] si vous désirez contribuer à la régionalisation générique de MediaWiki.",
- "allmessagesnotsupportedDB": "Cette page '''{{ns:special}}:Allmessages''' n'est pas utilisable car '''$wgUseDatabaseMessages''' a été désactivé.",
+ "allmessagesnotsupportedDB": "Cette page n’est pas utilisable car <strong>$wgUseDatabaseMessages</strong> a été désactivé.",
"allmessages-filter-legend": "Filtrer",
"allmessages-filter": "Filtrer par état de modification :",
"allmessages-filter-unmodified": "Non modifié",
"thumbnail_invalid_params": "Paramètres de la miniature incorrects",
"thumbnail_toobigimagearea": "Fichier avec des dimensions supérieures à $1",
"thumbnail_dest_directory": "Impossible de créer le répertoire de destination",
- "thumbnail_image-type": "Type d'image non supporté",
+ "thumbnail_image-type": "Type d’image non pris en charge",
"thumbnail_gd-library": "Configuration incomplète de la bibliothèque GD : fonction $1 introuvable",
"thumbnail_image-missing": "Le fichier suivant est introuvable : $1",
"thumbnail_image-failure-limit": "Il y a eu récemment trop de tentatives échouées ($1 ou plus) pour restituer cette vignette. Veuillez réessayer ultérieurement.",
"import-mapping-subpage": "Importer comme sous-pages de la page suivante :",
"import-upload-filename": "Nom du fichier :",
"import-comment": "Commentaire :",
- "importtext": "Veuillez exporter le fichier depuis le wiki d'origine en utilisant son [[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
+ "importtext": "Veuillez exporter le fichier depuis le wiki d’origine en utilisant l’[[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
"importstart": "Importation des pages…",
"import-revision-count": "$1 version{{PLURAL:$1||s}}",
"importnopages": "Aucune page à importer.",
"importuploaderrortemp": "L'import du fichier a échoué.\nUn dossier temporaire est manquant.",
"import-parse-failure": "Échec lors de l'analyse du XML à importer",
"import-noarticle": "Aucune page à importer !",
- "import-nonewrevisions": "Aucune révision importée (toutes étaient déjà présentes, ou ignorées du fait d’erreurs).",
+ "import-nonewrevisions": "Aucune révision importée (toutes étaient soit déjà présentes, soit ignorées du fait d’erreurs).",
"xml-error-string": "$1 à la ligne $2, colonne $3 (octet $4) : $5",
"import-upload": "Import de données XML",
"import-token-mismatch": "Perte des données de session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayez</strong>.\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
"import-error-special": "La page « $1 » n’a pas été importée parce qu’elle appartient à un espace de noms spécial qui n’autorise aucune page.",
"import-error-invalid": "Page « $1 » n’a pas été importée parce que le nom sous lequel elle aurait été importée n’est pas valide sur ce wiki.",
"import-error-unserialize": "La révision $2 de la page « $1 » ne peut pas être désérialisée. La révision est indiquée comme utilisant le modèle de contenu $3 sérialisé en $4.",
- "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stocké sur « $1 » sur ce wiki, car ce modèle n’est pas supporté sur cette page.",
+ "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stockée sur « $1 » sur ce wiki, car ce modèle n’est pas pris en charge sur cette page.",
"import-options-wrong": "{{PLURAL:$2|Mauvaise option|Mauvaises options}} : <nowiki>$1</nowiki>",
"import-rootpage-invalid": "La page racine fournie est un titre non valide.",
"import-rootpage-nosubpage": "L'espace de noms « $1 » de la page racine n'autorise pas les sous-pages.",
"javascripttest-pagetext-unknownaction": "Action « $1 » inconnue.",
"javascripttest-qunit-intro": "Voir [$1 la documentation de test] sur mediawiki.org.",
"tooltip-pt-userpage": "{{GENDER:|Votre}} page utilisateur",
- "tooltip-pt-anonuserpage": "La page utilisateur de l'IP avec laquelle vous contribuez",
+ "tooltip-pt-anonuserpage": "La page utilisateur avec l'adresse IP de laquelle vous contribuez",
"tooltip-pt-mytalk": "{{GENDER:|Votre}} page de discussion",
"tooltip-pt-anontalk": "La page de discussion pour les contributions depuis cette adresse IP",
"tooltip-pt-preferences": "{{GENDER:|Vos}} préférences",
- "tooltip-pt-watchlist": "La liste des pages dont vous suivez les modifications",
+ "tooltip-pt-watchlist": "Une liste des pages dont vous suivez les modifications",
"tooltip-pt-mycontris": "La liste de {{GENDER:|vos}} contributions",
"tooltip-pt-anoncontribs": "Une liste des modifications effectuées depuis cette adresse IP",
"tooltip-pt-login": "Il est recommandé de vous identifier ; ce n'est cependant pas obligatoire.",
"tooltip-n-recentchanges": "Liste des modifications récentes sur le wiki",
"tooltip-n-randompage": "Afficher une page au hasard",
"tooltip-n-help": "Aide",
- "tooltip-t-whatlinkshere": "Liste des pages liées à celle-ci",
- "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages liées à celle-ci",
+ "tooltip-t-whatlinkshere": "Liste des pages liées qui pointent sur celle-ci",
+ "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages appelées par celle-ci",
"tooltip-feed-rss": "Flux RSS pour cette page",
"tooltip-feed-atom": "Flux Atom pour cette page",
"tooltip-t-contributions": "Voir la liste des contributions de {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
"tags-edit-logentry-submit": "Appliquer les modifications à {{PLURAL:$1|cette entrée de journal|$1 entrées de journal}}",
"tags-edit-success": "Les modifications ont été appliquées.",
"tags-edit-failure": "Les modifications n’ont pas pu être appliquées :\n$1",
- "tags-edit-nooldid-title": "Révision cible non valide",
- "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de révision cible sur laquelle exécuter cette fonction, soit la révision spécifiée n’existe pas.",
+ "tags-edit-nooldid-title": "Version cible non valide",
+ "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de version cible sur laquelle exécuter cette fonction, soit la version spécifiée n’existe pas.",
"tags-edit-none-selected": "Veuillez sélectionner au moins une balise à ajouter ou enlever.",
"comparepages": "Comparer des pages",
"compare-page1": "Page 1",
"htmlform-title-not-exists": "$1 n’existe pas",
"htmlform-user-not-exists": "<strong>$1</strong> n’existe pas.",
"htmlform-user-not-valid": "<strong>$1</strong> n’est pas un nom d’utilisateur valide.",
- "sqlite-has-fts": "$1 avec recherche en texte intégral supportée",
- "sqlite-no-fts": "$1 sans recherche en texte intégral supportée",
+ "sqlite-has-fts": "$1 avec recherche en texte intégral prise en charge",
+ "sqlite-no-fts": "$1 sans recherche en texte intégral prise en charge",
"logentry-delete-delete": "$1 {{GENDER:$2|a supprimé}} la page $3",
"logentry-delete-restore": "$1 {{GENDER:$2|a restauré}} la page $3",
"logentry-delete-event": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'un événement du journal|de $5 événements du journal}} sur $3: $4",
"filerevert-submit": "Reverter",
"filerevert-success": "Reverteuse \"'''[[Media:$1|$1]]'''\" á [$4 versión do $2 ás $3].",
"filerevert-badversion": "Non existe unha versión local anterior deste ficheiro coa data e hora indicadas.",
+ "filerevert-identical": "A versión actual do ficheiro é igual á seleccionada.",
"filedelete": "Borrar \"$1\"",
"filedelete-legend": "Eliminar un ficheiro",
"filedelete-intro": "Está a piques de eliminar o ficheiro \"'''[[Media:$1|$1]]'''\" xunto con todo o seu historial.",
"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\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
"filerevert-submit": "שחזור",
"filerevert-success": "<strong>[[Media:$1|$1]]</strong> שוחזר ל[$4 גרסה מ־$3, $2].",
"filerevert-badversion": "אין גרסה מקומית קודמת של הקובץ שהועלתה בתאריך המבוקש.",
+ "filerevert-identical": "הגרסה הנוכחית של הקובץ כבר זהה לגרסה שנבחרה.",
"filedelete": "מחיקת $1",
"filedelete-legend": "מחיקת קובץ",
"filedelete-intro": "אתם עומדים למחוק את הקובץ <strong>[[Media:$1|$1]]</strong> יחד עם כל היסטוריית הגרסאות שלו.",
"rollback-success": "שוחזר מעריכות של $1 לעריכה האחרונה של $2",
"rollback-success-notify": "שוחזר מעריכות של $1 לעריכה האחרונה של $2. [$3 הצגת שינויים]",
"sessionfailure-title": "בעיה בחיבור",
- "sessionfailure": "נראה שיש בעיה בחיבורכם לאתר;\nפעולתכם בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבכם.\nאנא חזרו לדף הקודם, העלו אותו מחדש ונסו שוב.",
+ "sessionfailure": "נראה שיש בעיה בחיבור שלך לאתר;\nפעולה זו בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבך.\nנא לחזור לדף הקודם, לטעון אותו מחדש ולנסות שוב.",
"changecontentmodel": "שינוי מודל התוכן של דף",
"changecontentmodel-legend": "שינוי מודל התוכן",
"changecontentmodel-title-label": "שם הדף",
"passwordreset-emailelement": "सदस्यनाम: \n$1\n\nअस्थायी कूटशब्द: \n$2",
"passwordreset-emailsentemail": "यदि आपका यह ईमेल आपके खाते के साथ जोड़ा गया है तो पासवर्ड बदलने का ईमेल इसमें भेज दिया गया है।",
"passwordreset-emailsentusername": "यदि कोई ईमेल इस खाते से जुड़ी है तो पासवर्ड आपके ईमेल में भेज दिया जाएगा।",
- "passwordreset-emailsent-capture": "नीचे दिखाया गया कूटशब्द रीसेट ई-मेल भेज दिया गया है।",
- "passwordreset-emailerror-capture": "नीचे दृष्टित कूटशब्द रीसेट ई-मेल उत्पन्न किया गया था, परंतु उसे {{GENDER:$2|सदस्य}} को भेजना असफल रहा।\nत्रुटि: $1",
"passwordreset-emailerror-capture2": "{{GENDER:$2|सदस्य}} को ईमेल भेजना विफल : $1 {{PLURAL:$3|सदस्य नाम और पासवर्ड|सदस्य नाम और पासवर्ड की सूची}} नीचे दिया गया है।",
"passwordreset-invalideamil": "अवैध ईमेल पता",
"changeemail": "ई-मेल पता परिवर्तित करें",
"changeemail-header": "अपना ईमेल पता परिवर्तन हेतु इसे पूरा करें। यदि आप अपना वर्तमान ईमेल पता हटाना चाहते हैं, तो इसे खाली छोड़ दें और इसे भेजें।",
- "changeemail-passwordrequired": "आपको इस परिवर्तन हेतु पासवर्ड (कूटशब्द) डालना होगा।",
"changeemail-no-info": "इस पृष्ठ का सीधे प्रयोग करने के लिए आपको लॉग इन करना होगा।",
"changeemail-oldemail": "वर्तमान ई-मेल पता:",
"changeemail-newemail": "नया ई-मेल पता:",
"minoredit": "यह एक छोटा बदलाव है",
"watchthis": "इस पृष्ठ को ध्यानसूची में डालें",
"savearticle": "पृष्ठ सहेजें",
+ "savechanges": "बदलाव सहेजें",
"publishpage": "पृष्ठ प्रकाशित करें",
+ "publishchanges": "परिवर्तन प्रकाशित करें",
"preview": "झलक",
"showpreview": "झलक दिखाएँ",
"showdiff": "बदलाव दिखाएँ",
"undo-nochange": "ऐसा लगता है कि इस सम्पादन को पहले ही पूर्ववत कर दिया गया है।",
"undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|वार्ता]]) द्वारा किए बदलाव $1 को पूर्ववत किया",
"undo-summary-username-hidden": "छुपाए गए सदस्य द्वारा किये बदलाव $1 को पूर्ववत किया",
- "cantcreateaccounttitle": "खाता खोल नहीं सकते",
"cantcreateaccount-text": "इस आइ॰पी पते ('''$1''') को खाता निर्मित करने से [[User:$3|$3]] ने प्रतिबंधित किया है।\n\nइसके लिये $3 ने ''$2'' कारण दिया है।",
"cantcreateaccount-range-text": "<strong>$1</strong> की श्रेणी में आने वाले आई॰पी पतों से, जिसमें आपका आई॰पी पता (<strong>$4</strong>) शामिल है, नए खातों की रचना [[User:$3|$3]] द्वारा अवरोधित की गयी है। \n\n$3 द्वारा दिया गया कारण है: \"$2\"",
"viewpagelogs": "इस पृष्ठ का लॉग देखें",
"mw-widgets-dateinput-placeholder-day": "DD-MM-YYYY",
"mw-widgets-titleinput-description-new-page": "पृष्ठ अभी मौजूद नहीं है",
"mw-widgets-titleinput-description-redirect": "$1 को अनुप्रेषित",
- "api-error-blacklisted": "कृपया कोई दूसरा विवरणात्मक शीर्षक चुनें।",
"sessionmanager-tie": "एक साथ कई अनुरोध को नहीं मिला सकता: $1",
"sessionprovider-generic": "$1 सत्र",
"sessionprovider-mediawiki-session-cookiesessionprovider": "कुकी-आधारित सत्र",
"passwordreset-emailtitle": "{{SITENAME}} հաշվի մանրամասները",
"passwordreset-emailelement": "Մասնակցային անունը՝ \n$1\n\nԺամանակավոր գաղտնաբառը՝ \n$2",
"passwordreset-emailsentemail": "Ուղարկվեց հիշեցնող էլ․ նամակ։",
- "passwordreset-emailsent-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։",
- "passwordreset-emailerror-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։ Սակայն մասնակցին ուղարկելը չհաջողվեց․",
"changeemail": "Փոխել էլ. հասցեն",
"changeemail-header": "Փոխել հաշվի էլ․ հասցեն",
"changeemail-oldemail": "Ներկա էլ․ հասցե․",
"minoredit": "Սա չնչին խմբագրում է",
"watchthis": "Հսկել այս էջը",
"savearticle": "Հիշել էջը",
+ "publishpage": "Հիշել փոփոխությունները",
+ "publishchanges": "Հիշել փոփոխությունները",
"preview": "Նախադիտում",
"showpreview": "Նախադիտել",
"showdiff": "Կատարված փոփոխությունները",
"undo-success": "Խմբագրումը կարող է հետ շրջվել։ Ստուգեք տարբերակների համեմատությունը ստորև, որպեսզի համոզվեք, որ դա է ձեզ հետաքրքրող փոփոխությունը և մատնահարեք «Հիշել էջը»՝ գործողությունն ավարտելու համար։",
"undo-failure": "Խմբագրումը չի կարող հետ շրջվել միջանկյալ խմբագրումների ընդհարման պատճառով։",
"undo-summary": "Հետ է շրջվում $1 խմբագրումը, որի հեղինակն է՝ [[Special:Contributions/$2|$2]] ([[User talk:$2|քննարկում]]) {{GENDER:$2|մասնակիցը|մասնակցուհին}}",
- "cantcreateaccounttitle": "Չհաջողվեց ստեղծել մասնակցային հաշիվ",
"cantcreateaccount-text": "Այս IP-հասցեից ('''$1''') մասնակցային հաշվի ստեղծումը արգելափակվել է [[User:$3|$3]] մասնակցի կողմից։\n\n$3 մասնակիցը տվել է հետևյալ պատճառը. ''$2''",
"viewpagelogs": "Դիտել այս էջի տեղեկամատյանները",
"nohistory": "Այս էջը չունի խմբագրումների պատմություն։",
"minoredit": "Ini adalah suntingan kecil.",
"watchthis": "Pantau halaman ini",
"savearticle": "Simpan halaman",
+ "savechanges": "Simpan perubahan",
"publishpage": "Terbitkan halaman",
"preview": "Pratayang",
"showpreview": "Lihat pratayang",
"publishchanges": "Ipablaak dagiti binaliwan",
"preview": "Ipadas",
"showpreview": "Ipakita ti ipadas",
- "showdiff": "Ipakita dagiti sinukatan",
+ "showdiff": "Ipakita dagiti binaliwan",
"blankarticle": "<strong>Ballaag:</strong> Ti panid a parpatuatem ket blanko.\nNo pindutem manen ti \"{{int:savearticle}}\", ti panid ket mapartuatto nga awan ti ania man a linaon.",
"anoneditwarning": "<strong>Ballaag:</strong> Saanka a nakastrek. Ti IP a pagtaengan ket publikonto a makita nga agaramidka iti ania man a panagurnos. No <strong>[$1 sumrekka]</strong> wenno <strong>[$2 agpartuatka iti pakabilangan]</strong>, dagiti inurnosmo ket maitunosto iti naganmo nga agar-aramat, ken dagiti dadduma pay a pagimbagan.",
"anonpreviewwarning": "<em>Saanka a nakastrek. Ti panagidulin ket agirehistro ti IP a pagtaengam kadagitoy a pakasaritaan ti panagurnos iti daytoy a panid.</em>",
"tooltip-ca-nstab-template": "Kitaen ti plantilia",
"tooltip-ca-nstab-help": "Kitaen ti panid ti tulong",
"tooltip-ca-nstab-category": "Kitaen ti panid ti kategoria",
- "tooltip-minoredit": "Markaan daytoy a kas bassit a panag-urnos",
- "tooltip-save": "Idulin dagiti sinukatam",
+ "tooltip-minoredit": "Markaan daytoy a kas bassit a panagurnos",
+ "tooltip-save": "Idulin dagiti binaliwam",
"tooltip-publish": "Ipablaak dagiti binaliwam",
- "tooltip-preview": "Ipadas dagiti sinukatam, pangngaasi nga usarem daytoy sakbay nga idulin ti panid!",
- "tooltip-diff": "Ipakita no ania dagiti sinukatan nga inaramidmo iti testo",
+ "tooltip-preview": "Ipadas dagiti binaliwam. Pangngaasi nga usaren daytoy sakbay nga idulin ti panid.",
+ "tooltip-diff": "Ipakita no ania dagiti binaliwan nga inaramidmo iti teksto",
"tooltip-compareselectedversions": "Kitaen ti naggidiatan dagiti dua a napili a bersion iti daytoy a panid.",
"tooltip-watch": "Inayon daytoy a panid iti listaan ti bambantayam",
"tooltip-watchlistedit-normal-submit": "Ikkaten dagiti titulo",
"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",
"minoredit": "Þetta er minniháttar breyting",
"watchthis": "Vakta þessa síðu",
"savearticle": "Vista síðu",
+ "savechanges": "Vista breytingar",
"publishpage": "Gefa út síðu",
"publishchanges": "Gefa út breytingar",
"preview": "Forskoða",
"filerevert-submit": "Ripristina",
"filerevert-success": "'''Il file [[Media:$1|$1]]''' è stato ripristinato alla [$4 versione del $2, $3].",
"filerevert-badversion": "Non esistono versioni locali precedenti del file con il timestamp richiesto.",
+ "filerevert-identical": "La versione attuale del file è già identica a quella selezionata.",
"filedelete": "Cancella $1",
"filedelete-legend": "Cancella il file",
"filedelete-intro": "Stai per cancellare il file '''[[Media:$1|$1]]''' con tutta la sua cronologia.",
"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": "記録の削除",
"tog-watchmoves": "Wuwuh kaca lan barkas lih-lihanku nyang pawawanganku",
"tog-watchdeletion": "Wuwuh kaca lan barkas busakanku nyang pawawanganku",
"tog-watchuploads": "Wuwuh barkas anyar unggahanku nyang pawawanganku",
- "tog-watchrollback": "Wuwuh kaca sing tak wurungaké nyang pawawanganku",
+ "tog-watchrollback": "Wuwuh kaca sing takpulihaké nyang pawawanganku",
"tog-minordefault": "Tengeri kabèh besutan minangka besutan cilik sacara baku",
"tog-previewontop": "Deleng pratuduh sadurungé mbesut kothak",
"tog-previewonfirst": "Delelng pratuduh nalika mbesut pisanan",
"about": "Bab",
"article": "Kaca isi",
"newwindow": "(buka mawa jendhéla anyar)",
- "cancel": "Wurungaké",
+ "cancel": "Wurung",
"moredotdotdot": "Liyané...",
"morenotlisted": "Pratélan iki ora jangkep.",
"mypage": "Kaca",
"viewyourtext": "Sampéyan bisa ndeleng lan nyalin sumbering <strong>besutaning sampéyan</strong> ing kaca iki.",
"protectedinterface": "Kaca iki isiné tèks antarmuka sing dienggo software lan wis dikunci kanggo menghindari kasalahan.",
"editinginterface": "'''Pènget:''' Panjenengan nyunting kaca sing dianggo nyedyakaké tèks antarmuka kanggo piranti alus.\nPangowahan kaca iki bakal awèh pangaruh marang tampilan antarmuka panganggo kanggoné panganggo liya.\nKanggo terjemahan, mangga nganggo [https://translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], proyèk lokalisasi MediaWiki.",
- "translateinterface": "Kanggo nambah utawa ngowah pertalan kanggo kabèh wiki, mangga anggoa [https://translatewiki.net/ translatewiki.net] minangka proyèk palokaling MediaWiki.",
+ "translateinterface": "Saperlu nambah utawa ngowah pertalan tumrap kabèh wiki, mangga anggoa [https://translatewiki.net/ translatewiki.net] minangka proyèk panglokaling MediaWiki.",
"cascadeprotected": "Kaca iki wis direksa saka panyuntingan amerga disertakaké ing {{PLURAL:$1|kaca|kaca-kaca}} ngisor iki sing wis direksa mawa opsi \"runtun\" diaktifaké:\n$2",
"namespaceprotected": "Panjenengan ora kagungan idin kanggo nyunting kaca ing bilik nama '''$1'''.",
"customcssprotected": "Sampéyan ora dililakaké nyunting kaca CSS iki amarga kaisi pangaturan pribadi saka panganggo liya.",
"cannotchangeemail": "Alamat layang èlèktronik akun ora bisa diganti nèng wiki iki.",
"emaildisabled": "Situs iki ora bisa ngirim layang èlèktronik.",
"accountcreated": "Akun wis kagawé",
- "accountcreatedtext": "Akun panganggo kanggo [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|wicara]]) wis digawé.",
+ "accountcreatedtext": "Akun panganggo [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|rembug]]) wis digawé.",
"createaccount-title": "Gawé rékening kanggo {{SITENAME}}",
"createaccount-text": "Ana wong sing nggawé sawijining akun utawa rékening kanggo alamat e-mail panjenengan ing {{SITENAME}} ($4) mawa jeneng \"$2\" lan tembung sandi \"$3\". Panjenengan disaranaké kanggo mlebu log lan ngganti tembung sandi panjenengan saiki.\n\nPanjenengan bisa nglirwakaké pesen iki yèn akun utawa rékening iki digawé déné sawijining kaluputan.",
"login-throttled": "Panjenengan wis kakèhan njajal mlebu log.\nTulung nunggu dhisik $1 sadurungé njajal manèh.",
"minoredit": "Iki besutan cilik",
"watchthis": "Awasi kaca iki",
"savearticle": "Simpen kaca",
+ "savechanges": "Simpen owahan",
"publishpage": "Babar kaca",
"publishchanges": "Babar owahan",
"preview": "Pratuduh",
"page_last": "pungkasan",
"histlegend": "Kanggo mbandhingaké: tandhani kothak radhio révisi-révisi sing arep dibandhingaké lan pencèt ''Enter'' utawa tombol sing ana ing ngisor.<br />\nLegéndha: <strong>({{int:cur}})</strong> = béda karo révisi pungkasan, <strong>({{int:last}})</strong> = béda karo révisi sadurungé, <strong>{{int:minoreditletter}}</strong> = besutan cilik.",
"history-fieldset-title": "Luru sujarah",
- "history-show-deleted": "Namung sing dibusak",
- "histfirst": "suwé dhéwé",
+ "history-show-deleted": "Mligi sing dibusak",
+ "histfirst": "lawas dhéwé",
"histlast": "anyar dhéwé",
"historysize": "($1 {{PLURAL:$1|bét|bét}})",
"historyempty": "(suwung)",
"lineno": "Larik $1:",
"compareselectedversions": "Bandhingna vèrsi kapilih",
"showhideselectedversions": "Tampilaké/dhelikaké révisi kapilih",
- "editundo": "wurungaké",
+ "editundo": "wurung",
"diff-empty": "(Ora ana bedane)",
"diff-multi-sameuser": "({{PLURAL:$1|Saowahan madya|$1 owahan madya}} déning panganggo sing padha ora dituduhaké)",
"diff-multi-manyusers": "({{PLURAL:$1Siji rèvisi sedhengan|$1 rèvisi sedhengan}} déning luwih saka $2 {{PLURAL:$2|panganggo|panganggo}} ora dituduhaké)",
"searchmenu-exists": "'''Ana kaca kanthi jeneng \"[[$1]]\" ing wiki iki'''",
"searchmenu-new": "<strong>Gawéa kaca \"[[:$1]]\" nyang wiki iki!</strong> {{PLURAL:$2|0=|Uga delenga kaca sing katemu sarana panggolèking sampéyan.|Uga delenga kasiling panggolèk.}}",
"searchprofile-articles": "Kaca isi",
- "searchprofile-images": "Sarwamadya",
+ "searchprofile-images": "Multimédhia",
"searchprofile-everything": "Samubarang",
"searchprofile-advanced": "Lungidan",
"searchprofile-articles-tooltip": "Golèkan ing $1",
"yourvariant": "Werna basa isi:",
"prefs-help-variant": "Varian utawa ortograpi sing Sampéyan pilih kanggo nampilaké kaca kontèn saka wiki iki.",
"yournick": "Asma sesinglon/samaran (kagem tapak asta):",
- "prefs-help-signature": "Komentar ing kaca wicara kudu ditapak astani nganggo \"<nowiki>~~~~</nowiki>\" sing bakal dikonvèrsi dadi tapak asta panjenengan lan tanggal wektu.",
+ "prefs-help-signature": "Tanggapan ing kaca parembugan kudu ditandhatangani mawa \"<nowiki>~~~~</nowiki>\", sing bakal salin dadi tandha tangan lan cap wektumu.",
"badsig": "Tapak astanipun klèntu; cèk rambu HTML.",
"badsiglength": "Tapak asta panjenengan kedawan.\nAja luwih saka {{PLURAL:$1|karakter|karakter}}.",
"yourgender": "Kepiyé sampéyan medhar priangganing sampéyan?",
"grant-delete": "Busak kaca, owahan, lan isian cathetan",
"newuserlogpage": "Log naraguna anyar",
"newuserlogpagetext": "Ing ngisor iki kapacak log pandaftaran panganggo anyar.",
- "rightslog": "Log pangowahan hak aksès",
+ "rightslog": "Log hak panganggo",
"rightslogtext": "Ing ngisor iki kapacak log pangowahan marang hak-hak panganggo.",
"action-read": "maca kaca iki",
"action-edit": "besut kaca iki",
"action-createpage": "nggawé kaca-kaca",
- "action-createtalk": "gawé kaca wicara anyar",
+ "action-createtalk": "gawé kaca parembugan iki",
"action-createaccount": "gawé akun panganggo iki",
"action-minoredit": "tandhani iki minangka besutan cilik",
"action-move": "alihna kaca iki",
"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é.",
"statistics-header-hooks": "Statistik liya",
"statistics-articles": "Kaca-kaca isi",
"statistics-pages": "Gunggung kaca",
- "statistics-pages-desc": "Kabèh kaca ing wiki iki, klebu kaca wicara, pangalihan, lan liya-liyané.",
+ "statistics-pages-desc": "Kabèh kaca ing wiki iki, kalebu kaca parembugan, alihan, lsp.",
"statistics-files": "Berkas sing diunggahaké",
"statistics-edits": "Gunggung suntingan wiwit {{SITENAME}} diwiwiti",
"statistics-edits-average": "Rata-rata suntingan saben kaca",
"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é",
"usercreated": "{{GENDER:$3|Digawé}} $1 wanci $2",
"newpages": "Kaca anyar",
"newpages-username": "Asma panganggo:",
- "ancientpages": "Kaca-kaca langkung sepuh",
+ "ancientpages": "Kaca paling lawas",
"move": "Pindhahen",
"movethispage": "Lih kaca iki",
"unusedimagestext": "Berkas-berkas sing kapacak iki ana nanging ora dienggo ing kaca apa waé.\nTulung digatèkaké yèn situs wèb liyané mbok-menawa bisa nyambung ing sawijining berkas sacara langsung mawa URL langsung, lan berkas-berkas kaya mengkéné iku mbok-menawa ana ing daftar iki senadyan ora dienggo aktif manèh.",
"categories": "Kategori",
"categoriespagetext": "{{PLURAL:$1|kategori ing ngisor iki ngandhut|kategori ing ngisor iki ngandhut}} kaca utawa media.\n[[Special:UnusedCategories|Kategori sing ora dianggo]] ora ditampilaké ing kéné.\nDeleng uga [[Special:WantedCategories|kategori sing diperlokaké]].",
"categoriesfrom": "Tampilaké kategori-kategori diwiwiti saka:",
- "deletedcontributions": "Sumbanganing panganggo sing dibusak",
+ "deletedcontributions": "Sumbangan panganggo sing dibusak",
"deletedcontributions-title": "Sumbanganing panganggo sing dibusak",
"sp-deletedcontributions-contribs": "sumbangan",
"linksearch": "Golèkan pranala njaba",
"delete-toobig": "Kaca iki ndarbèni sajarah panyuntingan sing dawa, yaiku ngluwihi $1 {{PLURAL:$1|revision|révisi}}.\nPambusakan kaca sing kaya mangkono mau wis ora diparengaké kanggo menggak anané karusakan ing {{SITENAME}}.",
"delete-warning-toobig": "Kaca iki duwé sajarah panyuntingan sing dawa, luwih saka $1 {{PLURAL:$1|révisi|révisi}}.\nMbusak kaca iki bisa ngrusak operasi basis data ing {{SITENAME}};\nkudu ngati-ati.",
"deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
- "rollback": "Wurungaké besutan",
+ "rollback": "Pulihaké besutan",
"rollbacklink": "balèkaké",
"rollbacklinkcount": "balèkaké $1 {{PLURAL:$1|besutan|besutan}}",
"rollbacklinkcount-morethan": "balèkaké luwih saka $1 {{PLURAL:$1|suntingan|suntingan}}",
"rollbackfailed": "Pambalèkan gagal dilakoni",
"cantrollback": "Ora bisa mbalèkaké suntingan; panganggo pungkasan iku siji-sijiné penulis artikel iki.",
- "alreadyrolled": "Ora bisa mbalèkaké suntingan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); wong liya wis nyunting utawa mbalèkaké kaca artikel iku.\n\nSuntingan pungkasan dilakoni déning [[User:$3|$3]] ([[User talk:$3|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
+ "alreadyrolled": "Ora bisa mulihaké besutan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|rembug]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); ana wong liya sing wis mbesut utawa mulihaké kaca iki.\n\nBesutan pungkasan kaca iku garapané [[User:$3|$3]] ([[User talk:$3|rembug]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
"editcomment": "Ringkesan suntingan yaiku: <em>$1</em>.",
"revertpage": "Besutan sing dibalèkaké [[Special:Contributions/$2|$2]] ([[User talk:$2|rembugan]]) bab owahan pungkasan déning [[User:$1|$1]]",
"revertpage-nouser": "Suntingan déning panganggo sing didhelikake, dibalèkaké nèng benahan pungkasan déning [[User:$1|$1]]",
"tooltip-namespace_association": "Centhang kothak iki kanggo nglebokaké uga bilik jeneng gumenan utawa subyèk sing kakait karo bilik jeneng kapilih",
"blanknamespace": "(Pokok)",
"contributions": "Sumbangan {{GENDER:$1|panganggo}}",
- "contributions-title": "Sumbanganing panganggo $1",
+ "contributions-title": "Sumbangan panganggo $1",
"mycontris": "Sumbangan",
"anoncontribs": "Sumbangan",
"contribsub2": "Kanggo {{GENDER:$3|$1}} ($2)",
"sp-contributions-deleted": "sumbanganing panganggo sing dibusak",
"sp-contributions-uploads": "unggahan",
"sp-contributions-logs": "log",
- "sp-contributions-talk": "wicara",
+ "sp-contributions-talk": "rembug",
"sp-contributions-userrights": "pengaturan hak panganggo",
"sp-contributions-blocked-notice": "Panganggo iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
"sp-contributions-blocked-notice-anon": "Alamat IP iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
"sp-contributions-search": "Golèk sumbangan",
"sp-contributions-username": "Alamat IP utawa jeneng panganggo:",
- "sp-contributions-toponly": "Tuduhaké was suntingan saka benahan pungkasan",
+ "sp-contributions-toponly": "Tuduhaké besutan mligi rèvisi anyar",
+ "sp-contributions-newonly": "Tuduhaké besutan mligi kaca gawéan",
"sp-contributions-submit": "Golèk",
"whatlinkshere": "Sing nggayut mréné",
"whatlinkshere-title": "Kaca mawa pranala nggayut \"$1\"",
"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.",
"tooltip-recreate": "Gawéa kaca iki manèh senadyan tau dibusak",
"tooltip-upload": "Miwiti pangunggahan",
"tooltip-rollback": "Balèkaké besutan-besutan kaca iki déning sing pungkasan nyumbang sarana saklikan.",
- "tooltip-undo": "\"Wurungaké\" mbalèkaké besutan iki lan mbukak blangko besutan sarana modhe pratuduh. Alesan kena diwuwuhaké ing babagan ringkesan.",
+ "tooltip-undo": "\"Wurung\" mbalèkaké besutan iki lan mbukak blangko besutan sarana modhe pratuduh. Alesan kena diwuwuhaké ing babagan ringkesan.",
"tooltip-preferences-save": "Simpen préperensi",
"tooltip-summary": "Isi tingkesan cendhak",
"anonymous": "{{PLURAL:$1|Panganggo|panganggo}} anon ing {{SITENAME}}.",
"logentry-newusers-create2": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1",
"logentry-newusers-byemail": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1 lan tembung sandhine dikirim lewat layang elektronik",
"logentry-newusers-autocreate": "Akun $1 {{GENDER:$2|digawé}} otomatis",
+ "logentry-protect-unprotect": "$1 {{GENDER:$2|ngilangi}} rereksan saka $3",
"logentry-rights-rights": "$1 {{GENDER:$2|ngganti}} golongané {{GENDER:$6|$3}} saka $4 dadi $5",
"logentry-rights-rights-legacy": "$1 {{GENDER:$2|ngganti}} golongané $3",
"logentry-rights-autopromote": "$1 otomatis {{GENDER:$2|dipromosikne}} saka $4 nèng $5",
"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": "ამ გვერდისთვის სარეგისტრაციო ჟურნალების ჩვენება",
"right-managechangetags": "[[Special:Tags|ტეგების]] შექმნა და (დე)აქტივაცია",
"right-applychangetags": "[[Special:Tags|tags]] მიღება თქვენ ცვლილებებთან ერთად",
"right-changetags": "თვითნებური [[Special:Tags|tags]] დამატება ან წაშლა ცალკეულ ცვლილებებსა და ჟურნალის ჩანაწერებში",
+ "right-deletechangetags": "მონაცემთა ბაზიდან [[Special:Tags|ტეგების]] წაშლა",
"grant-generic": "\"$1\" უფლებები",
"grant-group-page-interaction": "კავშირი გვერდებთან",
"grant-group-file-interaction": "კავშირი მედია-ფაილებთან",
"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": "ამ გვერდის ისტორიის ნახვა",
"action-managechangetags": "ტეგების შექმნა და (დე)აქტივაცია",
"action-applychangetags": "ტეგების მიღება თქვენ ცვლილებებთან ერთად",
"action-changetags": "თავისუფალი ტეგების დამატება და წაშლა ცალკეულ ცვლილებებსა და ჟურნალების ჩანაწერებში",
+ "action-deletechangetags": "მონაცემთა ბაზიდან ტეგების წაშლა",
+ "action-purge": "ამ გვერდის წაშლა",
"nchanges": "$1 ცვლილება",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ბოლო ვიზიტის შემდეგ}}",
"enhancedrc-history": "ისტორია",
"upload-http-error": "მოხდა HTTP შეცდომა: $1",
"upload-copy-upload-invalid-domain": "ამ დომენში ატვირთვების კოპირება არ არის ხელმისაწვდომი.",
"upload-foreign-cant-upload": "ეს ვიკი არ არის დაკონფიგურირებული იმისათვის, რომ ატვირთოს ფაილების უცხო ფაილთა საწყობში.",
+ "upload-dialog-disabled": "ფაილის ატვირთვა ამ დიალოგური ფანჯრით გათიშულია ამ ვიკიზე.",
"upload-dialog-title": "ფაილის ატვირთვა",
"upload-dialog-button-cancel": "გაუქმება",
"upload-dialog-button-done": "შესრულდა",
"uploadstash-badtoken": "მითითებული მოქმედება ვერ შესრულდა. შესაძლოა, რედაქტირების უფლებამოსილების მოქმედების ვადა ამოიწურა. გთხოვთ, სცადეთ თავიდან.",
"uploadstash-errclear": "ფაილების გასუფთავება ვერ მოხერხდა.",
"uploadstash-refresh": "ფაილების სიის განახლება",
+ "uploadstash-thumbnail": "მინიატურის ნახვა",
"invalid-chunk-offset": "არასწორი საწყისი წერტილი",
"img-auth-accessdenied": "მოქმედება აკრძალულია",
"img-auth-nopathinfo": "დაკარგულია PATH_INFO.\nთქვენი სერვერი არ არის მომართული ამ ინფორმაციის გადასაცემად.\nშესაძლოა, ის მუშაობს CGI-ის ბაზაზე და არ გააჩნია img_auth მხარდაჭერა.\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization იხილეთ სურათის ავტორიზაცია.]",
"watchnologin": "რეგისტრაცია ვერ შესრულდა",
"addwatch": "კონტროლის სიაში დამატება",
"addedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
+ "addedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
"addedwatchtext-short": "გვერდი „$1“ დაემატა თქვენი კონტროლის სიას.",
"removewatch": "კონტროლის სიიდან წაშლა",
"removedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
+ "removedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
"removedwatchtext-short": "გვერდი „$1“ წაიშალა თქვენი კონტროლის სიიდან.",
"watch": "კონტროლი",
"watchthispage": "ამ გვერდის კონტროლი",
"rollbacklinkcount": "$1 {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
"rollbacklinkcount-morethan": "$1-ზე მეტი {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
"rollbackfailed": "შეცდომა გაუქმებისას",
+ "rollback-missingparam": "აკლია საჭირო პარამეტრები.",
"cantrollback": "შეუძლებელია უწინდელი რედაქციის აღდგენა; ის, ვინც უკანასკნელი ცვლილებები შეიტანა, ამ სტატიის ერთადერთი ავტორია.",
"alreadyrolled": "შეუძლებელია ბოლო ცვლილების გაუქმება [[:$1]], გაკეებული [[User:$2|$2]] ([[User talk:$2|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nვიღაცა სხვამ უკვე შეასწორა ან გაააუქმა ეს გვერდი.\n\nბოლო ცვლილებები შეიტანა [[User:$3|$3]] ([[User talk:$3|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
"editcomment": "რედაქტირება განმარტებული იყო როგორც: <em>$1</em>.",
"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": "კარგი",
+ "confirm-rollback-top": "დავაბრუნოთ რედაქტირებები ამ გვერდზე?",
"semicolon-separator": "; ",
"comma-separator": ", ",
"colon-separator": ": ",
"tags-delete-not-found": "აღნიშვნა „$1“ არ არსებობს.",
"tags-delete-too-many-uses": "ტეგი \"$1\" მიღებულია $2 ვერსიებთან, რაც იმას ნიშნავს, რომ იგი არ შეიძლება იყოს წაშლილი",
"tags-delete-warnings-after-delete": "ტეგი „$1“ წაიშალა, თუმცა აღმოჩენილია შემდეგი {{PLURAL:$2|შეტყობინება|შეტყობინებები}}:",
+ "tags-delete-no-permission": "თქვენ არ გაქვთ შეცვლილი ტეგების წაშლის უფლება.",
"tags-activate-title": "ტეგის გააქტიურება",
"tags-activate-question": "თქვენ ცდილობთ დასათაურების გააქტიურებას „$1“.",
"tags-activate-reason": "მიზეზი:",
"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",
"feedback-useragent": "მომხმარებლის აგენტი:",
"searchsuggest-search": "ძიება",
"searchsuggest-containing": "შეიცავს...",
+ "api-error-autoblocked": "თქვენი IP მისამართი ავტომატურად დაიბლოკა, რადგან ის გამოიყენა დაბლოკილმა მომხმარებელმა.",
"api-error-badaccess-groups": "თქვენ არ გაქვთ ამ ვიკიში ფაილების ატვირთვის უფლება.",
"api-error-badtoken": "შიდა შეცდომა: ცუდი ტოკენი.",
+ "api-error-blocked": "თქვენთვის რედაქტირება დაბლოკილია.",
"api-error-copyuploaddisabled": "ამ სერვერზე URL-მისამართის საშუალებით ატვირთვა გამორთულია.",
"api-error-duplicate": "საიტზე უკვე {{PLURAL:$1|არსებობს სხვა ფაილი|არსებობს სხვა ფაილები}} ანალოგიური შინაარსით.",
"api-error-duplicate-archive": "საიტზე ადრე {{PLURAL:$1|უკვე იყო ფაილი}} ანალოგიური შინაარსით, მაგრამ {{PLURAL:$1|ის წაიშალა|ისინი წაიშალა}}.",
"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-delete-delete": "გვერდის წაშლა",
+ "log-action-filter-delete-restore": "გვერდის აღდგენა",
+ "log-action-filter-delete-event": "ჯურნალის ჩანაწერის წაშლა",
+ "log-action-filter-import-interwiki": "Transwiki-ს იმპორტი",
+ "log-action-filter-import-upload": "XML ატვირთვიდან იმპორტი",
+ "log-action-filter-managetags-create": "ტეგის შექმნა",
+ "log-action-filter-managetags-delete": "ტეგის წაშლა",
+ "log-action-filter-managetags-activate": "ტეგის აქტივაცია",
+ "log-action-filter-managetags-deactivate": "ტეგის დეაქტივაცია",
+ "log-action-filter-move-move": "გადატანა გადამისამართებების გადაწერის გარეშე",
+ "log-action-filter-move-move_redir": "გადატანა გადამისამართებების გადაწერით",
+ "log-action-filter-newusers-create": "შექმნა ანონიმური მომხმარებლის მიერ",
+ "log-action-filter-newusers-create2": "დარეგისტრირებული მომხმარებლის შექმნა",
+ "log-action-filter-newusers-autocreate": "ავტომატური შექმნა",
+ "log-action-filter-newusers-byemail": "პაროლით შექმნა, რომელიც გამოიგზავნა იმეილით",
+ "log-action-filter-patrol-autopatrol": "ავტომატური შემოწმება",
+ "log-action-filter-protect-protect": "დაცვა",
+ "log-action-filter-protect-modify": "დაცვის შეცვლა",
+ "log-action-filter-protect-unprotect": "დაცვის მოხსნა",
+ "log-action-filter-protect-move_prot": "დაცვა გადატანისაგან",
+ "log-action-filter-rights-autopromote": "ავტომატური შეცვლა",
+ "log-action-filter-upload-upload": "ახალი ატვირთვა",
+ "log-action-filter-upload-overwrite": "ხელახლა ატვირთვა",
+ "authmanager-authplugin-setpass-failed-title": "პაროლის ცვლილება ვერ განხორციელდა",
+ "authmanager-authplugin-setpass-failed-message": "აუთენთიფიკაციის პლაგინმა უარყო პაროლის ცვლილება.",
+ "authmanager-authplugin-create-fail": "აუთენთიფიკაციის პლაგინმა უარყო ანგარიშის შექმნა.",
+ "authmanager-authplugin-setpass-denied": "აუთენთიფიკაციის პლაგინი არ იძლევა პაროლების გამოცვლის უფლებას.",
+ "authmanager-authplugin-setpass-bad-domain": "არასწორი დომეინი.",
+ "authmanager-autocreate-noperm": "ავტომატური ანგარიშის შექმნა არ არის ნებადართული.",
+ "authmanager-autocreate-exception": "ავტომატური ანგარიშის შექმნა დროებით გათიშულია ადრინდელი ხარვეზების გამო.",
+ "authmanager-userdoesnotexist": "მომხმარებლის ანგარიში „$1“ არ არის რეგისტრირებული",
+ "authmanager-username-help": "მომხმარებლის სახელი აუთენთიფიკაციისთვის.",
+ "authmanager-password-help": "პაროლი აუთენთიფიკაციისთვის.",
+ "authmanager-domain-help": "დომეინი გარე აუთენთიფიკაციისთვის.",
+ "authmanager-retype-help": "დასადასტურებლად კვლავ შეიყვანეთ პაროლი.",
+ "authmanager-email-label": "ელ. ფოსტა",
+ "authmanager-email-help": "ელ. ფოსტის მისამართი",
+ "authmanager-realname-label": "ნამდვილი სახელი",
+ "authmanager-realname-help": "მომხმარებლის ნამდვილი სახელი",
+ "authmanager-provider-password": "პაროლზე დაფუძნებული აუთენთიფიკაცია",
+ "authmanager-provider-password-domain": "პაროლზე და დომეინზე დაფუძნებული აუთენთიფიკაცია",
+ "authmanager-provider-temporarypassword": "დროებითი პაროლი",
+ "authprovider-resetpass-skip-label": "გამოტოვება",
+ "authprovider-resetpass-skip-help": "გამოტოვეთ პაროლის შეცვლის პროცესი.",
+ "authform-nosession-login": "ავტორიზაციამ წარმატებით ჩაიარა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+ "authform-nosession-signup": "ანგარიში შეიქმნა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+ "authform-newtoken": "არ არის ტოკენი. $1",
+ "authform-notoken": "არ არის ტოკენი",
+ "authform-wrongtoken": "არასწორი ტოკენი",
+ "specialpage-securitylevel-not-allowed-title": "არ არის ნებადართული",
+ "specialpage-securitylevel-not-allowed": "უკაცრავად, თქვე არ შეგიძლიათ ამ გვერდის გამოყენება, რადგან თქვენი იდენთიფიკაცია არ არის დადასტურებული.",
+ "authpage-cannot-login": "შეუძლებელია ავტორიზაციის დაწყება.",
+ "authpage-cannot-login-continue": "შეუძლებელია ავტორიზაციის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+ "authpage-cannot-create": "შეუძლებელია ანგარიშის შექმნის დაწყება.",
+ "authpage-cannot-create-continue": "შეუძლებელია ანგარიშის შექმნის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+ "authpage-cannot-link": "შეუძლებელია ანგარიშის დაკავშირების დაწყება.",
+ "authpage-cannot-link-continue": "შეუძლებელია ანგარიშის დაკავშირების გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+ "cannotauth-not-allowed-title": "ნებართვა უარყოფილია",
+ "cannotauth-not-allowed": "თქვენ არ გაქვთ ამ გვერდის გამოყენების უფლება",
+ "changecredentials": "მონაცემების შეცვლა",
+ "changecredentials-submit": "მონაცემების შეცვლა",
+ "changecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+ "changecredentials-success": "თქვენი მონაცემები შეიცვალა.",
+ "removecredentials": "მონაცემების წაშლა",
+ "removecredentials-submit": "მონაცემების წაშლა",
+ "removecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+ "removecredentials-success": "თქვენი მონაცემები წაიშალა.",
+ "credentialsform-provider": "მონაცემის ტიპი:",
+ "credentialsform-account": "ანგარიშის სახელი:",
+ "cannotlink-no-provider-title": "არ არის დაკავშირებული ანგარიშები",
+ "cannotlink-no-provider": "არ არის დაკავშირებული ანგარიშები.",
+ "linkaccounts": "ანგარიშების დაკავშირება",
+ "linkaccounts-success-text": "ანგარიში დაკავშირებულია.",
+ "linkaccounts-submit": "ანგარიშების დაკავშირება",
+ "unlinkaccounts": "ანგარიშებისთვის დაკავშირების მოშორება",
+ "unlinkaccounts-success": "ანგარიშს მოეხსნა დაკავშირება."
}
"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.",
"filerevert-submit": "되돌리기",
"filerevert-success": "'''[[Media:$1|$1]]''' 파일을 [$4 $2 $3 버전]으로 되돌렸습니다.",
"filerevert-badversion": "입력된 시간 기록을 가진 파일의 로컬 버전이 없습니다.",
+ "filerevert-identical": "파일의 현재 버전은 선택한 것과 이미 동일합니다.",
"filedelete": "$1 삭제하기",
"filedelete-legend": "파일 삭제하기",
"filedelete-intro": "'''[[Media:$1|$1]]''' 파일과 모든 역사를 삭제합니다.",
"filerevert-submit": "Zrécksetzen",
"filerevert-success": "<strong>[[Media:$1|$1]]</strong> gouf op d'[$4 Versioun vum $2, $3 Auer] zréckgesat.",
"filerevert-badversion": "Et gëtt keng vireg lokal Versioun vun deem Fichier mat der Zäitinformatioun déi Dir uginn hutt.",
+ "filerevert-identical": "Déi aktuell Versioun vum Fichier ass identesch mat där erausgesichter.",
"filedelete": "Läsch \"$1\"",
"filedelete-legend": "Fichier läschen",
"filedelete-intro": "Dir läscht de Fichier '''[[Media:$1|$1]]''' mat all senge Versiounen (Historique).",
"specialpages-group-changes": "Rezent Ännerungen a Lëschten",
"specialpages-group-media": "Medie-Rapporten an eropgeluede Fichieren",
"specialpages-group-users": "Benotzer a Rechter",
- "specialpages-group-highuse": "Dacks benotzte Säiten",
+ "specialpages-group-highuse": "Dacks benotzt Säiten",
"specialpages-group-pages": "Lëschte vu Säiten",
"specialpages-group-pagetools": "Handwierksgeschir fir Säiten",
"specialpages-group-wiki": "Daten an Handwierksgeschir",
"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",
"backend-fail-connect": "Не можев да се поврзам со складишната основа „$1“.",
"backend-fail-internal": "Се појави непозната грешка во складишната основа „$1“.",
"backend-fail-contenttype": "Не можев да утврдам каква содржина има податотеката што треба да ја складирам во „$1“.",
- "backend-fail-batchsize": "Складишната основа доби блок од $1 податочна {{PLURAL:$1|операција|операции}}, а ограничувањето е $2 {{PLURAL:$2|операција|операции}}.",
+ "backend-fail-batchsize": "Складишната основа доби блок од $1 {{PLURAL:$1|податотечна постапка|податотечни постапки}}, а ограничувањето е $2 {{PLURAL:$2|постапка|постапки}}.",
"backend-fail-usable": "Не можев да ја прочитам или запишам податотеката „$1“ бидејќи немате доволно дозволи или поради тоа што недостасуваат именици/содржатели.",
"filejournal-fail-dbconnect": "Не можев да се поврзам со дневничката база за складишната основа „$1“.",
"filejournal-fail-dbquery": "Не можев да ја подновам дневничката база за складишната основа „$1“.",
"filerevert-submit": "Врати",
"filerevert-success": "'''[[Media:$1|$1]]''' е вратен на [$4 верзијата од $3, $2].",
"filerevert-badversion": "Нема претходна месна верзија на оваа податотека со даденото време.",
+ "filerevert-identical": "Тековната верзија на податотеката е веќе истоветна на избраната.",
"filedelete": "Избриши го $1",
"filedelete-legend": "Избриши податотека",
"filedelete-intro": "Ја бришете податотеката '''[[Media:$1|$1]]''' заедно со нејзината историја.",
"unit-pixel": "п",
"confirm_purge_button": "OK",
"confirm-purge-top": "Да го исчистам меѓускладот на страницава?",
- "confirm-purge-bottom": "Со оваа опеÑ\80аÑ\86иÑ\98а се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
+ "confirm-purge-bottom": "Со оваа поÑ\81Ñ\82апка се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
"confirm-watch-button": "ОК",
"confirm-watch-top": "Да ја додадам страницава во набљудуваните?",
"confirm-unwatch-button": "ОК",
"filerevert-submit": "पूर्वपदास न्या",
"filerevert-success": "[$3, $2 प्रमाणे आवर्तन $4]कडे<strong>[[Media:$1|$1]]</strong>उलटवण्यात आली.",
"filerevert-badversion": "दिलेलेल्या वेळ मापनानुसार,या संचिकेकरिता कोणतीही पूर्वीची स्थानिक आवृत्ती नाही.",
+ "filerevert-identical": "या संचिकेची सध्याची आवृत्ती ही निवड केलेल्या आवृत्तीसमच आहे.",
"filedelete": "$1 वगळा",
"filedelete-legend": "संचिका वगळा",
"filedelete-intro": "तुम्ही<strong>[[Media:$1|$1]]</strong>त्याच्या सर्व इतिहासासह,वगळण्याच्या तयारीत आहात.",
"savearticle": "Lagre siden",
"savechanges": "Lagre endringer",
"publishpage": "Publiser siden",
- "publishchanges": "Publiser endringene",
+ "publishchanges": "Publiser endringer",
"preview": "Forhåndsvisning",
"showpreview": "Forhåndsvisning",
"showdiff": "Vis endringer",
"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|लग प्रविष्टी|लग प्रविष्टीहरू}}",
"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",
"minoredit": "Småplukk",
"watchthis": "Overvak sida",
"savearticle": "Lagra sida",
+ "savechanges": "Publiser endringane",
"publishpage": "Publiser sida",
"publishchanges": "Publiser endringar",
"preview": "Førehandsvising",
"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 :",
"trackingcategories-msg": "Categoria de monitoramento",
"trackingcategories-name": "Nome da mensagem",
"trackingcategories-desc": "Critérios de inclusão de categoria",
+ "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+ "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
"noindex-category-desc": "A página não é indexada por robôs, porque possui a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está em um namespace onde a flag é permitida.",
"index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
"post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
"botpasswords-deleted-body": "O robô palavra-passe para o nome do robô \"$1\"do utilizador \"$2\" foi eliminado.",
"botpasswords-newpassword": "A nova palavra-passe para iniciar sessão com <strong>$1</strong> é <strong>$2</strong>. Por favor, recorde-se dela para futura referência.</em>",
"botpasswords-no-provider": "BotPasswordsSessionProvider não está disponível.",
+ "botpasswords-restriction-failed": "Restrições de senha de robô evitam esta autenticação.",
+ "botpasswords-invalid-name": "O nome de usuário especificado não contém o separador de senha de robô (\"$1\").",
+ "botpasswords-not-exist": "O usuário \"$1\" não possui uma senha de robô \"$2\".",
"resetpass_forbidden": "Não é possível alterar palavras-passe",
"resetpass_forbidden-reason": "As palavras-passe não podem ser alteradas: $1",
"resetpass-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
"passwordreset-emailelement": "{{GENDER:$1|Utilizador|Utilizadora}}: \n$1\n\nPalavra-passe temporária: \n$2",
"passwordreset-emailsentemail": "Se este é o endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma palavra-passe de reposição.",
"passwordreset-emailsentusername": "Se houver um endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma mensagem para redefinir a sua palavra-passe.",
+ "passwordreset-emailsent-capture2": "A redefinição da senha {{PLURAL:$1|do e-mail|dos e-mails}} foi enviada. {{PLURAL:$1|O nome de usuário e senha|A lista de nomes de usuário e senhas}} encontram-se a seguir.",
+ "passwordreset-emailerror-capture2": "O envio do correio {{GENDER:$2|ao usuário|à usuária}} falhou: $1 {{PLURAL:$3|O nome de usuário e senha são mostradas abaixo|A lista de nomes de usuários e senhas é mostrada abaixo}}.",
+ "passwordreset-nocaller": "Um interlocutor deve ser fornecido",
+ "passwordreset-nosuchcaller": "A pessoa que chama não existe: $1",
+ "passwordreset-ignored": "A redefinição de senha não foi realizada. Talvez o provedor não tenha sido configurado, sim?",
"passwordreset-invalideamil": "Correio eletrónico inválido",
"passwordreset-nodata": "Não foram fornecidos nome de utilizador(a) nem endereço de correio eletrónico",
"changeemail": "Alterar ou remover o endereço de correio eletrónico",
"apisandbox-loading-results": "A receber resultados da API...",
"apisandbox-request-url-label": "URL do pedido:",
"apisandbox-request-time": "Tempo de processamento: {{PLURAL:$1|$1 ms}}",
+ "apisandbox-results-fixtoken": "Corrija o identificador e envie-o novamente",
+ "apisandbox-results-fixtoken-fail": "Não foi possível recuperar o identificador \"$1\".",
+ "apisandbox-alert-page": "Os campos nesta página não são válidos.",
"apisandbox-alert-field": "O valor deste campo não é válido.",
"booksources": "Fontes bibliográficas",
"booksources-search-legend": "Pesquisar referências bibliográficas",
"trackingcategories-msg": "Categoria monitorada",
"trackingcategories-name": "Nome da mensagem",
"trackingcategories-desc": "Critérios de inclusão",
+ "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+ "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
"noindex-category-desc": "A página não é indexada por robôs porque contém a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está num domínio onde o estatuto é permitido.",
"index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
"post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
"rollbacklinkcount-morethan": "reverter mais do que $1 {{PLURAL:$1|edição|edições}}",
"rollbackfailed": "A reversão falhou",
"rollback-missingparam": "Faltam parâmetros obrigatórios no pedido.",
+ "rollback-missingrevision": "Não é possível carregar os dados de revisão.",
"cantrollback": "Não foi possível reverter a edição; o último contribuidor é o único autor desta página",
"alreadyrolled": "Não foi possível reverter as edições de [[:$1]] por [[User:$2|$2]] ([[User talk:$2|discussão]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguém editou ou já reverteu a página.\n\nA última edição foi de [[User:$3|$3]] ([[User talk:$3|discussão]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
"editcomment": "O resumo da edição era: <em$1</em>.",
"special-characters-group-ipa": "AFI (IPA)",
"special-characters-group-symbols": "Símbolos",
"special-characters-group-greek": "Grego",
+ "special-characters-group-greekextended": "Grego estendido",
"special-characters-group-cyrillic": "Cirílico",
"special-characters-group-arabic": "Árabe",
"special-characters-group-arabicextended": "Arábico estendido",
"mw-widgets-dateinput-placeholder-month": "AAAA-MM",
"mw-widgets-titleinput-description-new-page": "a página ainda não existe.",
"mw-widgets-titleinput-description-redirect": "redirecionar para $1",
+ "sessionmanager-tie": "Não se pode combinar múltiplas solicitações de tipos de autenticação: $1.",
"sessionprovider-generic": "Sessões $1",
"sessionprovider-mediawiki-session-cookiesessionprovider": "sessões baseadas em cookie",
"sessionprovider-nocookies": "Os cookies podem estar desativados. Certifique-se de que os cookies estão ativados e inicie novamente.",
"log-action-filter-suppress-block": "Supressão de utilizadores por bloqueio",
"log-action-filter-upload-upload": "Novo carregamento",
"log-action-filter-upload-overwrite": "Recarregar",
+ "authmanager-authn-no-primary": "As informações de identificação fornecidas não podem ser autenticadas.",
+ "authmanager-authn-autocreate-failed": "A criação automática de uma conta local falhou: $1",
"authmanager-create-disabled": "A criação de contas está desativada.",
"authmanager-create-from-login": "Para criar a sua conta, por favor, preencha os campos abaixo.",
"authmanager-authplugin-setpass-failed-title": "A alteração de palavra-passe falhou",
"authpage-cannot-login-continue": "Não é possível continuar a iniciar sessão. A sua sessão pode ter expirado.",
"authpage-cannot-create": "Não é possível iniciar a criação da conta.",
"authpage-cannot-create-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
+ "authpage-cannot-link": "Não se pode iniciar a vinculação da conta.",
+ "authpage-cannot-link-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
"cannotauth-not-allowed-title": "Permissão negada",
"cannotauth-not-allowed": "Não possui permissão para utilizar esta página",
"changecredentials": "Alterar credenciais",
"2axterix2",
"Ата",
"Matěj Suchánek",
- "Chaduvari"
+ "Chaduvari",
+ "MarcoAurelio"
]
},
"sidebar": "{{notranslate}}",
"filerevert-submit": "{{Identical|Revert}}",
"filerevert-success": "Message displayed when you succeed in reverting a version of a file.\n* $1 is the name of the media\n* $2 is a date\n* $3 is a time\n* $4 is an URL and must follow square bracket: [$4\n{{Identical|Revert}}",
"filerevert-badversion": "Used as error message.",
+ "filerevert-identical": "Used as error message.",
"filedelete": "Used as page title. Parameters:\n* $1 - file title\nSee also:\n* {{msg-mw|Filedelete-intro}}",
"filedelete-legend": "Used as fieldset label in the \"Delete file\" form.\n{{Identical|Delete file}}",
"filedelete-intro": "Used as introduction for FileDelete form. Parameters:\n* $1 - page title for file\nSee also:\n* {{msg-mw|Filedelete|page title}}",
"logentry-delete-revision": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3",
"logentry-delete-event-legacy": "{{Logentry|[[Special:Log/delete]]}}",
"logentry-delete-revision-legacy": "{{Logentry|[[Special:Log/delete]]}}",
- "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.",
+ "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.\n\n'''This is not a normal deletion log entry''' and is used only when the page is removed even from administrators view (on WMF wikis this is called 'oversight' or 'suppression'.",
"logentry-suppress-event": "{{Logentry}}\n{{Logentryparam}}\n* $5 - count of affected log events",
"logentry-suppress-revision": "{{Logentry}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3.",
"logentry-suppress-event-legacy": "{{Logentry}}",
"passwordreset-emailtext-user": "{{SITENAME}}-pi kaq $1 sutiyuq ruraqqa {{SITENAME}}-paq ($4)\nrakiqunaykipaq yaykuna rimata kutichinatam mañakurqan. Kay qatiq ruraqpa {{PLURAL:$3|rakiqunanmi|rakiqunankunam}}\nkay e-chaski imamaytayuq kachkan:\n\n$2\n\nKay mit'alla yaykuna {{PLURAL:$3|rimaqa|rimakunaqa}} kunanmanta {{PLURAL:$5|huk p'unchawpi|$5 p'unchawpi}} mawk'ayanqam.\nYaykuspayki musuq yaykuna rimaykitam akllankiman. Pi wakiykipas kayta mañakurqaptinqa,\nicha qam ñawpaq yaykuna rimaykita yuyaspayki manaña wakinchayta munaspaykiqa,\nkay willayta mana qhawaspa mana imatapas ruraspa ñawpaq yaykuna rimaykiwanmi llamk'ayta atinki.",
"passwordreset-emailelement": "Ruraqpa sutin: \n$1\n\nMit'alla yaykuna rima: \n$2",
"passwordreset-emailsentemail": "Yaykuna rimata kutichina e-chaskiqa kachasqañam.",
- "passwordreset-emailsent-capture": "Yaykuna rimata kutichina e-chaskiqa kachasqañam, kay qatiqpi rikunki.",
- "passwordreset-emailerror-capture": "{{GENDER:$2|}}Yaykuna rimata kutichina e-chaskiqa rurasqa karqan, imatachus kay qatiqpi rikunki, ichataq kachasqa kaptin pantasqam tukurqan: $1",
"changeemail": "E-chaski imamaytata wakinchay",
"changeemail-header": "Rakiqunap e-chaski imamaytanta wakinchay",
"changeemail-no-info": "Yaykunaykim tiyan kay p'anqata chiqalla aypanaykipaq.",
"minoredit": "Kayqa uchuylla hukchaymi",
"watchthis": "Kay qillqata watiqay",
"savearticle": "P'anqata waqaychay",
+ "savechanges": "Hukchasqata waqaychay",
"preview": "Manaraq waqaychaspa qhawariy",
"showpreview": "Ñawpaqta qhawallay",
"showdiff": "Hukchasqakunata rikuchiy",
"undo-norev": "Manam atinichu llamk'apusqata kutichiyta, mana kaptinmi icha qullusqa kaptinmi.",
"undo-summary": "[[Special:Contributions/$2|$2]]-pa $1 hukchasqanta kutichisqa ([[User talk:$2|rimay]])",
"undo-summary-username-hidden": "Pakasqa ruraqpa $1 nisqa musuqchasqata kutichiy",
- "cantcreateaccounttitle": "Manam atinichu rakiqunata kichayta",
"cantcreateaccount-text": "Kay IP tiyaymanta ('''$1''') rakiquna kichariyqa [[User:$3|$3]]-pa hark'asqanmi.\n\n$3-qa nirqan kayraykum: ''$2''",
"viewpagelogs": "Kay p'anqamanta hallch'akunata qhaway",
"nohistory": "Kay p'anqamantaqa manam llamk'apuy wiñay kawsay kanchu.",
"minoredit": "Aceasta este o modificare minoră",
"watchthis": "Urmărește această pagină",
"savearticle": "Salvare pagină",
+ "savechanges": "Salvează modificările",
"publishpage": "Publică pagina",
"publishchanges": "Publică modificările",
"preview": "Previzualizare",
"hide": "Скрыть",
"show": "Показать",
"minoreditletter": "м",
- "newpageletter": "н",
+ "newpageletter": "Ð\9d",
"boteditletter": "б",
"unpatrolledletter": "!",
"number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдающий участник|наблюдающих участника|наблюдающих участников}}]",
"zip-wrong-format": "Указанный файл — не ZIP-архив.",
"zip-bad": "ZIP-файл повреждён или не может быть прочитан.\nОн не может быть должным образом проверен.",
"zip-unsupported": "Этот ZIP-файл использует возможности, не поддерживаемые MediaWiki.\nОн не может быть должным образом проверен.",
- "uploadstash": "СкÑ\80Ñ\8bÑ\82наÑ\8f загÑ\80Ñ\83зка",
+ "uploadstash": "Ð\97агÑ\80Ñ\83зка во вÑ\80еменное Ñ\85Ñ\80анилиÑ\89е",
"uploadstash-summary": "Данная страница предоставляет доступ к файлам, которые были загружены (или находятся в процессе загрузки), но ещё не были опубликованы в вики. Эти файлы никому не видны, кроме загрузившего их участника.",
- "uploadstash-clear": "Очистить скрытые файлы",
- "uploadstash-nofiles": "У вас нет скрытых файлов.",
+ "uploadstash-clear": "Очистить временные файлы",
+ "uploadstash-nofiles": "У вас нет временных файлов.",
"uploadstash-badtoken": "Не удалось выполнить указанные действия. Возможно, истёк срок действия ваших учётных данных. Пожалуйста, пробуйте ещё раз.",
"uploadstash-errclear": "Очистка файлов не удалась.",
"uploadstash-refresh": "Обновить список файлов",
"uploadstash-thumbnail": "показать миниатюру",
+ "uploadstash-exception": "Не удалось сохранить загрузку во временное хранилище ($1): «$2».",
"invalid-chunk-offset": "Недопустимое смещение фрагмента",
"img-auth-accessdenied": "Доступ запрещён",
"img-auth-nopathinfo": "Отсутствует <code>PATH_INFO</code>.\nВаш сервер не настроен для передачи этих сведений.\nВозможно, он работает на основе CGI и не поддерживает <code>img_auth</code>.\nСм. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
"filerevert-submit": "Возвратить",
"filerevert-success": "'''[[Media:$1|$1]]''' был возвращён к [$4 версии от $3, $2].",
"filerevert-badversion": "Не существует предыдущей локальной версии этого файла с указанной меткой времени.",
+ "filerevert-identical": "Текущая версия файла уже идентична выбранной.",
"filedelete": "$1 — удаление",
"filedelete-legend": "Удалить файл",
"filedelete-intro": "Вы собираетесь удалить файл '''[[Media:$1|$1]]''' со всей его историей.",
"api-error-ratelimited": "Вы пытаетесь загрузить несколько файлов за более короткий промежуток времени, чем это позволено.\nПожалуйста, попробуйте ещё раз через несколько минут.",
"api-error-stashfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
"api-error-publishfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
- "api-error-stasherror": "При загрузке файла в хранилище произошла ошибка.",
+ "api-error-stasherror": "При загрузке файла во временное хранилище произошла ошибка.",
"api-error-stashedfilenotfound": "При попытке загрузить файл из временного хранилища исходный файл не найден.",
"api-error-stashpathinvalid": "Путь, по которому должен располагаться файл, загруженный во временное хранилище, некорректен.",
"api-error-stashfilestorage": "При загрузке файла во временное хранилище произошла ошибка.",
"passwordreset-capture-help": "अस्यां मञ्जूषायां यदि भवता अङ्क्यते तर्हि वि-पत्रम् (अस्थायिकूटशब्देन सह) दर्श्यते प्रेष्यते च ।",
"passwordreset-email": "वि-पत्रसङ्केतः",
"passwordreset-emailtitle": "{{SITENAME}} इत्यत्र योजकविषये",
- "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|योजकः|योजकाः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
- "passwordreset-emailtext-user": "$1 सदस्यः {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|सदस्यः|सदस्याः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विज्ञप्तिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
+ "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
+ "passwordreset-emailtext-user": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
"passwordreset-emailelement": "सदस्यनाम : \n$1\n\nअल्पकालीनकूटशब्दः : \n$2",
"passwordreset-emailsentemail": "परिवर्तितकूटशब्दस्य वि-पत्रं प्रेषितम् अस्ति ।",
"changeemail": "वि-पत्रसङ्केतः परिवर्त्यताम्",
"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": "शोधनी",
"filerevert-submit": "Vrni",
"filerevert-success": "Datoteka '''[[Media:$1|$1]]''' je bila vrnjena na [$4 različico $3, $2].",
"filerevert-badversion": "Ne najdem preteklih lokalnih verzij datoteke s podanim časovnim žigom.",
+ "filerevert-identical": "Trenutna različica datoteke je že enaka izbrani.",
"filedelete": "Izbriši $1",
"filedelete-legend": "Brisanje datoteke",
"filedelete-intro": "Brišete datoteko '''[[Media:$1|$1]]''' skupaj z njeno celotno zgodovino.",
"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",
"filerevert-submit": "Återställ",
"filerevert-success": "'''[[Media:$1|$1]]''' har återställts till [$4 versionen från $2 kl. $3].",
"filerevert-badversion": "Det finns ingen tidigare version av filen från den angivna tidpunkten.",
+ "filerevert-identical": "Den aktuella versionen av filen är redan identisk med den valda.",
"filedelete": "Radera $1",
"filedelete-legend": "Radera fil",
"filedelete-intro": "Du håller på att radera filen '''[[Media:$1|$1]]''' tillsammans med hela dess historik.",
"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",
"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": "నా మార్పులు",
"İnternion",
"Hbseren",
"Kumkumuk",
- "Basak"
+ "Basak",
+ "Ece Alpdeniz"
]
},
"tog-underline": "Bağlantıların altını çiz:",
"print": "Yazdır",
"view": "Görüntüle",
"view-foreign": "$1 üzerinde gör",
- "edit": "Değiştir",
+ "edit": "Düzenle",
"edit-local": "Yerel açıklamayı düzenle",
"create": "Oluştur",
"create-local": "Yerel açıklama ekle",
"userpage-userdoesnotexist": "Користувач під назвою \"<nowiki>$1</nowiki>\" не зареєстрований. Переконайтеся, що ви хочете створити/редагувати цю сторінку.",
"userpage-userdoesnotexist-view": "Обліковий запис користувача «$1» не зареєстровано.",
"blocked-notice-logextract": "Цей користувач наразі заблокований.\nОстанній запис у журналі блокувань такий:",
- "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Привітність & безпека → очистити дані браузера → кеш</em>",
+ "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Приватність & безпека → очистити дані браузера → кеш</em>",
"usercssyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий css-файл перед збереженням.",
"userjsyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий код JavaScript перед збереженням.",
"usercsspreview": "'''Пам'ятайте, що це лише попередній перегляд вашого css-файлу.'''\n'''Його ще не збережено!'''",
"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>.».",
"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": "براہِ کرم! تبصرہ نیچے درج کیجئے.",
"pageinfo-visiting-watchers": "تعداد ناظرین جنہوں نے حالیہ ترامیم کا مشاہدہ کیا",
"pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
"pageinfo-toolboxlink": "معلومات صفحہ",
+ "pageinfo-category-info": "زمرے کی معلومات",
+ "pageinfo-category-pages": "تعداد صفحات",
+ "pageinfo-category-subcats": "تعداد ذیلی زمرہ جات",
+ "pageinfo-category-files": "تعداد املاف",
"markaspatrolledtext": "اس صفحہ کو بطور مراجعت شدہ نشان زد کریں",
"markedaspatrollederrornotify": "بطور مراجعت نشان زد نہیں کیا جا سکا۔",
"deletedrevision": "حذف شدہ پرانی ترمیم $1۔",
"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.",
"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}}"
}
"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": "לאגביכער",
"suppressionlog": "廢止日誌",
"suppressionlogtext": "下面係刪除同埋由操作員牽涉到內容封鎖嘅一覽。\n睇吓[[Special:BlockList|封鎖一覽]]去睇現時進行緊嘅禁止同埋封鎖表。",
"mergehistory": "合併頁歷史",
- "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史版。",
+ "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史連續性。",
"mergehistory-box": "合併兩版嘅修訂:",
"mergehistory-from": "來源頁:",
"mergehistory-into": "目的頁:",
"changeemail-newemail": "新的电子邮件地址:",
"changeemail-newemail-help": "此字段应留空,如果您希望移除您的电子邮件地址的话。如果电子邮件地址被移除,您将无法重置忘记的密码,并将不会接收来自此wiki的电子邮件。",
"changeemail-none": "(无)",
- "changeemail-password": "你的{{SITENAME}}密码:",
+ "changeemail-password": "您的{{SITENAME}}密码:",
"changeemail-submit": "更改电子邮件地址",
"changeemail-throttled": "您最近尝试了太多次登录。请等待$1后再试。",
"changeemail-nochange": "请输入一个不同的新的电子邮件地址。",
"filerevert-submit": "恢复",
"filerevert-success": "<strong>[[Media:$1|$1]]</strong>已经恢复至[$4 $2 $3的版本]。",
"filerevert-badversion": "文件并无所请求时间戳下的早期本地版本。",
+ "filerevert-identical": "文件的当前版本已与选择的版本相同。",
"filedelete": "删除$1",
"filedelete-legend": "删除文件",
"filedelete-intro": "您将要删除文件<strong>[[Media:$1|$1]]</strong>及其全部历史。",
"logentry-delete-revision": "$1{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
"logentry-delete-event-legacy": "$1{{GENDER:$2|更改}}$3的日志事件的可见性",
"logentry-delete-revision-legacy": "$1{{GENDER:$2|更改}}页面$3的版本的可见性",
- "logentry-suppress-delete": "$1{{GENDER:$2|已隐藏}}页面$3",
+ "logentry-suppress-delete": "$1{{GENDER:$2|已屏蔽}}页面$3",
"logentry-suppress-event": "$1秘密地{{GENDER:$2|更改}}$3的{{PLURAL:$5|$5个日志事件}}的可见性:$4",
"logentry-suppress-revision": "$1秘密地{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
"logentry-suppress-event-legacy": "$1秘密地{{GENDER:$2|更改}}$3的日志事件的可见性",
"suppressionlog": "禁止顯示日誌",
"suppressionlogtext": "以下清單為管理員透過刪除或封鎖所隱藏的內容。\n請至 [[Special:BlockList|封鎖清單]] 取得目前已封鎖的清單。",
"mergehistory": "合併頁面歷史",
- "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改會繼續保留該頁面先前的歷史版本。",
+ "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改能夠繼續保留該頁面先前歷史版本的連續性。",
"mergehistory-box": "合併兩個頁面的修訂:",
"mergehistory-from": "來源頁面:",
"mergehistory-into": "目標頁面:",
$maintClass = false;
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* Abstract maintenance class for quickly writing and churning out
private $mDb = null;
/** @var float UNIX timestamp */
- private $lastSlaveWait = 0.0;
+ private $lastReplicationWait = 0.0;
/**
* Used when creating separate schema files.
}
+ /**
+ * Set triggers like when to try to run deferred updates
+ * @since 1.28
+ */
+ public function setTriggers() {
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ self::setLBFactoryTriggers( $lbFactory );
+ }
+
+ /**
+ * @param LBFactory $LBFactory
+ * @since 1.28
+ */
+ public static function setLBFactoryTriggers( LBFactory $LBFactory ) {
+ // Hook into period lag checks which often happen in long-running scripts
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $lbFactory->setWaitForReplicationListener(
+ __METHOD__,
+ function () {
+ global $wgCommandLineMode;
+ // Check config in case of JobRunner and unit tests
+ if ( $wgCommandLineMode ) {
+ DeferredUpdates::tryOpportunisticExecute( 'run' );
+ }
+ }
+ );
+ // Check for other windows to run them. A script may read or do a few writes
+ // to the master but mostly be writing to something else, like a file store.
+ $lbFactory->getMainLB()->setTransactionListener(
+ __METHOD__,
+ function ( $trigger ) {
+ global $wgCommandLineMode;
+ // Check config in case of JobRunner and unit tests
+ if ( $wgCommandLineMode && $trigger === IDatabase::TRIGGER_COMMIT ) {
+ DeferredUpdates::tryOpportunisticExecute( 'run' );
+ }
+ }
+ );
+ }
+
/**
* Run a child maintenance script. Pass all of the current arguments
* to it.
* If not set, wfGetDB() will be used.
* This function has the same parameters as wfGetDB()
*
- * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+ * @param integer $db DB index (DB_REPLICA/DB_MASTER)
* @param array $groups; default: empty array
* @param string|bool $wiki; default: current wiki
* @return IDatabase
}
/**
- * Commit the transcation on a DB handle and wait for slaves to catch up
+ * Commit the transcation on a DB handle and wait for replica DBs to catch up
*
* This method makes it clear that commit() is called from a maintenance script,
* which has outermost scope. This is safe, unlike $dbw->commit() called in other places.
*
* @param IDatabase $dbw
* @param string $fname Caller name
- * @return bool Whether the slave wait succeeded
+ * @return bool Whether the replica DB wait succeeded
* @since 1.27
*/
protected function commitTransaction( IDatabase $dbw, $fname ) {
$dbw->commit( $fname );
+ try {
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $lbFactory->waitForReplication(
+ [ 'timeout' => 30, 'ifWritesSince' => $this->lastReplicationWait ]
+ );
+ $this->lastReplicationWait = microtime( true );
- $ok = wfWaitForSlaves( $this->lastSlaveWait, false, '*', 30 );
- $this->lastSlaveWait = microtime( true );
-
- return $ok;
+ return true;
+ } catch ( DBReplicationWaitError $e ) {
+ return false;
+ }
}
/**
$dbr = $this->forcedDb;
if ( $this->forcedDb === null ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
}
$this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ );
$this->startTime = microtime( true );
}
$this->lb = wfGetLBFactory()->newMainLB();
- $db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+ $db = $this->lb->getConnection( DB_REPLICA, 'dump' );
// Discourage the server from disconnecting us if it takes a long time
// to read out the big ol' batch query.
* @return bool|string Revision ID, or false if not found or error
*/
function getRevIdForTime( Title $title, $timestamp ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$id = $dbr->selectField(
[ 'revision', 'page' ],
public function execute() {
$this->output( "Fetching redirects...\n" );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$result = $dbr->select(
[ 'page' ],
[ 'page_namespace', 'page_title', 'page_latest' ],
public function execute() {
$start = '';
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$numImages = 0;
$numGood = 0;
}
function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$maxUserId = 0;
do {
$this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
$found = false;
foreach ( $wgLocalDatabases as $wikiID ) {
- $dbr = $this->getDB( DB_SLAVE, [], $wikiID );
+ $dbr = $this->getDB( DB_REPLICA, [], $wikiID );
$count = $dbr->selectField( 'externallinks', 'COUNT(*)',
[ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
} else {
// Clean up spam on this wiki
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$res = $dbr->select( 'externallinks', [ 'DISTINCT el_from' ],
[ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
$count = $dbr->numRows( $res );
* @throws MWException
*/
public function runTable( $params ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
if ( array_diff( array_keys( $params ),
[ 'table', 'conds', 'index', 'callback' ] )
protected function fileExists( $name ) {
// XXX: Doesn't actually check for file existence, just presence of image record.
// This is reasonable, since cleanupImages.php only iterates over the image table.
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$row = $dbr->selectRow( 'image', [ 'img_name' ], [ 'img_name' => $name ], __METHOD__ );
return $row !== false;
// this could be done some other, more direct/efficient way, but using
// UploadStash's own methods means it's less likely to fall accidentally
// out-of-date someday
- $stash = new UploadStash( $repo );
+ $stash = new UploadStash( $repo, new User() );
$i = 0;
foreach ( $keys as $key ) {
public function execute() {
global $wgLocalDatabases, $wgMemc;
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$res = $dbr->select( 'interwiki', [ 'iw_prefix' ], false );
$prefixes = [];
foreach ( $res as $row ) {
public function execute() {
$pages = $this->getOption( 'maxpages' );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$totalsec = 0.0;
$scanned = 0;
global $wgUser;
$this->output( "Checking existence of old default messages..." );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$res = $dbr->select( [ 'page', 'revision' ],
[ 'page_namespace', 'page_title' ],
[
// 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();
}
public function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$result = $dbr->select( [ 'pagelinks', 'page' ],
[
'page_id',
// 2. The Connection, through the load balancer.
try {
- $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+ $this->db = $this->lb->getConnection( DB_REPLICA, 'dump' );
} catch ( Exception $e ) {
throw new MWException( __METHOD__
. " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
* @param bool $shared True to pass shared-dir settings to hash func
*/
function fetchUsed( $shared ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$image = $dbr->tableName( 'image' );
$imagelinks = $dbr->tableName( 'imagelinks' );
* @param bool $shared True to pass shared-dir settings to hash func
*/
function fetchLocal( $shared ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$result = $dbr->select( 'image',
[ 'img_name' ],
'',
* note that the text string itself is *not* followed by newline
*/
public function execute() {
- $db = $this->getDB( DB_SLAVE );
+ $db = $this->getDB( DB_REPLICA );
$stdin = $this->getStdin();
while ( !feof( $stdin ) ) {
$line = fgets( $stdin );
return true;
}
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$namespaces = [
NS_MEDIAWIKI => $dbr->buildLike( $dbr->anyString(), '.json' ),
NS_USER => $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString(), '.json' ),
$title = null;
}
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
// See also SpecialDoubleRedirects
$tables = [
$this->output( "Could not find registration for #$id NULL\n" );
}
}
- $this->output( "Waiting for slaves..." );
+ $this->output( "Waiting for replica DBs..." );
wfWaitForSlaves();
$this->output( " done.\n" );
} while ( $res->numRows() >= $this->mBatchSize );
public $timestamp;
/**
- * A database slave object
+ * A database replica DB object
*
* @var object
*/
$this->identifier = $this->getOption( 'identifier', wfWikiID() );
$this->compress = $this->getOption( 'compress', 'yes' ) !== 'no';
$this->skipRedirects = $this->getOption( 'skip-redirects', false ) !== false;
- $this->dbr = $this->getDB( DB_SLAVE );
+ $this->dbr = $this->getDB( DB_REPLICA );
$this->generateNamespaces();
$this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
$this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' );
--- /dev/null
+<?php
+/**
+ * Reports the hostname of a replica DB server.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Maintenance script that reports the hostname of a replica DB server.
+ *
+ * @ingroup Maintenance
+ */
+class GetSlaveServer extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( "group", "Query group to check specifically" );
+ $this->addDescription( 'Report the hostname of a replica DB server' );
+ }
+
+ public function execute() {
+ global $wgAllDBsAreLocalhost;
+ if ( $wgAllDBsAreLocalhost ) {
+ $host = 'localhost';
+ } elseif ( $this->hasOption( 'group' ) ) {
+ $db = $this->getDB( DB_REPLICA, $this->getOption( 'group' ) );
+ $host = $db->getServer();
+ } else {
+ $lb = wfGetLB();
+ $i = $lb->getReaderIndex();
+ $host = $lb->getServerName( $i );
+ }
+ $this->output( "$host\n" );
+ }
+}
+
+$maintClass = "GetSlaveServer";
+require_once RUN_MAINTENANCE_IF_MAIN;
<?php
-/**
- * Reports the hostname of a slave server.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script that reports the hostname of a slave server.
- *
- * @ingroup Maintenance
- */
-class GetSlaveServer extends Maintenance {
- public function __construct() {
- parent::__construct();
- $this->addOption( "group", "Query group to check specifically" );
- $this->addDescription( 'Report the hostname of a slave server' );
- }
-
- public function execute() {
- global $wgAllDBsAreLocalhost;
- if ( $wgAllDBsAreLocalhost ) {
- $host = 'localhost';
- } elseif ( $this->hasOption( 'group' ) ) {
- $db = $this->getDB( DB_SLAVE, $this->getOption( 'group' ) );
- $host = $db->getServer();
- } else {
- $lb = wfGetLB();
- $i = $lb->getReaderIndex();
- $host = $lb->getServerName( $i );
- }
- $this->output( "$host\n" );
- }
-}
-
-$maintClass = "GetSlaveServer";
-require_once RUN_MAINTENANCE_IF_MAIN;
+// B/C alias
+require_once ( __DIR__ . '/getReplicaServer.php' );
if ( $doProtect ) {
# Protect the file
- echo "\nWaiting for slaves...\n";
- // Wait for slaves.
+ echo "\nWaiting for replica DBs...\n";
+ // Wait for replica DBs.
sleep( 2.0 ); # Why this sleep?
wfWaitForSlaves();
parent::__construct();
$this->addOption( 'quick', 'Force the update to be done in a single query' );
$this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but
- avoids locking tables or lagging slaves with large updates;
- calculates counts on a slave if possible.
+ avoids locking tables or lagging replica DBs with large updates;
+ calculates counts on a replica DB if possible.
Background mode will be automatically used if multiple servers are listed
in the load balancer, usually indicating a replication environment.' );
if ( $backgroundMode ) {
$this->output( "Using replication-friendly background mode...\n" );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$chunkSize = 100;
$lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ );
* accidentally introduce an assumption of title validity to the code we
* are calling.
*/
- $updates = [ new LinksDeletionUpdate( $wikiPage ) ];
- DataUpdate::runUpdates( $updates );
+ DeferredUpdates::addUpdate( new LinksDeletionUpdate( $wikiPage ) );
+ DeferredUpdates::doUpdates();
return true;
}
break;
}
- // print status and let slaves catch up
+ // print status and let replica DBs catch up
$this->output( sprintf(
"id %d done (up to %d), %5.3f%% \r", $lastId, $endId, $lastId / $endId * 100 ) );
wfWaitForSlaves();
* @return int
*/
protected function doLenUpdates( $table, $idCol, $prefix, $fields ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$dbw = $this->getDB( DB_MASTER );
$start = $dbw->selectField( $table, "MIN($idCol)", false, __METHOD__ );
$end = $dbw->selectField( $table, "MAX($idCol)", false, __METHOD__ );
}
// Validate the timestamps
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$this->startTimestamp = $dbr->timestamp( $this->getOption( 'starttime' ) );
$this->endTimestamp = $dbr->timestamp( $this->getOption( 'endtime' ) );
*/
protected function purgeFromLogType( $type ) {
$repo = RepoGroup::singleton()->getLocalRepo();
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
foreach ( self::$typeMappings[$type] as $logType => $logActions ) {
$this->verbose( "Scanning for {$logType}/" . implode( ',', $logActions ) . "\n" );
}
}
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$minTime = $dbr->timestamp( $this->getOption( 'starttime' ) );
$maxTime = $dbr->timestamp( $this->getOption( 'endtime' ) );
* @param int|bool $namespace
*/
private function purgeNamespace( $namespace = false ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$startId = 0;
if ( $namespace === false ) {
$conds = [];
$this->output( "Building content page file cache from page {$start}!\n" );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$overwrite = $this->getOption( 'overwrite', false );
$start = ( $start > 0 )
? $start
$this->init( $count, $table );
$this->output( "Processing $table...\n" );
- $result = $this->getDB( DB_SLAVE )->select( $table, '*', [], __METHOD__ );
+ $result = $this->getDB( DB_REPLICA )->select( $table, '*', [], __METHOD__ );
foreach ( $result as $row ) {
$update = call_user_func( $callback, $row, null );
public function execute() {
// Rebuild the text index
- if ( $this->getDB( DB_SLAVE )->getType() != 'postgres' ) {
+ if ( $this->getDB( DB_REPLICA )->getType() != 'postgres' ) {
$this->output( "** Rebuilding fulltext search index (if you abort "
. "this will break searching; run this script again to fix):\n" );
$rebuildText = $this->runChild( 'RebuildTextIndex', 'rebuildtextindex.php' );
$end = str_replace( ' ', '_', $this->getOption( 'end', '' ) ); // page on img_name
$count = 0;
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
do {
$conds = [ "img_name > {$dbr->addQuotes( $start )}" ];
if ( strlen( $end ) ) {
$end = null, $redirectsOnly = false, $oldRedirectsOnly = false
) {
$reportingInterval = 100;
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
if ( $start === null ) {
$start = 1;
return;
}
- $updates = $content->getSecondaryDataUpdates( $page->getTitle() );
- DataUpdate::runUpdates( $updates );
+ foreach ( $content->getSecondaryDataUpdates( $page->getTitle() ) as $update ) {
+ DeferredUpdates::addUpdate( $update );
+ }
}
/**
) {
wfWaitForSlaves();
$this->output( "Deleting illegal entries from the links tables...\n" );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
do {
// Find the start of the next chunk. This is based only
// on existent page_ids.
*/
private function dfnCheckInterval( $start = null, $end = null, $batchSize = 100 ) {
$dbw = $this->getDB( DB_MASTER );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$linksTables = [ // table name => page_id field
'pagelinks' => 'pl_from',
}
public function execute() {
$this->commit = $this->hasOption( 'commit' );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$dbw = $this->getDB( DB_MASTER );
$lastId = 0;
do {
# Do an initial scan for inactive accounts and report the result
$this->output( "Checking for unused user accounts...\n" );
$del = [];
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
if ( $this->hasOption( 'ignore-groups' ) ) {
$excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
* @return bool
*/
private function isInactiveAccount( $id, $master = false ) {
- $dbo = $this->getDB( $master ? DB_MASTER : DB_SLAVE );
+ $dbo = $this->getDB( $master ? DB_MASTER : DB_REPLICA );
$checks = [
'revision' => 'rev',
'archive' => 'ar',
wfCountDown( 5 );
}
+ // We list user by user_id from one of the replica DBs
// We list user by user_id from one of the slave database
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$where = [];
if ( $this->nullsOnly ) {
* @return array
*/
private function getRollbackTitles( $user ) {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$titles = [];
$results = $dbr->select(
[ 'page', 'revision' ],
<?php
/**
- * Run a database query in batches and wait for slaves. This is used on large
+ * Run a database query in batches and wait for replica DBs. This is used on large
* wikis to prevent replication lag from going through the roof when executing
* large write queries.
*
require_once __DIR__ . '/Maintenance.php';
/**
- * Maintenance script to run a database query in batches and wait for slaves.
+ * Maintenance script to run a database query in batches and wait for replica DBs.
*
* @ingroup Maintenance
*/
public function __construct() {
parent::__construct();
$this->addDescription(
- "Run a query repeatedly until it affects 0 rows, and wait for slaves in between.\n" .
+ "Run a query repeatedly until it affects 0 rows, and wait for replica DBs in between.\n" .
"NOTE: You need to set a LIMIT clause yourself." );
}
'ss_images' => 'Number of images',
];
- // Get cached stats from slave database
- $dbr = $this->getDB( DB_SLAVE );
+ // Get cached stats from a replica DB
+ $dbr = $this->getDB( DB_REPLICA );
$stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
// Get maximum size for each column
parent::__construct();
$this->addDescription( 'Send SQL queries to a MediaWiki database. ' .
'Takes a file name containing SQL as argument or runs interactively.' );
- $this->addOption( 'query', 'Run a single query instead of running interactively', false, true );
+ $this->addOption( 'query',
+ 'Run a single query instead of running interactively', false, true );
$this->addOption( 'cluster', 'Use an external cluster by name', false, true );
- $this->addOption( 'wikidb', 'The database wiki ID to use if not the current one', false, true );
- $this->addOption( 'slave', 'Use a slave server (either "any" or by name)', false, true );
+ $this->addOption( 'wikidb',
+ 'The database wiki ID to use if not the current one', false, true );
+ $this->addOption( 'replicadb',
+ 'Replica DB server to use instead of the master DB (can be "any")', false, true );
}
public function execute() {
$lb = wfGetLB( $wiki );
}
// Figure out which server to use
- if ( $this->hasOption( 'slave' ) ) {
- $server = $this->getOption( 'slave' );
- if ( $server === 'any' ) {
- $index = DB_SLAVE;
- } else {
- $index = null;
- $serverCount = $lb->getServerCount();
- for ( $i = 0; $i < $serverCount; ++$i ) {
- if ( $lb->getServerName( $i ) === $server ) {
- $index = $i;
- break;
- }
- }
- if ( $index === null ) {
- $this->error( "No slave server configured with the name '$server'.", 1 );
+ $replicaDB = $this->getOption( 'replicadb', $this->getOption( 'slave', '' ) );
+ if ( $replicaDB === 'any' ) {
+ $index = DB_REPLICA;
+ } elseif ( $replicaDB != '' ) {
+ $index = null;
+ $serverCount = $lb->getServerCount();
+ for ( $i = 0; $i < $serverCount; ++$i ) {
+ if ( $lb->getServerName( $i ) === $replicaDB ) {
+ $index = $i;
+ break;
}
}
+ if ( $index === null ) {
+ $this->error( "No replica DB server configured with the name '$server'.", 1 );
+ }
} else {
$index = DB_MASTER;
}
// Get a DB handle (with this wiki's DB selected) from the appropriate load balancer
$db = $lb->getConnection( $index, [], $wiki );
- if ( $this->hasOption( 'slave' ) && $db->getLBInfo( 'master' ) !== null ) {
- $this->error( "The server selected ({$db->getServer()}) is not a slave.", 1 );
+ if ( $replicaDB != '' && $db->getLBInfo( 'master' ) !== null ) {
+ $this->error( "The server selected ({$db->getServer()}) is not a replica DB.", 1 );
}
if ( $this->hasArg( 0 ) ) {
];
function check( $fix = false, $xml = '' ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $fix ) {
print "Checking, will fix errors if possible...\n";
} else {
return;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$dbw = wfGetDB( DB_MASTER );
$dbr->ping();
$dbw->ping();
}
// Find text row again
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
if ( !$oldId ) {
echo "Missing revision row for rev_id $id\n";
) {
$loadStyle = self::LS_CHUNKED;
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$dbw = $this->getDB( DB_MASTER );
# Set up external storage
}
public function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$row = $dbr->selectRow(
[ 'text', 'revision' ],
[ 'old_flags', 'old_text' ],
}
function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$dbw = $this->getDB( DB_MASTER );
$dryRun = $this->getOption( 'dry-run' );
unset( $this->mapCache[$key] );
}
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$map = [];
$res = $dbr->select( 'revision',
[ 'rev_id', 'rev_text_id' ],
function moveToExternal( $cluster, $maxID, $minID = 1 ) {
$fname = 'moveToExternal';
$dbw = wfGetDB( DB_MASTER );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$count = $maxID - $minID + 1;
$blockSize = 1000;
protected function &getDB( $cluster, $groups = [], $wiki = false ) {
$lb = wfGetLBFactory()->getExternalLB( $cluster );
- return $lb->getConnection( DB_SLAVE );
+ return $lb->getConnection( DB_REPLICA );
}
public function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
if ( !$dbr->tableExists( 'blob_orphans' ) ) {
$this->error( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first", true );
}
*/
use MediaWiki\Logger\LegacyLogger;
+use MediaWiki\MediaWikiServices;
$optionsWithArgs = RecompressTracked::getOptionsWithArgs();
require __DIR__ . '/../commandLine.inc';
public $numProcs = 1;
public $numBatches = 0;
public $pageBlobClass, $orphanBlobClass;
- public $slavePipes, $slaveProcs, $prevSlaveId;
+ public $replicaPipes, $replicaProcs, $prevReplicaId;
public $copyOnly = false;
public $isChild = false;
- public $slaveId = false;
+ public $replicaId = false;
public $noCount = false;
public $debugLog, $infoLog, $criticalLog;
public $store;
private static $optionsWithArgs = [
'procs',
- 'slave-id',
+ 'replica-id',
'debug-log',
'info-log',
'critical-log'
'procs' => 'numProcs',
'copy-only' => 'copyOnly',
'child' => 'isChild',
- 'slave-id' => 'slaveId',
+ 'replica-id' => 'replicaId',
'debug-log' => 'debugLog',
'info-log' => 'infoLog',
'critical-log' => 'criticalLog',
$this->store = new ExternalStoreDB;
if ( !$this->isChild ) {
$GLOBALS['wgDebugLogPrefix'] = "RCT M: ";
- } elseif ( $this->slaveId !== false ) {
- $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->slaveId}: ";
+ } elseif ( $this->replicaId !== false ) {
+ $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->replicaId}: ";
}
$this->pageBlobClass = function_exists( 'xdiff_string_bdiff' ) ?
'DiffHistoryBlob' : 'ConcatenatedGzipHistoryBlob';
function logToFile( $msg, $file ) {
$header = '[' . date( 'd\TH:i:s' ) . '] ' . wfHostname() . ' ' . posix_getpid();
- if ( $this->slaveId !== false ) {
- $header .= "({$this->slaveId})";
+ if ( $this->replicaId !== false ) {
+ $header .= "({$this->replicaId})";
}
$header .= ' ' . wfWikiID();
LegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file );
}
/**
- * Wait until the selected slave has caught up to the master.
- * This allows us to use the slave for things that were committed in a
+ * Wait until the selected replica DB has caught up to the master.
+ * This allows us to use the replica DB for things that were committed in a
* previous part of this batch process.
*/
function syncDBs() {
$dbw = wfGetDB( DB_MASTER );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$pos = $dbw->getMasterPos();
$dbr->masterPosWait( $pos, 100000 );
}
}
$this->syncDBs();
- $this->startSlaveProcs();
+ $this->startReplicaProcs();
$this->doAllPages();
$this->doAllOrphans();
- $this->killSlaveProcs();
+ $this->killReplicaProcs();
}
/**
* @return bool
*/
function checkTrackingTable() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( !$dbr->tableExists( 'blob_tracking' ) ) {
$this->critical( "Error: blob_tracking table does not exist" );
* This necessary because text recompression is slow: loading, compressing and
* writing are all slow.
*/
- function startSlaveProcs() {
+ function startReplicaProcs() {
$cmd = 'php ' . wfEscapeShellArg( __FILE__ );
foreach ( self::$cmdLineOptionMap as $cmdOption => $classOption ) {
- if ( $cmdOption == 'slave-id' ) {
+ if ( $cmdOption == 'replica-id' ) {
continue;
} elseif ( in_array( $cmdOption, self::$optionsWithArgs ) && isset( $this->$classOption ) ) {
$cmd .= " --$cmdOption " . wfEscapeShellArg( $this->$classOption );
' --wiki ' . wfEscapeShellArg( wfWikiID() ) .
' ' . call_user_func_array( 'wfEscapeShellArg', $this->destClusters );
- $this->slavePipes = $this->slaveProcs = [];
+ $this->replicaPipes = $this->replicaProcs = [];
for ( $i = 0; $i < $this->numProcs; $i++ ) {
$pipes = [];
$spec = [
[ 'file', 'php://stderr', 'w' ]
];
MediaWiki\suppressWarnings();
- $proc = proc_open( "$cmd --slave-id $i", $spec, $pipes );
+ $proc = proc_open( "$cmd --replica-id $i", $spec, $pipes );
MediaWiki\restoreWarnings();
if ( !$proc ) {
- $this->critical( "Error opening slave process: $cmd" );
+ $this->critical( "Error opening replica DB process: $cmd" );
exit( 1 );
}
- $this->slaveProcs[$i] = $proc;
- $this->slavePipes[$i] = $pipes[0];
+ $this->replicaProcs[$i] = $proc;
+ $this->replicaPipes[$i] = $pipes[0];
}
- $this->prevSlaveId = -1;
+ $this->prevReplicaId = -1;
}
/**
* Gracefully terminate the child processes
*/
- function killSlaveProcs() {
- $this->info( "Waiting for slave processes to finish..." );
+ function killReplicaProcs() {
+ $this->info( "Waiting for replica DB processes to finish..." );
for ( $i = 0; $i < $this->numProcs; $i++ ) {
- $this->dispatchToSlave( $i, 'quit' );
+ $this->dispatchToReplica( $i, 'quit' );
}
for ( $i = 0; $i < $this->numProcs; $i++ ) {
- $status = proc_close( $this->slaveProcs[$i] );
+ $status = proc_close( $this->replicaProcs[$i] );
if ( $status ) {
$this->critical( "Warning: child #$i exited with status $status" );
}
}
/**
- * Dispatch a command to the next available slave.
- * This may block until a slave finishes its work and becomes available.
+ * Dispatch a command to the next available replica DB.
+ * This may block until a replica DB finishes its work and becomes available.
*/
function dispatch( /*...*/ ) {
$args = func_get_args();
- $pipes = $this->slavePipes;
+ $pipes = $this->replicaPipes;
$numPipes = stream_select( $x = [], $pipes, $y = [], 3600 );
if ( !$numPipes ) {
- $this->critical( "Error waiting to write to slaves. Aborting" );
+ $this->critical( "Error waiting to write to replica DBs. Aborting" );
exit( 1 );
}
for ( $i = 0; $i < $this->numProcs; $i++ ) {
- $slaveId = ( $i + $this->prevSlaveId + 1 ) % $this->numProcs;
- if ( isset( $pipes[$slaveId] ) ) {
- $this->prevSlaveId = $slaveId;
- $this->dispatchToSlave( $slaveId, $args );
+ $replicaId = ( $i + $this->prevReplicaId + 1 ) % $this->numProcs;
+ if ( isset( $pipes[$replicaId] ) ) {
+ $this->prevReplicaId = $replicaId;
+ $this->dispatchToReplica( $replicaId, $args );
return;
}
}
/**
- * Dispatch a command to a specified slave
- * @param int $slaveId
+ * Dispatch a command to a specified replica DB
+ * @param int $replicaId
* @param array|string $args
*/
- function dispatchToSlave( $slaveId, $args ) {
+ function dispatchToReplica( $replicaId, $args ) {
$args = (array)$args;
$cmd = implode( ' ', $args );
- fwrite( $this->slavePipes[$slaveId], "$cmd\n" );
+ fwrite( $this->replicaPipes[$replicaId], "$cmd\n" );
}
/**
* Move all tracked pages to the new clusters
*/
function doAllPages() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$i = 0;
$startId = 0;
if ( $this->noCount ) {
if ( $current == $end || $this->numBatches >= $this->reportingInterval ) {
$this->numBatches = 0;
$this->info( "$label: $current / $end" );
- wfWaitForSlaves();
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
}
}
* Move all orphan text to the new clusters
*/
function doAllOrphans() {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$startId = 0;
$i = 0;
if ( $this->noCount ) {
case 'quit':
return;
}
- wfWaitForSlaves();
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
}
}
} else {
$titleText = '[deleted]';
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Finish any incomplete transactions
if ( !$this->copyOnly ) {
$startId = 0;
$trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
while ( true ) {
$res = $dbr->select(
[ 'blob_tracking', 'text' ],
$this->debug( "$titleText: committing blob with " . $trx->getSize() . " items" );
$trx->commit();
$trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
- wfWaitForSlaves();
+ $lbFactory->waitForReplication();
}
}
}
* @param array $conds
*/
function finishIncompleteMoves( $conds ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$startId = 0;
$conds = array_merge( $conds, [
$startId = $row->bt_text_id;
$this->moveTextRow( $row->bt_text_id, $row->bt_new_url );
if ( $row->bt_text_id % 10 == 0 ) {
- wfWaitForSlaves();
+ $lbFactory->waitForReplication();
}
}
}
$trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
- $res = wfGetDB( DB_SLAVE )->select(
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $res = wfGetDB( DB_REPLICA )->select(
[ 'text', 'blob_tracking' ],
[ 'old_id', 'old_text', 'old_flags' ],
[
$this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
$trx->commit();
$trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
- wfWaitForSlaves();
+ $lbFactory->waitForReplication();
}
}
$this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
/* Check to see if the target text_ids have been moved already.
*
- * We originally read from the slave, so this can happen when a single
+ * We originally read from the replica DB, so this can happen when a single
* text_id is shared between multiple pages. It's rare, but possible
* if a delete/move/undelete cycle splits up a null edit.
*
function resolveStubs() {
$fname = 'resolveStubs';
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$maxID = $dbr->selectField( 'text', 'MAX(old_id)', false, $fname );
$blockSize = 10000;
$numBlocks = intval( $maxID / $blockSize ) + 1;
$stub = unserialize( $stubText );
$flags = explode( ',', $flags );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$dbw = wfGetDB( DB_MASTER );
if ( strtolower( get_class( $stub ) ) !== 'historyblobstub' ) {
class StorageTypeStats extends Maintenance {
function execute() {
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
if ( !$endId ) {
}
$type = isset( $options['type'] ) ? $options['type'] : 'ConcatenatedGzipHistoryBlob';
-$dbr = $this->getDB( DB_SLAVE );
+$dbr = $this->getDB( DB_REPLICA );
$res = $dbr->select(
[ 'page', 'revision', 'text' ],
'*',
function checkIntegrity() {
echo "Doing integrity check...\n";
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Scan for HistoryBlobStub objects in the text table (bug 20757)
function getTextClause() {
if ( !$this->textClause ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->textClause = '';
foreach ( $this->clusters as $cluster ) {
if ( $this->textClause != '' ) {
*/
function trackRevisions() {
$dbw = wfGetDB( DB_MASTER );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$textClause = $this->getTextClause();
$startId = 0;
* archive table counts as orphan for our purposes.
*/
function trackOrphanText() {
- # Wait until the blob_tracking table is available in the slave
+ # Wait until the blob_tracking table is available in the replica DB
$dbw = wfGetDB( DB_MASTER );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$pos = $dbw->getMasterPos();
$dbr->masterPosWait( $pos, 100000 );
echo "Searching for orphan blobs in $cluster...\n";
$lb = wfGetLBFactory()->getExternalLB( $cluster );
try {
- $extDB = $lb->getConnection( DB_SLAVE );
+ $extDB = $lb->getConnection( DB_REPLICA );
} catch ( DBConnectionError $e ) {
if ( strpos( $e->error, 'Unknown database' ) !== false ) {
echo "No database on $cluster\n";
class TidyUpBug37714 extends Maintenance {
public function execute() {
// Search for all log entries which are about changing the visability of other log entries.
- $result = $this->getDB( DB_SLAVE )->select(
+ $result = $this->getDB( DB_REPLICA )->select(
'logging',
[ 'log_id', 'log_params' ],
[
foreach ( $result as $row ) {
$ids = explode( ',', explode( "\n", $row->log_params )[0] );
- $result = $this->getDB( DB_SLAVE )->select( // Work out what log entries were changed here.
+ $result = $this->getDB( DB_REPLICA )->select( // Work out what log entries were changed here.
'logging',
'log_type',
[ 'log_id' => $ids ],
if ( $this->hasOption( 'use-master' ) ) {
$dbr = $this->getDB( DB_MASTER );
} else {
- $dbr = $this->getDB( DB_SLAVE, 'vslow' );
+ $dbr = $this->getDB( DB_REPLICA, 'vslow' );
}
$counter = new SiteStatsInit( $dbr );
$result = $counter->articles();
*/
class UpdateCollation extends Maintenance {
const BATCH_SIZE = 100; // Number of rows to process in one batch
- const SYNC_INTERVAL = 5; // Wait for slaves after this many batches
+ const SYNC_INTERVAL = 5; // Wait for replica DBs after this many batches
public $sizeHistogram = [];
global $wgCategoryCollation;
$dbw = $this->getDB( DB_MASTER );
- $dbr = $this->getDB( DB_SLAVE );
+ $dbr = $this->getDB( DB_REPLICA );
$force = $this->getOption( 'force' );
$dryRun = $this->getOption( 'dry-run' );
$verboseStats = $this->getOption( 'verbose-stats' );
$this->output( "$count done.\n" );
if ( !$dryRun && ++$batchCount % self::SYNC_INTERVAL == 0 ) {
- $this->output( "Waiting for slaves ... " );
+ $this->output( "Waiting for replica DBs ... " );
wfWaitForSlaves();
$this->output( "done\n" );
}
} while ( !wfGetLB()->pingAll() );
$this->output( "Reconnected\n\n" );
}
- # Wait for the slave to catch up
+ # Wait for the replica DB to catch up
wfWaitForSlaves();
} else {
$this->output( "cheap, skipped\n" );
$this->output( $minutes . 'm ' );
}
$this->output( sprintf( "%.2fs\n", $seconds ) );
- # Wait for the slave to catch up
+ # Wait for the replica DB to catch up
wfWaitForSlaves();
}
}
$ret = [];
$defaultOptions = User::getDefaultOptions();
- // We list user by user_id from one of the slave database
- $dbr = wfGetDB( DB_SLAVE );
+ // We list user by user_id from one of the replica DBs
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->select( 'user',
[ 'user_id' ],
[],
private function CHANGER() {
$this->warn();
- // We list user by user_id from one of the slave database
- $dbr = wfGetDB( DB_SLAVE );
+ // We list user by user_id from one of the replica DBs
+ $dbr = wfGetDB( DB_REPLICA );
$result = $dbr->select( 'user',
[ 'user_id' ],
[],
],
'jquery.ui.position' => [
'deprecated' => true,
+ 'targets' => [ 'mobile', 'desktop' ],
'scripts' => 'resources/lib/jquery.ui/jquery.ui.position.js',
'group' => 'jquery.ui',
],
'mediawiki.notification' => [
'styles' => [
'resources/src/mediawiki/mediawiki.notification.common.css',
- 'resources/src/mediawiki/mediawiki.notification.hideForPrint.css'
+ 'resources/src/mediawiki/mediawiki.notification.print.css'
=> [ 'media' => 'print' ],
],
'skinStyles' => [
],
'mediawiki.page.gallery.styles' => [
'styles' => [
- 'resources/src/mediawiki/page/gallery-print.css' => [ 'media' => 'print' ],
+ 'resources/src/mediawiki/page/gallery.print.css' => [ 'media' => 'print' ],
'resources/src/mediawiki/page/gallery.css',
],
'position' => 'top',
if ( error.message ) {
return this.upload.getApi()
.then( function ( api ) {
- return api.loadMessagesIfMissing( [ error.message.key ] ).then( function () {
- if ( !mw.message( error.message.key ).exists() ) {
- return $.Deferred().reject();
- }
- return new OO.ui.Error(
- $( '<p>' ).msg( error.message.key, error.message.params || [] ),
- { recoverable: false }
- );
- } );
+ // 'amenableparser' will expand templates and parser functions server-side.
+ // We still do the rest of wikitext parsing here (throught jqueryMsg).
+ return api.loadMessagesIfMissing( [ error.message.key ], { amenableparser: true } )
+ .then( function () {
+ if ( !mw.message( error.message.key ).exists() ) {
+ return $.Deferred().reject();
+ }
+ return new OO.ui.Error(
+ $( '<p>' ).msg( error.message.key, error.message.params || [] ),
+ { recoverable: false }
+ );
+ } );
} )
.then( null, function () {
// We failed when loading the error message, or it doesn't actually exist, fall back
// Force jQuery behaviour to be for crossDomain. Otherwise jQuery would use
// XHR for a same domain request instead of <script>, which changes the request
// headers (potentially missing a cache hit), and reduces caching in general
- // since browsers cache XHR much less (if at all). And XHR means we retreive
+ // since browsers cache XHR much less (if at all). And XHR means we retrieve
// text, so we'd need to $.globalEval, which then messes up line numbers.
crossDomain: true,
cache: true
}
}
+ /**
+ * Evaluate a batch of load.php responses retrieved from mw.loader.store.
+ *
+ * @private
+ * @param {string[]} implementations Array containing pieces of JavaScript code in the
+ * form of calls to mw.loader#implement().
+ * @param {Function} cb Callback in case of failure
+ * @param {Error} cb.err
+ */
+ function batchEval( implementations, cb ) {
+ if ( !implementations.length ) {
+ return;
+ }
+ mw.requestIdleCallback( function iterate( deadline ) {
+ while ( implementations[ 0 ] && deadline.timeRemaining() > 5 ) {
+ try {
+ $.globalEval( implementations.shift() );
+ } catch ( err ) {
+ cb( err );
+ return;
+ }
+ }
+ if ( implementations[ 0 ] ) {
+ mw.requestIdleCallback( iterate );
+ }
+ } );
+ }
+
/* Public Members */
return {
/**
* @protected
*/
work: function () {
- var q, batch, concatSource, origBatch;
+ var q, batch, implementations, sourceModules;
batch = [];
}
}
+ // Now that the queue has been processed into a batch, clear the queue.
+ // This MUST happen before we initiate any eval or network request. Otherwise,
+ // it is possible for a cached script to instantly trigger the same work queue
+ // again; all before we've cleared it causing each request to include modules
+ // which are already loaded.
+ queue = [];
+
+ if ( !batch.length ) {
+ return;
+ }
+
mw.loader.store.init();
if ( mw.loader.store.enabled ) {
- concatSource = [];
- origBatch = batch;
+ implementations = [];
+ sourceModules = [];
batch = $.grep( batch, function ( module ) {
- var source = mw.loader.store.get( module );
- if ( source ) {
- concatSource.push( source );
+ var implementation = mw.loader.store.get( module );
+ if ( implementation ) {
+ implementations.push( implementation );
+ sourceModules.push( module );
return false;
}
return true;
} );
- try {
- $.globalEval( concatSource.join( ';' ) );
- } catch ( err ) {
+ batchEval( implementations, function ( err ) {
// Not good, the cached mw.loader.implement calls failed! This should
// never happen, barring ResourceLoader bugs, browser bugs and PEBKACs.
// Depending on how corrupt the string is, it is likely that some
// modules' implement() succeeded while the ones after the error will
// never run and leave their modules in the 'loading' state forever.
-
// Since this is an error not caused by an individual module but by
// something that infected the implement call itself, don't take any
// risks and clear everything in this cache.
mw.loader.store.clear();
- // Re-add the ones still pending back to the batch and let the server
- // repopulate these modules to the cache.
- // This means that at most one module will be useless (the one that had
- // the error) instead of all of them.
mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
- origBatch = $.grep( origBatch, function ( module ) {
+
+ // Re-add the failed ones that are still pending back to the batch
+ var failed = $.grep( sourceModules, function ( module ) {
return registry[ module ].state === 'loading';
} );
- batch = batch.concat( origBatch );
- }
+ batchRequest( failed );
+ } );
}
- // Now that the queue has been processed into a batch, clear up the queue.
- // This MUST happen before we initiate any network request. Else it's possible
- // that a script will be locally cached, instantly load, and work the queue
- // again; all before we've cleared it causing each request to include modules
- // which are already loaded.
- queue = [];
-
batchRequest( batch );
},
+++ /dev/null
-.mw-notification-area {
- display: none;
-}
--- /dev/null
+.mw-notification-area {
+ display: none;
+}
*/
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.
*
+++ /dev/null
-li.gallerybox {
- vertical-align: top;
- display: inline-block;
-}
-
-ul.gallery, li.gallerybox {
- zoom: 1;
- *display: inline;
-}
-
-ul.gallery {
- margin: 2px;
- padding: 2px;
- display: block;
-}
-
-li.gallerycaption {
- font-weight: bold;
- text-align: center;
- display: block;
- word-wrap: break-word;
-}
-
-li.gallerybox div.thumb {
- text-align: center;
- border: 1px solid #ccc;
- margin: 2px;
-}
-
-div.gallerytext {
- overflow: hidden;
- font-size: 94%;
- padding: 2px 4px;
- word-wrap: break-word;
-}
/* Galleries */
/* These display attributes look nonsensical, but are needed to support IE and FF2 */
-/* Don't forget to update gallery-print.css */
+/* Don't forget to update gallery.print.css */
li.gallerybox {
vertical-align: top;
display: -moz-inline-box;
--- /dev/null
+li.gallerybox {
+ vertical-align: top;
+ display: inline-block;
+}
+
+ul.gallery, li.gallerybox {
+ zoom: 1;
+ *display: inline;
+}
+
+ul.gallery {
+ margin: 2px;
+ padding: 2px;
+ display: block;
+}
+
+li.gallerycaption {
+ font-weight: bold;
+ text-align: center;
+ display: block;
+ word-wrap: break-word;
+}
+
+li.gallerybox div.thumb {
+ text-align: center;
+ border: 1px solid #ccc;
+ margin: 2px;
+}
+
+div.gallerytext {
+ overflow: hidden;
+ font-size: 94%;
+ padding: 2px 4px;
+ word-wrap: break-word;
+}
DeferredUpdates::clearPendingUpdates();
ObjectCache::getMainWANInstance()->clearProcessCache();
+ // XXX: reset maintenance triggers
+ // Hook into period lag checks which often happen in long-running scripts
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ Maintenance::setLBFactoryTriggers( $lbFactory );
+
ob_start( 'MediaWikiTestCase::wfResetOutputBuffersBarrier' );
}
wfGetDB( DB_MASTER )->commit( __METHOD__ );
+ $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
+
if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
$latest = $page->getLatest();
$page->doDeleteArticleReal( $pageTitle );
[
'<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'
],
[
'<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'
],
$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 ==
* This is a non DBMS depending test.
*/
class DatabaseSQLTest extends MediaWikiTestCase {
-
- /**
- * @var DatabaseTestHelper
- */
+ /** @var DatabaseTestHelper */
private $database;
protected function setUp() {
parent::setUp();
- $this->database = new DatabaseTestHelper( __CLASS__ );
+ $this->database = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => true ] );
}
protected function assertLastSql( $sqlText ) {
);
}
+ protected function assertLastSqlDb( $sqlText, $db ) {
+ $this->assertEquals( $db->getLastSqls(), $sqlText );
+ }
+
/**
* @dataProvider provideSelect
* @covers DatabaseBase::select
* @dataProvider provideInsertSelect
* @covers DatabaseBase::insertSelect
*/
- public function testInsertSelect( $sql, $sqlText ) {
+ public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
$this->database->insertSelect(
$sql['destTable'],
$sql['srcTable'],
isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
);
- $this->assertLastSql( $sqlText );
+ $this->assertLastSql( $sqlTextNative );
+
+ $dbWeb = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => false ] );
+ $dbWeb->forceNextResult( [
+ array_flip( array_keys( $sql['varMap'] ) )
+ ] );
+ $dbWeb->insertSelect(
+ $sql['destTable'],
+ $sql['srcTable'],
+ $sql['varMap'],
+ $sql['conds'],
+ __METHOD__,
+ isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
+ isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
+ );
+ $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
}
public static function provideInsertSelect() {
"INSERT INTO insert_table " .
"(field_insert,field) " .
"SELECT field_select,field2 " .
- "FROM select_table"
+ "FROM select_table",
+ "SELECT field_select AS field_insert,field2 AS field " .
+ "FROM select_table WHERE * FOR UPDATE",
+ "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
],
[
[
"(field_insert,field) " .
"SELECT field_select,field2 " .
"FROM select_table " .
- "WHERE field = '2'"
+ "WHERE field = '2'",
+ "SELECT field_select AS field_insert,field2 AS field FROM " .
+ "select_table WHERE field = '2' FOR UPDATE",
+ "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
],
[
[
"SELECT field_select,field2 " .
"FROM select_table " .
"WHERE field = '2' " .
- "ORDER BY field"
+ "ORDER BY field",
+ "SELECT field_select AS field_insert,field2 AS field " .
+ "FROM select_table WHERE field = '2' ORDER BY field FOR UPDATE",
+ "INSERT IGNORE INTO insert_table (field_insert,field) VALUES ('0','1')"
],
];
}
$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;
*/
protected $lastSqls = [];
+ /** @var array List of row arrays */
+ protected $nextResult = [];
+
/**
* Array of tables to be considered as existing by tableExist()
* Use setExistingTables() to alter.
*/
protected $tablesExists;
- public function __construct( $testName ) {
+ public function __construct( $testName, array $opts = [] ) {
$this->testName = $testName;
$this->profiler = new ProfilerStub( [] );
$this->trxProfiler = new TransactionProfiler();
+ $this->cliMode = isset( $opts['cliMode'] ) ? $opts['cliMode'] : true;
}
/**
$this->tablesExists = (array)$tablesExists;
}
+ /**
+ * @param mixed $res Use an array of row arrays to set row result
+ */
+ public function forceNextResult( $res ) {
+ $this->nextResult = $res;
+ }
+
protected function addSql( $sql ) {
// clean up spaces before and after some words and the whole string
$this->lastSqls[] = trim( preg_replace(
}
protected function doQuery( $sql ) {
- return [];
+ $res = $this->nextResult;
+ $this->nextResult = [];
+
+ return new FakeResultWrapper( $res );
}
}
$wgDBserver, $dbw->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
$dbr = $lb->getConnection( DB_SLAVE );
- $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+ $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
$this->assertEquals(
$wgDBserver, $dbr->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
$this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
$dbr = $lb->getConnection( DB_SLAVE );
- $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+ $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
$factory->shutdown();
$lb->closeAll();
$this->setMwGlobals( 'wgCommandLineMode', false );
$updates = [
- '1' => 'deferred update 1',
- '2' => 'deferred update 2',
- '3' => 'deferred update 3',
- '2-1' => 'deferred update 1 within deferred update 2',
+ '1' => "deferred update 1;\n",
+ '2' => "deferred update 2;\n",
+ '2-1' => "deferred update 1 within deferred update 2;\n",
+ '2-2' => "deferred update 2 within deferred update 2;\n",
+ '3' => "deferred update 3;\n",
+ '3-1' => "deferred update 1 within deferred update 3;\n",
+ '3-2' => "deferred update 2 within deferred update 3;\n",
+ '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+ '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
];
DeferredUpdates::addCallableUpdate(
function () use ( $updates ) {
echo $updates['2-1'];
}
);
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['2-2'];
+ }
+ );
}
);
DeferredUpdates::addCallableUpdate(
function () use ( $updates ) {
- echo $updates[3];
+ echo $updates['3'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-1'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-1-1'];
+ }
+ );
+ }
+ );
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-2'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-2-1'];
+ }
+ );
+ }
+ );
}
);
+ $this->assertEquals( 3, DeferredUpdates::pendingUpdatesCount() );
+
$this->expectOutputString( implode( '', $updates ) );
DeferredUpdates::doUpdates();
public function testDoUpdatesCLI() {
$this->setMwGlobals( 'wgCommandLineMode', true );
-
$updates = [
- '1' => 'deferred update 1',
- '2' => 'deferred update 2',
- '2-1' => 'deferred update 1 within deferred update 2',
- '3' => 'deferred update 3',
+ '1' => "deferred update 1;\n",
+ '2' => "deferred update 2;\n",
+ '2-1' => "deferred update 1 within deferred update 2;\n",
+ '2-2' => "deferred update 2 within deferred update 2;\n",
+ '3' => "deferred update 3;\n",
+ '3-1' => "deferred update 1 within deferred update 3;\n",
+ '3-2' => "deferred update 2 within deferred update 3;\n",
+ '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+ '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
];
+
+ wfGetLBFactory()->commitMasterChanges( __METHOD__ ); // clear anything
+
DeferredUpdates::addCallableUpdate(
function () use ( $updates ) {
echo $updates['1'];
echo $updates['2-1'];
}
);
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['2-2'];
+ }
+ );
}
);
DeferredUpdates::addCallableUpdate(
function () use ( $updates ) {
- echo $updates[3];
+ echo $updates['3'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-1'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-1-1'];
+ }
+ );
+ }
+ );
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-2'];
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $updates ) {
+ echo $updates['3-2-1'];
+ }
+ );
+ }
+ );
}
);
) {
$update = new LinksUpdate( $title, $parserOutput );
- // NOTE: make sure LinksUpdate does not generate warnings when called inside a transaction.
- $update->beginTransaction();
$update->doUpdate();
- $update->commitTransaction();
$this->assertSelect( $table, $fields, $condition, $expectedRows );
return $update;
] ],
[ [
'supportsBucketing' => true,
- 'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' + rand() ],
+ 'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' . rand() ],
'thumbnailBucket' => 1024,
'physicalWidth' => 2048,
'expectedPath' => 'fsFilePath',
return [
// $data, $sampleRate, $seed, $expectWrite
- [ $unsampled, 1, 0 /*0.44*/, $unsampled ],
- [ $sampled, 1, 0 /*0.44*/, null ],
- [ $sampled, 1, 4 /*0.03*/, $sampled ],
- [ $unsampled, 0.1, 4 /*0.03*/, $sampled ],
- [ $sampled, 0.5, 0 /*0.44*/, null ],
- [ $sampled, 0.5, 4 /*0.03*/, $sampled ],
+ [ $unsampled, 1, 0 /*0.44*/, true ],
+ [ $sampled, 1, 0 /*0.44*/, false ],
+ [ $sampled, 1, 4 /*0.03*/, true ],
+ [ $unsampled, 0.1, 0 /*0.44*/, false ],
+ [ $sampled, 0.5, 0 /*0.44*/, false ],
+ [ $sampled, 0.5, 4 /*0.03*/, false ],
];
}
+
+ public function testSetSamplingRates() {
+ $matching = new StatsdData();
+ $matching->setKey( 'foo.bar' );
+ $matching->setValue( 1 );
+
+ $nonMatching = new StatsdData();
+ $nonMatching->setKey( 'oof.bar' );
+ $nonMatching->setValue( 1 );
+
+ $sender = $this->getMock( 'Liuggio\StatsdClient\Sender\SenderInterface' );
+ $sender->expects( $this->any() )->method( 'open' )->will( $this->returnValue( true ) );
+ $sender->expects( $this->once() )->method( 'write' )->with( $this->anything(),
+ $this->equalTo( $nonMatching ) );
+
+ $client = new SamplingStatsdClient( $sender );
+ $client->setSamplingRates( [ 'foo.*' => 0.2 ] );
+
+ mt_srand( 0 ); // next random is 0.44
+ $client->send( $matching );
+ mt_srand( 0 );
+ $client->send( $nonMatching );
+ }
}
--- /dev/null
+<?php
+
+class WaitConditionLoopFakeTime extends WaitConditionLoop {
+ protected $wallClock = 1;
+
+ function __construct( callable $condition, $timeout, array $busyCallbacks ) {
+ parent::__construct( $condition, $timeout, $busyCallbacks );
+ }
+
+ function usleep( $microseconds ) {
+ $this->wallClock += $microseconds / 1e6;
+ }
+
+ function getCpuTime() {
+ return 0.0;
+ }
+
+ function getWallTime() {
+ return $this->wallClock;
+ }
+
+ public function setWallClock( &$timestamp ) {
+ $this->wallClock =& $timestamp;
+ }
+}
+
+class WaitConditionLoopTest extends PHPUnit_Framework_TestCase {
+ public function testCallbackReached() {
+ $wallClock = microtime( true );
+
+ $count = 0;
+ $status = new StatusValue();
+ $loop = new WaitConditionLoopFakeTime(
+ function () use ( &$count, $status ) {
+ ++$count;
+ $status->value = 'cookie';
+
+ return WaitConditionLoop::CONDITION_REACHED;
+ },
+ 10.0,
+ $this->newBusyWork( $x, $y, $z )
+ );
+ $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+ $this->assertEquals( 1, $count );
+ $this->assertEquals( 'cookie', $status->value );
+ $this->assertEquals( [ 0, 0, 0 ], [ $x, $y, $z ], "No busy work done" );
+
+ $count = 0;
+ $loop = new WaitConditionLoopFakeTime(
+ function () use ( &$count, &$wallClock ) {
+ $wallClock += 1;
+ ++$count;
+
+ return $count >= 2 ? WaitConditionLoop::CONDITION_REACHED : false;
+ },
+ 7.0,
+ $this->newBusyWork( $x, $y, $z, $wallClock )
+ );
+ $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke(),
+ "Busy work did not cause timeout" );
+ $this->assertEquals( [ 1, 0, 0 ], [ $x, $y, $z ] );
+
+ $count = 0;
+ $loop = new WaitConditionLoopFakeTime(
+ function () use ( &$count, &$wallClock ) {
+ $wallClock += .1;
+ ++$count;
+
+ return $count > 80 ? true : false;
+ },
+ 50.0,
+ $this->newBusyWork( $x, $y, $z, $wallClock, $dontCallMe, $badCalls )
+ );
+ $this->assertEquals( 0, $badCalls, "Callback exception not yet called" );
+ $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+ $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+ $this->assertEquals( 1, $badCalls, "Bad callback ran and was exception caught" );
+
+ try {
+ $e = null;
+ $dontCallMe();
+ } catch ( Exception $e ) {
+ }
+
+ $this->assertInstanceOf( 'RunTimeException', $e );
+ $this->assertEquals( 1, $badCalls, "Callback exception cached" );
+ }
+
+ public function testCallbackTimeout() {
+ $count = 0;
+ $wallClock = microtime( true );
+ $loop = new WaitConditionLoopFakeTime(
+ function () use ( &$count, &$wallClock ) {
+ $wallClock += 3;
+ ++$count;
+
+ return $count > 300 ? true : false;
+ },
+ 50.0,
+ $this->newBusyWork( $x, $y, $z, $wallClock )
+ );
+ $loop->setWallClock( $wallClock );
+ $this->assertEquals( $loop::CONDITION_TIMED_OUT, $loop->invoke() );
+ $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+ }
+
+ public function testCallbackAborted() {
+ $x = 0;
+ $wallClock = microtime( true );
+ $loop = new WaitConditionLoopFakeTime(
+ function () use ( &$x, &$wallClock ) {
+ $wallClock += 2;
+ ++$x;
+
+ return $x > 2 ? WaitConditionLoop::CONDITION_ABORTED : false;
+ },
+ 10.0,
+ $this->newBusyWork( $x, $y, $z, $wallClock )
+ );
+ $loop->setWallClock( $wallClock );
+ $this->assertEquals( $loop::CONDITION_ABORTED, $loop->invoke() );
+ }
+
+ private function newBusyWork(
+ &$x, &$y, &$z, &$wallClock = 1, &$dontCallMe = null, &$badCalls = 0
+ ) {
+ $x = $y = $z = 0;
+ $badCalls = 0;
+
+ $list = [];
+ $list[] = function () use ( &$x, &$wallClock ) {
+ $wallClock += 1;
+
+ return ++$x;
+ };
+ $dontCallMe = function () use ( &$badCalls ) {
+ ++$badCalls;
+ throw new RuntimeException( "TrollyMcTrollFace" );
+ };
+ $list[] =& $dontCallMe;
+ $list[] = function () use ( &$y, &$wallClock ) {
+ $wallClock += 15;
+
+ return ++$y;
+ };
+ $list[] = function () use ( &$z, &$wallClock ) {
+ $wallClock += 0.1;
+
+ return ++$z;
+ };
+
+ return $list;
+ }
+}
private static function removeSomeHtml( $html ) {
$html = str_replace( '"', '"', $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 ) {
* @todo give this test a real name explaining what is being tested here
*/
public function testBug29408() {
- $this->setMwGlobals( 'wgUser', self::$users['uploader']->getUser() );
-
$repo = RepoGroup::singleton()->getLocalRepo();
- $stash = new UploadStash( $repo );
+ $stash = new UploadStash( $repo, self::$users['uploader']->getUser() );
// Throws exception caught by PHPUnit on failure
$file = $stash->stashFile( $this->bug29408File );
/**
* @covers FileContentsHasherTest
*/
-class FileContentsHasherTest extends MediaWikiTestCase {
+class FileContentsHasherTest extends PHPUnit_Framework_TestCase {
public function provideSingleFile() {
return array_map( function ( $file ) {
* @group Hash
*/
-class MWCryptHashTest extends MediaWikiTestCase {
+class MWCryptHashTest extends PHPUnit_Framework_TestCase {
public function testHashLength() {
if ( MWCryptHash::hashAlgo() !== 'whirlpool' ) {
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' );