experience problems, as this configuration is not well tested. safe_mode is also
not tested and unlikely to work.
-If you want math support see the instructions in math/README
+Support for rendering mathematical formulas requires installing the Math extension,
+see http://www.mediawiki.org/wiki/Extension:Math
Don't forget to check the RELEASE-NOTES file...
Decompress the MediaWiki installation archive either on your server, or on your
local machine and upload the directory tree. Rename it from "mediawiki-1.x.x" to
-something nice, like "wiki", since it'll be in your URL.
+something nice, like "wiki", since it will be appearing in your URL,
+ie. /wiki/index.php/Article.
+--------------------------------------------------------------------------+
- | Hint: If you plan to use a fancy URL-rewriting scheme to prettify your |
- | URLs, you should put the files in a *different* directory from the |
- | virtual path where page names will appear. |
+ | Note: If you plan to use a fancy URL-rewriting scheme to prettify your |
+ | URLs, such as http://www.example.com/wiki/Article, you should put the |
+ | files in a *different* directory from the virtual path where page names |
+ | will appear. It is common in this case to use w as the folder name and |
+ | /wiki/ as the virtual article path where your articles pretend to be. |
| |
| See: http://www.mediawiki.org/wiki/Manual:Short_URL |
+--------------------------------------------------------------------------+
of page watchers required for the number to be accessible to users
without the unwatchedpages permission.
* $wgBug34832TransitionalRollback has been removed.
+* (bug 29472) $wgUseDynamicDates has been removed and its functionality
+ disabled.
=== New features in 1.21 ===
* (bug 38110) Schema changes (adding or dropping tables, indicies and
a security fix (bug 42202).
* Added the ability to limit the wall clock time used by shell processes,
as well as the CPU time. Configurable with $wgMaxShellWallClockTime.
+* Allow memory of shell subprocesses to be limited using Linux cgroups
+ instead of ulimit -v, which tends to cause deadlocks in recent versions
+ of ImageMagick. Configurable with $wgShellCgroup.
* Added $wgWhitelistReadRegexp for regex whitelisting.
* (bug 5346) Categories that are redirects will be displayed italic in
the category links section at the bottom of a page.
redirects.
* On error, any warnings generated before that error will be shown in the result.
* action=help suports generalized submodules (modules=query+value), querymodules obsolete
+* ApiQueryImageInfo continuation is more reliable. The only major change is
+ that the imagerepository property will no longer be set on page objects not
+ processed in the current query (i.e. non-images or those skipped due to
+ iicontinue).
=== API internal changes in 1.21 ===
* For debugging only, a new global $wgDebugAPI removes many API restrictions when true.
+++ /dev/null
-#!/bin/bash
-
-if [ "$1" -gt 0 ]; then
- ulimit -t "$1"
-fi
-if [ "$2" -gt 0 ]; then
- if [ -e /sys/fs/cgroup/memory/mediawiki/job/ ]; then
- mkdir -m 0700 /sys/fs/cgroup/memory/mediawiki/job/$$
- echo $$ > /sys/fs/cgroup/memory/mediawiki/job/$$/tasks
- echo "1" > /sys/fs/cgroup/memory/mediawiki/job/$$/notify_on_release
- #memory
- echo $(($2*1024)) > /sys/fs/cgroup/memory/mediawiki/job/$$/memory.limit_in_bytes
- #memory+swap
- echo $(($2*1024)) > /sys/fs/cgroup/memory/mediawiki/job/$$/memory.memsw.limit_in_bytes
- fi
- ulimit -v "$2"
-fi
-if [ "$3" -gt 0 ]; then
- ulimit -f "$3"
-fi
-if [ "$4" -gt 0 -a -x "/usr/bin/timeout" ]; then
- /usr/bin/timeout $4 /bin/bash -c "$5"
- STATUS="$?"
- if [ "$STATUS" == 124 ]; then
- echo "ulimit5.sh: timed out." 1>&2
- fi
- exit "$STATUS"
-else
- eval "$5"
-fi
{
"name": "mediawiki/core",
- "version": "1.21-alpha",
"description": "Free software wiki application developed by the Wikimedia Foundation and others",
"keywords": ["mediawiki", "wiki"],
"homepage": "https://www.mediawiki.org/",
}
],
"license": "GPL-2.0",
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://gerrit.wikimedia.org/r/p/mediawiki/core.git"
+ }
+ ],
"support": {
"issues": "https://bugzilla.wikimedia.org/",
"irc": "irc://irc.freenode.net/mediawiki",
"require": {
"php": ">=5.3.2"
},
+ "require-dev": {
+ "phpunit/phpunit": "*"
+ },
"suggest": {
"ext-fileinfo": "*",
"ext-mbstring": "*",
- "ext-wikidiff2": "*"
+ "ext-wikidiff2": "*",
+ "ext-apc": "*"
}
}
// Check to see if the file exists
if ( !$repo->fileExists( $filename ) ) {
- wfForbidden( 'img-auth-accessdenied','img-auth-nofile', $filename );
+ wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $filename );
return;
}
*
* @internal documentation reviewed 15 Mar 2010
*/
-class Article extends Page {
+class Article implements Page {
/**@{{
* @private
*/
'ExternalStoreDB' => 'includes/externalstore/ExternalStoreDB.php',
'ExternalStoreHttp' => 'includes/externalstore/ExternalStoreHttp.php',
'ExternalStoreMedium' => 'includes/externalstore/ExternalStoreMedium.php',
+ 'ExternalStoreMwstore' => 'includes/externalstore/ExternalStoreMwstore.php',
'ExternalUser' => 'includes/ExternalUser.php',
'FakeTitle' => 'includes/FakeTitle.php',
'Fallback' => 'includes/Fallback.php',
'TitleArray' => 'includes/TitleArray.php',
'TitleArrayFromResult' => 'includes/TitleArray.php',
'ThrottledError' => 'includes/Exception.php',
+ 'UIDGenerator' => 'includes/UIDGenerator.php',
'UnlistedSpecialPage' => 'includes/SpecialPage.php',
'UploadSourceAdapter' => 'includes/Import.php',
'UppercaseCollation' => 'includes/Collation.php',
'MWTidyWrapper' => 'includes/parser/Tidy.php',
'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPCustomFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDAccum_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
'PPDPart_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDPart_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDStack' => 'includes/parser/Preprocessor_DOM.php',
'PPDStackElement' => 'includes/parser/Preprocessor_DOM.php',
'PPDStackElement_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDStackElement_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDStack_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDStack_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPFrame' => 'includes/parser/Preprocessor.php',
'PPFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPNode' => 'includes/parser/Preprocessor.php',
'PPNode_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPNode_Hash_Array' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Attr' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Text' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Tree' => 'includes/parser/Preprocessor_Hash.php',
- 'PPNode_HipHop_Array' => 'includes/parser/Preprocessor_HipHop.hphp',
- 'PPNode_HipHop_Attr' => 'includes/parser/Preprocessor_HipHop.hphp',
- 'PPNode_HipHop_Text' => 'includes/parser/Preprocessor_HipHop.hphp',
- 'PPNode_HipHop_Tree' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPTemplateFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPTemplateFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPTemplateFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'Parser' => 'includes/parser/Parser.php',
'ParserCache' => 'includes/parser/ParserCache.php',
'ParserOptions' => 'includes/parser/ParserOptions.php',
'Preprocessor' => 'includes/parser/Preprocessor.php',
'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'Preprocessor_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'StripState' => 'includes/parser/StripState.php',
# includes/profiler
}
return false;
}
+
+ /**
+ * Return the version of ICU library used by PHP's intl extension,
+ * or false when the extension is not installed of the version
+ * can't be determined.
+ *
+ * The constant INTL_ICU_VERSION this function refers to isn't really
+ * documented. It is available since PHP 5.3.7 (see PHP bug 54561).
+ * This function will return false on older PHPs.
+ *
+ * @since 1.21
+ * @return string|false
+ */
+ static function getICUVersion() {
+ return defined( 'INTL_ICU_VERSION' ) ? INTL_ICU_VERSION : false;
+ }
}
*/
$wgLegacySchemaConversion = false;
-/**
- * Enable to allow rewriting dates in page text.
- * DOES NOT FORMAT CORRECTLY FOR MOST LANGUAGES.
- */
-$wgUseDynamicDates = false;
/**
* Enable dates like 'May 12' instead of '12 May', this only takes effect if
* the interface is set to English.
*/
$wgSecureLogin = false;
+/**
+ * By default, keep users logged in via HTTPS when $wgSecureLogin is also
+ * true. Users opt-out of HTTPS when they login by de-selecting the checkbox.
+ * @since 1.21
+ */
+$wgSecureLoginDefaultHTTPS = true;
+
/** @} */ # end user accounts }
/************************************************************************//**
*/
$wgMaxShellWallClockTime = 180;
+/**
+ * Under Linux: a cgroup directory used to constrain memory usage of shell
+ * commands. The directory must be writable by the user which runs MediaWiki.
+ *
+ * If specified, this is used instead of ulimit, which is inaccurate, and
+ * causes malloc() to return NULL, which exposes bugs in C applications, making
+ * them segfault or deadlock.
+ *
+ * A wrapper script will create a cgroup for each shell command that runs, as
+ * a subgroup of the specified cgroup. If the memory limit is exceeded, the
+ * kernel will send a SIGKILL signal to a process in the subgroup.
+ *
+ * @par Example:
+ * @code
+ * mkdir -p /sys/fs/cgroup/memory/mediawiki
+ * mkdir -m 0777 /sys/fs/cgroup/memory/mediawiki/job
+ * echo '$wgShellCgroup = "/sys/fs/cgroup/memory/mediawiki/job";' >> LocalSettings.php
+ * @endcode
+ *
+ * The reliability of cgroup cleanup can be improved by installing a
+ * notify_on_release script in the root cgroup, see e.g.
+ * https://gerrit.wikimedia.org/r/#/c/40784
+ */
+$wgShellCgroup = false;
+
/**
* Executable path of the PHP cli binary (php/php5). Should be set up on install.
*/
define( 'OT_HTML', 1 );
define( 'OT_WIKI', 2 );
define( 'OT_PREPROCESS', 3 );
-define( 'OT_MSG' , 3 ); // b/c alias for OT_PREPROCESS
+define( 'OT_MSG', 3 ); // b/c alias for OT_PREPROCESS
define( 'OT_PLAIN', 4 );
/**@}*/
/**
* Find out whether or not a mixed variable exists in a string
*
+ * @deprecated Just use str(i)pos
* @param $needle String
* @param $str String
* @param $insensitive Boolean
* @return Boolean
*/
function in_string( $needle, $str, $insensitive = false ) {
+ wfDeprecated( __METHOD__, '1.21' );
$func = 'strpos';
if( $insensitive ) $func = 'stripos';
*/
function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
- $wgMaxShellWallClockTime;
+ $wgMaxShellWallClockTime, $wgShellCgroup;
static $disabled;
if ( is_null( $disabled ) ) {
$filesize = intval ( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
- $cmd = '/bin/bash ' . escapeshellarg( "$IP/bin/ulimit5.sh" ) .
- " $time $mem $filesize $wallTime " . escapeshellarg( $cmd );
+ $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
+ escapeshellarg( $cmd ) . ' ' .
+ escapeshellarg(
+ "MW_CPU_LIMIT=$time; " .
+ 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
+ "MW_MEM_LIMIT=$mem; " .
+ "MW_FILE_SIZE_LIMIT=$filesize; " .
+ "MW_WALL_CLOCK_LIMIT=$wallTime"
+ );
}
}
wfDebug( "wfShellExec: $cmd\n" );
* @return String: valid dotted quad IPv4 address or null
*/
public static function canonicalize( $addr ) {
- $addr = preg_replace( '/\%.*/','', $addr ); // remove zone info (bug 35738)
+ // remove zone info (bug 35738)
+ $addr = preg_replace( '/\%.*/', '', $addr );
+
if ( self::isValid( $addr ) ) {
return $addr;
}
if ( $size[0] < $width_orig && $size[1] < $height_orig
&& $size[0] != $width && $size[1] != $height )
{
- $otherSizes[] = $this->makeSizeLink( $params, $size[0], $size[1] );
+ $sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] );
+ if ( $sizeLink ) {
+ $otherSizes[] = $sizeLink;
+ }
}
}
- $msgsmall = wfMessage( 'show-big-image-preview' )->
- rawParams( $this->makeSizeLink( $params, $width, $height ) )->
- parse();
+ $msgsmall = '';
+ $sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
+ if ( $sizeLinkBigImagePreview ) {
+ $msgsmall .= wfMessage( 'show-big-image-preview' )->
+ rawParams( $sizeLinkBigImagePreview )->
+ parse();
+ }
if ( count( $otherSizes ) ) {
$msgsmall .= ' ' .
Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ),
* appending MM_WELL_KNOWN_MIME_TYPES behind $wgMimeTypeFile, but who knows
* what will break? In practice this probably isn't a problem anyway -- Bryan)
*/
-define('MM_WELL_KNOWN_MIME_TYPES',<<<END_STRING
+define('MM_WELL_KNOWN_MIME_TYPES', <<<END_STRING
application/ogg ogx ogg ogm ogv oga spx
application/pdf pdf
application/vnd.oasis.opendocument.chart odc
--- /dev/null
+<?php
+/**
+ * This file deals with UID generation.
+ *
+ * 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
+ */
+
+/**
+ * Class for getting statistically unique IDs
+ *
+ * @since 1.21
+ */
+class UIDGenerator {
+ /** @var UIDGenerator */
+ protected static $instance = null;
+
+ protected $nodeId32; // string; node ID in binary (32 bits)
+ protected $nodeId48; // string; node ID in binary (48 bits)
+
+ protected $lockFile88; // string; local file path
+ protected $lockFile128; // string; local file path
+
+ /** @var Array */
+ protected $fileHandles = array(); // cache file handles
+
+ const QUICK_RAND = 1; // get randomness from fast and unsecure sources
+
+ protected function __construct() {
+ $idFile = wfTempDir() . '/mw-' . __CLASS__ . '-UID-nodeid';
+ $nodeId = is_file( $idFile ) ? file_get_contents( $idFile ) : '';
+ // Try to get some ID that uniquely identifies this machine (RFC 4122)...
+ if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
+ wfSuppressWarnings();
+ if ( wfIsWindows() ) {
+ // http://technet.microsoft.com/en-us/library/bb490913.aspx
+ $csv = trim( wfShellExec( 'getmac /NH /FO CSV' ) );
+ $line = substr( $csv, 0, strcspn( $csv, "\n" ) );
+ $info = str_getcsv( $line );
+ $nodeId = isset( $info[0] ) ? str_replace( '-', '', $info[0] ) : '';
+ } elseif ( is_executable( '/sbin/ifconfig' ) ) { // Linux/BSD/Solaris/OS X
+ // See http://linux.die.net/man/8/ifconfig
+ $m = array();
+ preg_match( '/\s([0-9a-f]{2}(:[0-9a-f]{2}){5})\s/', wfShellExec( '/sbin/ifconfig -a' ), $m );
+ $nodeId = isset( $m[1] ) ? str_replace( ':', '', $m[1] ) : '';
+ }
+ wfRestoreWarnings();
+ if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
+ $nodeId = MWCryptRand::generateHex( 12, true );
+ $nodeId[1] = dechex( hexdec( $nodeId[1] ) | 0x1 ); // set multicast bit
+ }
+ file_put_contents( $idFile, $nodeId ); // cache
+ }
+ $this->nodeId32 = wfBaseConvert( substr( sha1( $nodeId ), 0, 8 ), 16, 2, 32 );
+ $this->nodeId48 = wfBaseConvert( $nodeId, 16, 2, 48 );
+ // If different processes run as different users, they may have different temp dirs.
+ // This is dealt with by initializing the clock sequence number and counters randomly.
+ $this->lockFile88 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-88';
+ $this->lockFile128 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-128';
+ }
+
+ /**
+ * @return UIDGenerator
+ */
+ protected static function singleton() {
+ if ( self::$instance === null ) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Get a statistically unique 88-bit unsigned integer ID string.
+ * The bits of the UID are prefixed with the time (down to the millisecond).
+ *
+ * These IDs are suitable as values for the shard key of distributed data.
+ * If a column uses these as values, it should be declared UNIQUE to handle collisions.
+ * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
+ * They can also be stored "DECIMAL(27) UNSIGNED" or BINARY(11) in MySQL.
+ *
+ * UID generation is serialized on each server (as the node ID is for the whole machine).
+ *
+ * @param $base integer Specifies a base other than 10
+ * @return string Number
+ * @throws MWException
+ */
+ public static function newTimestampedUID88( $base = 10 ) {
+ if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
+ throw new MWException( "Base must an integer be between 2 and 36" );
+ }
+ $gen = self::singleton();
+ $time = $gen->getTimestampAndDelay( 'lockFile88', 1, 1024 );
+ return wfBaseConvert( $gen->getTimestampedID88( $time ), 2, $base );
+ }
+
+ /**
+ * @param $time array (UIDGenerator::millitime(), clock sequence)
+ * @return string 88 bits
+ */
+ protected function getTimestampedID88( array $info ) {
+ list( $time, $counter ) = $info;
+ // Take the 46 MSBs of "milliseconds since epoch"
+ $id_bin = $this->millisecondsSinceEpochBinary( $time );
+ // Add a 10 bit counter resulting in 56 bits total
+ $id_bin .= str_pad( decbin( $counter ), 10, '0', STR_PAD_LEFT );
+ // Add the 32 bit node ID resulting in 88 bits total
+ $id_bin .= $this->nodeId32;
+ // Convert to a 1-27 digit integer string
+ if ( strlen( $id_bin ) !== 88 ) {
+ throw new MWException( "Detected overflow for millisecond timestamp." );
+ }
+ return $id_bin;
+ }
+
+ /**
+ * Get a statistically unique 128-bit unsigned integer ID string.
+ * The bits of the UID are prefixed with the time (down to the millisecond).
+ *
+ * These IDs are suitable as globally unique IDs, without any enforced uniqueness.
+ * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
+ * They can also be stored as "DECIMAL(39) UNSIGNED" or BINARY(16) in MySQL.
+ *
+ * UID generation is serialized on each server (as the node ID is for the whole machine).
+ *
+ * @param $base integer Specifies a base other than 10
+ * @return string Number
+ * @throws MWException
+ */
+ public static function newTimestampedUID128( $base = 10 ) {
+ if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
+ throw new MWException( "Base must be an integer between 2 and 36" );
+ }
+ $gen = self::singleton();
+ $time = $gen->getTimestampAndDelay( 'lockFile128', 16384, 1048576 );
+ return wfBaseConvert( $gen->getTimestampedID128( $time ), 2, $base );
+ }
+
+ /**
+ * @param $info array (UIDGenerator::milltime(), counter, clock sequence)
+ * @return string 128 bits
+ */
+ protected function getTimestampedID128( array $info ) {
+ list( $time, $counter, $clkSeq ) = $info;
+ // Take the 46 MSBs of "milliseconds since epoch"
+ $id_bin = $this->millisecondsSinceEpochBinary( $time );
+ // Add a 20 bit counter resulting in 66 bits total
+ $id_bin .= str_pad( decbin( $counter ), 20, '0', STR_PAD_LEFT );
+ // Add a 14 bit clock sequence number resulting in 80 bits total
+ $id_bin .= str_pad( decbin( $clkSeq ), 14, '0', STR_PAD_LEFT );
+ // Add the 48 bit node ID resulting in 128 bits total
+ $id_bin .= $this->nodeId48;
+ // Convert to a 1-39 digit integer string
+ if ( strlen( $id_bin ) !== 128 ) {
+ throw new MWException( "Detected overflow for millisecond timestamp." );
+ }
+ return $id_bin;
+ }
+
+ /**
+ * Return an RFC4122 compliant v4 UUID
+ *
+ * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
+ * @return string
+ * @throws MWException
+ */
+ public static function newUUIDv4( $flags = 0 ) {
+ $hex = ( $flags & self::QUICK_RAND )
+ ? wfRandomString( 31 )
+ : MWCryptRand::generateHex( 31 );
+
+ return sprintf( '%s-%s-%s-%s-%s',
+ // "time_low" (32 bits)
+ substr( $hex, 0, 8 ),
+ // "time_mid" (16 bits)
+ substr( $hex, 8, 4 ),
+ // "time_hi_and_version" (16 bits)
+ '4' . substr( $hex, 12, 3 ),
+ // "clk_seq_hi_res (8 bits, variant is binary 10x) and "clk_seq_low" (8 bits)
+ dechex( 0x8 | ( hexdec( $hex[15] ) & 0x3 ) ) . $hex[16] . substr( $hex, 17, 2 ),
+ // "node" (48 bits)
+ substr( $hex, 19, 12 )
+ );
+ }
+
+ /**
+ * Return an RFC4122 compliant v4 UUID
+ *
+ * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
+ * @return string 32 hex characters with no hyphens
+ * @throws MWException
+ */
+ public static function newRawUUIDv4( $flags = 0 ) {
+ return str_replace( '-', '', self::newUUIDv4( $flags ) );
+ }
+
+ /**
+ * Get a (time,counter,clock sequence) where (time,counter) is higher
+ * than any previous (time,counter) value for the given clock sequence.
+ * This is useful for making UIDs sequential on a per-node bases.
+ *
+ * @param $lockFile string Name of a local lock file
+ * @param $clockSeqSize integer The number of possible clock sequence values
+ * @param $counterSize integer The number of possible counter values
+ * @return Array (result of UIDGenerator::millitime(), counter, clock sequence)
+ * @throws MWException
+ */
+ protected function getTimestampAndDelay( $lockFile, $clockSeqSize, $counterSize ) {
+ // Get the UID lock file handle
+ if ( isset( $this->fileHandles[$lockFile] ) ) {
+ $handle = $this->fileHandles[$lockFile];
+ } else {
+ $handle = fopen( $this->$lockFile, 'cb+' );
+ $this->fileHandles[$lockFile] = $handle ?: null; // cache
+ }
+ // Acquire the UID lock file
+ if ( $handle === false ) {
+ throw new MWException( "Could not open '{$this->$lockFile}'." );
+ } elseif ( !flock( $handle, LOCK_EX ) ) {
+ throw new MWException( "Could not acquire '{$this->$lockFile}'." );
+ }
+ // Get the current timestamp, clock sequence number, last time, and counter
+ rewind( $handle );
+ $data = explode( ' ', fgets( $handle ) ); // "<clk seq> <sec> <msec> <counter> <offset>"
+ $clockChanged = false; // clock set back significantly?
+ if ( count( $data ) == 5 ) { // last UID info already initialized
+ $clkSeq = (int) $data[0] % $clockSeqSize;
+ $prevTime = array( (int) $data[1], (int) $data[2] );
+ $offset = (int) $data[4] % $counterSize; // random counter offset
+ $counter = 0; // counter for UIDs with the same timestamp
+ // Delay until the clock reaches the time of the last ID.
+ // This detects any microtime() drift among processes.
+ $time = $this->timeWaitUntil( $prevTime );
+ if ( !$time ) { // too long to delay?
+ $clockChanged = true; // bump clock sequence number
+ $time = self::millitime();
+ } elseif ( $time == $prevTime ) {
+ // Bump the counter if there are timestamp collisions
+ $counter = (int) $data[3] % $counterSize;
+ if ( ++$counter >= $counterSize ) { // sanity (starts at 0)
+ flock( $handle, LOCK_UN ); // abort
+ throw new MWException( "Counter overflow for timestamp value." );
+ }
+ }
+ } else { // last UID info not initialized
+ $clkSeq = mt_rand( 0, $clockSeqSize - 1 );
+ $counter = 0;
+ $offset = mt_rand( 0, $counterSize - 1 );
+ $time = self::millitime();
+ }
+ // microtime() and gettimeofday() can drift from time() at least on Windows.
+ // The drift is immediate for processes running while the system clock changes.
+ // time() does not have this problem. See https://bugs.php.net/bug.php?id=42659.
+ if ( abs( time() - $time[0] ) >= 2 ) {
+ // We don't want processes using too high or low timestamps to avoid duplicate
+ // UIDs and clock sequence number churn. This process should just be restarted.
+ flock( $handle, LOCK_UN ); // abort
+ throw new MWException( "Process clock is outdated or drifted." );
+ }
+ // If microtime() is synced and a clock change was detected, then the clock went back
+ if ( $clockChanged ) {
+ // Bump the clock sequence number and also randomize the counter offset,
+ // which is useful for UIDs that do not include the clock sequence number.
+ $clkSeq = ( $clkSeq + 1 ) % $clockSeqSize;
+ $offset = mt_rand( 0, $counterSize - 1 );
+ trigger_error( "Clock was set back; sequence number incremented." );
+ }
+ // Update the (clock sequence number, timestamp, counter)
+ ftruncate( $handle, 0 );
+ rewind( $handle );
+ fwrite( $handle, "{$clkSeq} {$time[0]} {$time[1]} {$counter} {$offset}" );
+ fflush( $handle );
+ // Release the UID lock file
+ flock( $handle, LOCK_UN );
+
+ return array( $time, ( $counter + $offset ) % $counterSize, $clkSeq );
+ }
+
+ /**
+ * Wait till the current timestamp reaches $time and return the current
+ * timestamp. This returns false if it would have to wait more than 10ms.
+ *
+ * @param $time array Result of UIDGenerator::millitime()
+ * @return Array|bool UIDGenerator::millitime() result or false
+ */
+ protected function timeWaitUntil( array $time ) {
+ do {
+ $ct = self::millitime();
+ if ( $ct >= $time ) { // http://php.net/manual/en/language.operators.comparison.php
+ return $ct; // current timestamp is higher than $time
+ }
+ } while ( ( ( $time[0] - $ct[0] )*1000 + ( $time[1] - $ct[1] ) ) <= 10 );
+
+ return false;
+ }
+
+ /**
+ * @param $time array Result of UIDGenerator::millitime()
+ * @return string 46 MSBs of "milliseconds since epoch" in binary (rolls over in 4201)
+ */
+ protected function millisecondsSinceEpochBinary( array $time ) {
+ list( $sec, $msec ) = $time;
+ if ( PHP_INT_SIZE >= 8 ) { // 64 bit integers
+ $ts = ( 1000 * $sec + $msec );
+ $id_bin = str_pad( decbin( $ts % pow( 2, 46 ) ), 46, '0', STR_PAD_LEFT );
+ } elseif ( extension_loaded( 'gmp' ) ) {
+ $ts = gmp_mod( // wrap around
+ gmp_add( gmp_mul( (string) $sec, (string) 1000 ), (string) $msec ),
+ gmp_pow( '2', '46' )
+ );
+ $id_bin = str_pad( gmp_strval( $ts, 2 ), 46, '0', STR_PAD_LEFT );
+ } elseif ( extension_loaded( 'bcmath' ) ) {
+ $ts = bcmod( // wrap around
+ bcadd( bcmul( $sec, 1000 ), $msec ),
+ bcpow( 2, 46 )
+ );
+ $id_bin = wfBaseConvert( $ts, 10, 2, 46 );
+ } else {
+ throw new MWException( 'bcmath or gmp extension required for 32 bit machines.' );
+ }
+ return $id_bin;
+ }
+
+ /**
+ * @return Array (current time in seconds, milliseconds since then)
+ */
+ protected static function millitime() {
+ list( $msec, $sec ) = explode( ' ', microtime() );
+ return array( (int) $sec, (int) ( $msec * 1000 ) );
+ }
+
+ function __destruct() {
+ array_map( 'fclose', $this->fileHandles );
+ }
+}
public function getPageRenderingHash() {
wfDeprecated( __METHOD__, '1.17' );
- global $wgUseDynamicDates, $wgRenderHashAppend, $wgLang, $wgContLang;
+ global $wgRenderHashAppend, $wgLang, $wgContLang;
if( $this->mHash ) {
return $this->mHash;
}
$confstr = $this->getOption( 'math' );
$confstr .= '!' . $this->getStubThreshold();
- if ( $wgUseDynamicDates ) { # This is wrong (bug 24714)
- $confstr .= '!' . $this->getDatePreference();
- }
$confstr .= '!' . ( $this->getOption( 'numberheadings' ) ? '1' : '' );
$confstr .= '!' . $wgLang->getCode();
$confstr .= '!' . $this->getOption( 'thumbsize' );
/**
* Abstract class for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
*/
-abstract class Page {}
+interface Page {}
/**
* Class representing a MediaWiki article and history.
*
* @internal documentation reviewed 15 Mar 2010
*/
-class WikiPage extends Page implements IDBAccessObject {
+class WikiPage implements Page, IDBAccessObject {
// Constants for $mDataLoadedFrom and related
/**
$pageInfo['header-basic'][] = array(
$this->msg( 'pageinfo-watchers' ), $lang->formatNum( $pageCounts['watchers'] )
);
+ } elseif ( $wgUnwatchedPageThreshold !== false ) {
+ $pageInfo['header-basic'][] = array(
+ $this->msg( 'pageinfo-watchers' ),
+ $this->msg( 'pageinfo-few-watchers' )->numParams( $wgUnwatchedPageThreshold )
+ );
}
// Redirects to this page
$titles = array_keys( $pageIds[NS_FILE] );
asort( $titles ); // Ensure the order is always the same
- $skip = false;
+ $fromTitle = null;
if ( !is_null( $params['continue'] ) ) {
- $skip = true;
$cont = explode( '|', $params['continue'] );
$this->dieContinueUsageIf( count( $cont ) != 2 );
$fromTitle = strval( $cont[0] );
$images = RepoGroup::singleton()->findFiles( $titles );
}
foreach ( $titles as $title ) {
+ $pageId = $pageIds[NS_FILE][$title];
+ $start = $title === $fromTitle ? $fromTimestamp : $params['start'];
+
if ( !isset( $images[$title] ) ) {
+ $result->addValue(
+ array( 'query', 'pages', intval( $pageId ) ),
+ 'imagerepository', ''
+ );
+ // The above can't fail because it doesn't increase the result size
continue;
}
- $start = $skip ? $fromTimestamp : $params['start'];
- $pageId = $pageIds[NS_FILE][$title];
$img = $images[$title];
$fit = $result->addValue(
// thing again. When the violating queries have been
// out-continued, the result will get through
$this->setContinueEnumParameter( 'start',
- wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) );
+ $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
+ );
} else {
$this->setContinueEnumParameter( 'continue',
- $this->getContinueStr( $img ) );
+ $this->getContinueStr( $img, $start ) );
}
break;
}
if ( !$fit ) {
break;
}
- $skip = false;
- }
-
- $data = $this->getResultData();
- foreach ( $data['query']['pages'] as $pageid => $arr ) {
- if ( is_array( $arr ) && !isset( $arr['imagerepository'] ) ) {
- $result->addValue(
- array( 'query', 'pages', $pageid ),
- 'imagerepository', ''
- );
- }
- // The above can't fail because it doesn't increase the result size
}
}
}
* @param $img File
* @return string
*/
- protected function getContinueStr( $img ) {
- return $img->getOriginalTitle()->getText() .
- '|' . $img->getTimestamp();
+ protected function getContinueStr( $img, $start = null ) {
+ if ( $start === null ) {
+ $start = $img->getTimestamp();
+ }
+ return $img->getOriginalTitle()->getText() . '|' . $start;
}
public function getAllowedParams() {
*/
/**
- * Accessable external objects
+ * Accessable external objects in a particular storage medium
+ *
* @ingroup ExternalStorage
+ * @since 1.21
*/
abstract class ExternalStoreMedium {
/** @var Array */
--- /dev/null
+<?php
+/**
+ * External storage in a file backend.
+ *
+ * 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
+ */
+
+/**
+ * File backend accessable external objects.
+ *
+ * In this system, each store "location" maps to the name of a file backend.
+ * The file backends must be defined in $wgFileBackends and must be global
+ * and fully qualified with a global "wikiId" prefix in the configuration.
+ *
+ * @ingroup ExternalStorage
+ * @since 1.21
+ */
+class ExternalStoreMwstore extends ExternalStoreMedium {
+ /**
+ * The URL returned is of the form of the form mwstore://backend/container/wiki/id
+ *
+ * @see ExternalStoreMedium::fetchFromURL()
+ */
+ public function fetchFromURL( $url ) {
+ $be = FileBackendGroup::singleton()->backendFromPath( $url );
+ if ( $be instanceof FileBackend ) {
+ // We don't need "latest" since objects are immutable and
+ // backends should at least have "read-after-create" consistency.
+ return $be->getFileContents( array( 'src' => $url ) );
+ }
+ return false;
+ }
+
+ /**
+ * @see ExternalStoreMedium::store()
+ */
+ public function store( $backend, $data ) {
+ $be = FileBackendGroup::singleton()->get( $backend );
+ if ( $be instanceof FileBackend ) {
+ // Get three random base 36 characters to act as shard directories
+ $rand = wfBaseConvert( mt_rand( 0, 46655 ), 10, 36, 3 );
+ // Make sure ID is roughly lexicographically increasing for performance
+ $id = str_pad( UIDGenerator::newTimestampedUID128( 32 ), 26, '0', STR_PAD_LEFT );
+ // Segregate items by wiki ID for the sake of bookkeeping
+ $wiki = isset( $this->params['wiki'] ) ? $this->params['wiki'] : wfWikiID();
+
+ $url = $be->getContainerStoragePath( 'data' ) . '/' .
+ rawurlencode( $wiki ) . "/{$rand[0]}/{$rand[1]}/{$rand[2]}/{$id}";
+
+ $be->prepare( array( 'dir' => dirname( $url ) ) );
+ if ( $be->create( array( 'dst' => $url, 'content' => $data ) )->isOK() ) {
+ return $url;
+ }
+ }
+ return false;
+ }
+}
'config-using531' => 'אי־אפשר להשתמש במדיה־ויקי עם <span dir="ltr">PHP $1</span> בגלל באג בפרמטרים של הפניות (reference parameters) ל־<code dir="ltr">__call()</code>.
שדרגו ל־PHP 5.3.2 או לגרסה גבוהה יותר כדי לתקן את זה ([//bugs.php.net/bug.php?id=50394 bug filed with PHP]) או שַנמכו ל־PHP 5.3.0 כדי לפתור את הבעיה הזאת.
ההתקנה בוטלה.',
- 'config-suhosin-max-value-length' => '×\9e×\95תק×\9f פ×\94 Suhosin ×\95×\94×\95×\90 ×\9e×\92×\91×\99×\9c ×\90ת ×\90×\95ר×\9a פר×\9e×\98ר GET ×\9cÖ¾$1 ×\91ת×\99×\9d. ר×\9b×\99×\91 ResourceLoader ש×\9c ×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×\99עק×\95×£ ×\90ת ×\94×\9e×\92×\9c×\91×\94 ×\94×\96×\90ת, ×\90×\91×\9c ×\96×\94 ×\99פ×\92×¢ ×\91×\91×\99צ×\95×¢×\99×\9d. ×\90×\9d ×\96×\94 ×\91×\9b×\9c×\9c ×\90פשר×\99, ×\9b×\93×\99 ×\9cתק×\9f ×\90ת ×\94ער×\9a ש×\9c <code>suhosin.get.max_value_length</code> ×\9cÖ¾1024 ×\91ק×\95×\91×¥ <code>php.ini</code> ×\95×\9c×\94×\92×\93×\99ר ×\90ת â\80\8e<code>$wgResourceLoaderMaxQueryLength</code> ×\9c×\90×\95ת×\95 ×\94ער×\9a ×\91ק×\95×\91×¥ LocalSettings.php.', # Fuzzy
+ 'config-suhosin-max-value-length' => '×\9e×\95תק×\9f פ×\94 Suhosin ×\95×\94×\95×\90 ×\9e×\92×\91×\99×\9c ×\90ת ×\90×\95ר×\9a פר×\9e×\98ר GET ×\9cÖ¾$1 ×\91ת×\99×\9d. ר×\9b×\99×\91 ResourceLoader ש×\9c ×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×\99עק×\95×£ ×\90ת ×\94×\9e×\92×\9c×\91×\94 ×\94×\96×\90ת, ×\90×\91×\9c ×\96×\94 ×\99פ×\92×¢ ×\91×\91×\99צ×\95×¢×\99×\9d. ×\90×\9d ×\96×\94 ×\91×\9b×\9c×\9c ×\90פשר×\99, ×\9b×\93×\90×\99 ×\9cתק×\9f ×\90ת ×\94ער×\9a ש×\9c <code>suhosin.get.max_value_length</code> ×\9cÖ¾1024 ×\90×\95 ×\99×\95תר ×\91ק×\95×\91×¥ <code>php.ini</code> ×\95×\9c×\94×\92×\93×\99ר ×\90ת â\80\8e<code>$wgResourceLoaderMaxQueryLength</code> ×\9c×\90×\95ת×\95 ×\94ער×\9a ×\91ק×\95×\91×¥ LocalSettings.php.',
'config-db-type' => 'סוג מסד הנתונים:',
'config-db-host' => 'שרת מסד הנתונים:',
'config-db-host-help' => 'אם שרת מסד הנתונים שלכם נמצא על שרת אחר, הקלידו את שם המחשב או את כתובת ה־IP כאן.
'config-support-postgres' => '$1 הוא מסד נתונים נפוץ בקוד פתוח והוא נפוץ בתור חלופה ל־MySQL (ר׳ [http://www.php.net/manual/en/pgsql.installation.php how to compile PHP with PostgreSQL support]). ייתכן שיש בתצורה הזאת באגים מסוימים והיא לא מומלצת לסביבות מבצעיות.',
'config-support-sqlite' => '* $1 הוא מסד נתונים קליל עם תמיכה טובה מאוד. (ר׳ [http://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], משתמש ב־PDO)',
'config-support-oracle' => '* $1 הוא מסד נתונים עסקי מסחרי. (ר׳ [http://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])',
- 'config-support-ibm_db2' => '* $1 הוא מסד נתונים מסחרי ארגוני.', # Fuzzy
+ 'config-support-ibm_db2' => '* $1 הוא מסד נתונים מסחרי ארגוני. ([http://www.php.net/manual/en/ibm-db2.installation.php How to compile PHP with IBM DB2 support])',
'config-header-mysql' => 'הגדרות MySQL',
'config-header-postgres' => 'הגדרות PostgreSQL',
'config-header-sqlite' => 'הגדרות SQLite',
'config-optional-continue' => 'הצגת שאלות נוספות.',
'config-optional-skip' => 'משעמם לי, תתקינו לי כבר את הוויקי הזה.',
'config-profile' => 'תסריט הרשאות משתמשים:',
- 'config-profile-wiki' => '×\95×\99ק×\99 ×\9eס×\95רת×\99', # Fuzzy
+ 'config-profile-wiki' => '×\95×\99ק×\99 פ×\99ת×\95×\97',
'config-profile-no-anon' => 'נדרשת יצירת חשבון',
'config-profile-fishbowl' => 'עורכים מורשים בלבד',
'config-profile-private' => 'ויקי פרטי',
עם זאת, אנשים שונים מצאו למדיה־ויקי שימושים מגוּונים ולעתים לא קל לשכנע את כולם ביתרונות של \"דרך הוויקי\" המסורתית. ולכן יש לכם בררה.
-באתר '''{{int:config-profile-wiki}}''' – לכולם יש הרשאה לערוך, אפילו בלי להיכנס לחשבון.
+באתר מסוג '''{{int:config-profile-wiki}}''' – לכולם יש הרשאה לערוך, אפילו בלי להיכנס לחשבון.
באתר וויקי מסוג '''{{int:config-profile-no-anon}}''' יש ביטחון גדול יותר, אבל הגדרה כזאת יכולה להרתיע תורמים מזדמנים.
בתסריט '''{{int:config-profile-fishbowl}}''' רק משתמשים שקיבלו אישור יכולים לערוך, אבל כל הגולשים יכולים לקרוא את הדפים ואת גרסאותיהם הקודמות.
ב'''{{int:config-profile-private}}''' רק משתמשים שקיבלו אישור יכולים לקרוא ולערוך דפים.
-הגדרות מורכבות של הרשאות אפשריות אחרי ההתקנה, ר׳ את [//www.mediawiki.org/wiki/Manual:User_rights הפרק על הנושא הזה בספר ההדרכה].", # Fuzzy
+הגדרות מורכבות של הרשאות אפשריות אחרי ההתקנה, ר׳ את [//www.mediawiki.org/wiki/Manual:User_rights הפרק על הנושא הזה בספר ההדרכה].",
'config-license' => 'זכויות יוצרים ורישיון:',
'config-license-none' => 'ללא כותרת תחתית עם רישיון',
'config-license-cc-by-sa' => 'קריאייטיב קומונז–ייחוס–שיתוף זהה',
'config-install-alreadydone' => "'''אזהרה:''' נראה שכבר התקנתם את מדיה־ויקי ואתם מנסים להתקין אותה שוב.
אנה התקדמו לדף הבא.",
'config-install-begin' => 'כשתלחצו על "{{int:config-continue}}", תתחילו את ההתקנה של מדיה־ויקי.
-אם אתם עדיין רוצים לשנות משהו, לחצו על "הקודם".', # Fuzzy
+אם אתם עדיין רוצים לשנות משהו, לחצו על "{{int:config-back}}"',
'config-install-step-done' => 'בוצע',
'config-install-step-failed' => 'נכשל',
'config-install-extensions' => 'כולל הרחבות',
== קישורים שימושיים ==
* [//www.mediawiki.org/wiki/Manual:Configuration_settings רשימת ההגדרות]
* [//www.mediawiki.org/wiki/Manual:FAQ שאלות ותשובות על מדיה־ויקי]
-* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce רשימת התפוצה על השקת גרסאות]', # Fuzzy
+* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce רשימת התפוצה על השקת גרסאות]
+* [//www.mediawiki.org/wiki/Localisation#Translation_resources תרגום מדיה־ויקי לשפה שלך]',
);
/** Hindi (हिन्दी)
}
}
- protected function renameIndex( $table, $old, $new ) {
- if ( $this->db->indexExists( $table, $old ) ) {
- $this->output( "Renaming index $old to $new\n" );
- $this->db->query( "ALTER INDEX $old RENAME TO $new" );
+ protected function renameIndex(
+ $table, $old, $new, $skipBothIndexExistWarning = false, $a = false, $b = false
+ ) {
+ // First requirement: the table must exist
+ if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
+ $this->output( "...skipping: '$table' table doesn't exist yet.\n" );
+ return;
+ }
+
+ // Second requirement: the new index must be missing
+ if ( $this->db->indexExists( $table, $new, __METHOD__ ) ) {
+ $this->output( "...index $new already set on $table table.\n" );
+ if ( !$skipBothIndexExistWarning
+ && $this->db->indexExists( $table, $old, __METHOD__ ) )
+ {
+ $this->output( "...WARNING: $old still exists, despite it has been renamed into $new (which also exists).\n" .
+ " $old should be manually removed if not needed anymore.\n" );
+ }
+ return;
}
+
+ // Third requirement: the old index must exist
+ if ( !$this->db->indexExists( $table, $old, __METHOD__ ) ) {
+ $this->output( "...skipping: index $old doesn't exist.\n" );
+ return;
+ }
+
+ $this->db->query( "ALTER INDEX $old RENAME TO $new" );
}
protected function addPgField( $table, $field, $type ) {
* @return void
*/
protected function doWaitForBackups() {}
+
+ /**
+ * Return a map of task names to task definition maps.
+ * A "task" is a fast periodic queue maintenance action.
+ * Mutually exclusive tasks must implement their own locking in the callback.
+ *
+ * Each task value is an associative array with:
+ * - name : the name of the task
+ * - callback : a PHP callable that performs the task
+ * - period : the period in seconds corresponding to the task frequency
+ *
+ * @return Array
+ */
+ final public function getPeriodicTasks() {
+ $tasks = $this->doGetPeriodicTasks();
+ foreach ( $tasks as $name => &$def ) {
+ $def['name'] = $name;
+ }
+ return $tasks;
+ }
+
+ /**
+ * @see JobQueue::getPeriodicTasks()
+ * @return Array
+ */
+ protected function doGetPeriodicTasks() {
+ return array();
+ }
+
+ /**
+ * Clear any process and persistent caches
+ *
+ * @return void
+ */
+ final public function flushCaches() {
+ wfProfileIn( __METHOD__ );
+ $this->doFlushCaches();
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * @see JobQueue::flushCaches()
+ * @return void
+ */
+ protected function doFlushCaches() {}
}
* @since 1.21
*/
class JobQueueDB extends JobQueue {
+ const ROOTJOB_TTL = 1209600; // integer; seconds to remember root jobs (14 days)
const CACHE_TTL_SHORT = 30; // integer; seconds to cache info without re-validating
const CACHE_TTL_LONG = 300; // integer; seconds to cache info that is kept up to date
const MAX_AGE_PRUNE = 604800; // integer; seconds a job can live once claimed
protected function doGetAcquiredCount() {
global $wgMemc;
+ if ( $this->claimTTL <= 0 ) {
+ return 0; // no acknowledgements
+ }
+
$key = $this->getCacheKey( 'acquiredcount' );
$count = $wgMemc->get( $key );
$uuid = wfRandomString( 32 ); // pop attempt
$job = false; // job popped off
- // Occasionally recycle jobs back into the queue that have been claimed too long
- if ( mt_rand( 0, 99 ) == 0 ) {
- $this->recycleStaleJobs();
- }
do { // retry when our row is invalid or deleted as a duplicate
// Try to reserve a row in the DB...
if ( in_array( $this->order, array( 'fifo', 'timestamp' ) ) ) {
*
* @return integer Number of jobs recycled/deleted
*/
- protected function recycleStaleJobs() {
+ public function recycleAndDeleteStaleJobs() {
global $wgMemc;
- $now = time();
+ $now = time();
list( $dbw, $scope ) = $this->getMasterDB();
$count = 0; // affected rows
}
// Update the timestamp of the last root job started at the location...
- return $wgMemc->set( $key, $params['rootJobTimestamp'], 14*86400 ); // 2 weeks
+ return $wgMemc->set( $key, $params['rootJobTimestamp'], JobQueueDB::ROOTJOB_TTL );
} );
return true;
wfWaitForSlaves();
}
+ /**
+ * @return Array
+ */
+ protected function doGetPeriodicTasks() {
+ return array(
+ 'recycleAndDeleteStaleJobs' => array(
+ 'callback' => array( $this, 'recycleAndDeleteStaleJobs' ),
+ 'period' => ceil( $this->claimTTL / 2 )
+ )
+ );
+ }
+
+ /**
+ * @return void
+ */
+ protected function doFlushCaches() {
+ global $wgMemc;
+
+ foreach ( array( 'empty', 'size', 'acquiredcount' ) as $type ) {
+ $wgMemc->delete( $this->getCacheKey( $type ) );
+ }
+ }
+
/**
* @return Array (DatabaseBase, ScopedCallback)
*/
}
return $types;
}
+
+ /**
+ * Execute any due periodic queue maintenance tasks for all queues.
+ *
+ * A task is "due" if the time ellapsed since the last run is greater than
+ * the defined run period. Concurrent calls to this function will cause tasks
+ * to be attempted twice, so they may need their own methods of mutual exclusion.
+ *
+ * @return integer Number of tasks run
+ */
+ public function executeReadyPeriodicTasks() {
+ global $wgMemc;
+
+ list( $db, $prefix ) = wfSplitWikiID( $this->wiki );
+ $key = wfForeignMemcKey( $db, $prefix, 'jobqueuegroup', 'taskruns', 'v1' );
+ $lastRuns = $wgMemc->get( $key ); // (queue => task => UNIX timestamp)
+
+ $count = 0;
+ $tasksRun = array(); // (queue => task => UNIX timestamp)
+ foreach ( $this->getQueueTypes() as $type ) {
+ $queue = $this->get( $type );
+ foreach ( $queue->getPeriodicTasks() as $task => $definition ) {
+ if ( $definition['period'] <= 0 ) {
+ continue; // disabled
+ } elseif ( !isset( $lastRuns[$type][$task] )
+ || $lastRuns[$type][$task] < ( time() - $definition['period'] ) )
+ {
+ if ( call_user_func( $definition['callback'] ) !== null ) {
+ $tasksRun[$type][$task] = time();
+ ++$count;
+ }
+ }
+ }
+ }
+
+ $wgMemc->merge( $key, function( $cache, $key, $lastRuns ) use ( $tasksRun ) {
+ if ( is_array( $lastRuns ) ) {
+ foreach ( $tasksRun as $type => $tasks ) {
+ foreach ( $tasks as $task => $timestamp ) {
+ if ( !isset( $lastRuns[$type][$task] )
+ || $timestamp > $lastRuns[$type][$task] )
+ {
+ $lastRuns[$type][$task] = $timestamp;
+ }
+ }
+ }
+ } else {
+ $lastRuns = $tasksRun;
+ }
+ return $lastRuns;
+ } );
+
+ return $count;
+ }
}
--- /dev/null
+#!/bin/bash
+#
+# Resource limiting wrapper for command execution
+#
+# Why is this in shell script? Because bash has a setrlimit() wrapper
+# and is available on most Linux systems. If Perl was distributed with
+# BSD::Resource included, we would happily use that instead, but it isn't.
+
+MW_CPU_LIMIT=0
+MW_CGROUP=
+MW_MEM_LIMIT=0
+MW_FILE_SIZE_LIMIT=0
+MW_WALL_CLOCK_LIMIT=0
+
+# Override settings
+eval "$2"
+
+if [ "$MW_CPU_LIMIT" -gt 0 ]; then
+ ulimit -t "$MW_CPU_LIMIT"
+fi
+if [ "$MW_MEM_LIMIT" -gt 0 ]; then
+ if [ -n "$MW_CGROUP" ]; then
+ # Create cgroup
+ if ! mkdir -m 0700 "$MW_CGROUP"/$$; then
+ echo "limit.sh: failed to create the cgroup." 1>&2
+ exit 1
+ fi
+ echo $$ > "$MW_CGROUP"/$$/tasks
+ if [ -n "$MW_CGROUP_NOTIFY" ]; then
+ echo "1" > "$MW_CGROUP"/$$/notify_on_release
+ fi
+ # Memory
+ echo $(($MW_MEM_LIMIT*1024)) > "$MW_CGROUP"/$$/memory.limit_in_bytes
+ # Memory+swap
+ echo $(($MW_MEM_LIMIT*1024)) > "$MW_CGROUP"/$$/memory.memsw.limit_in_bytes
+ else
+ ulimit -v "$MW_MEM_LIMIT"
+ fi
+fi
+if [ "$MW_FILE_SIZE_LIMIT" -gt 0 ]; then
+ ulimit -f "$MW_FILE_SIZE_LIMIT"
+fi
+if [ "$MW_WALL_CLOCK_LIMIT" -gt 0 -a -x "/usr/bin/timeout" ]; then
+ /usr/bin/timeout $MW_WALL_CLOCK_LIMIT /bin/bash -c "$1"
+ STATUS="$?"
+ if [ "$STATUS" == 124 ]; then
+ echo "limit.sh: timed out." 1>&2
+ fi
+else
+ eval "$1"
+ STATUS="$?"
+fi
+
+# Clean up cgroup
+cleanup() {
+ # First we have to move the current task into a "garbage" group, otherwise
+ # the cgroup will not be empty, and attempting to remove it will fail with
+ # "Device or resource busy"
+ if [ -w "$MW_CGROUP"/tasks ]; then
+ GARBAGE="$MW_CGROUP"
+ else
+ GARBAGE="$MW_CGROUP"/garbage-"$USER"
+ if [ ! -e "$GARBAGE" ]; then
+ mkdir -m 0700 "$GARBAGE"
+ fi
+ fi
+ echo $BASHPID > "$GARBAGE"/tasks
+
+ # Suppress errors in case the cgroup has disappeared due to a release script
+ rmdir "$MW_CGROUP"/$$ 2>/dev/null
+}
+
+updateTaskCount() {
+ # There are lots of ways to count lines in a file in shell script, but this
+ # is one of the few that doesn't create another process, which would
+ # increase the returned number of tasks.
+ readarray < "$MW_CGROUP"/$$/tasks
+ NUM_TASKS=${#MAPFILE[*]}
+}
+
+if [ -n "$MW_CGROUP" ]; then
+ updateTaskCount
+
+ if [ $NUM_TASKS -gt 1 ]; then
+ # Spawn a monitor process which will continue to poll for completion
+ # of all processes in the cgroup after termination of the parent shell
+ (
+ while [ $NUM_TASKS -gt 1 ]; do
+ sleep 10
+ updateTaskCount
+ done
+ cleanup
+ ) >&/dev/null < /dev/null &
+ disown -a
+ else
+ cleanup
+ fi
+fi
+exit "$STATUS"
+
$lastHangul = 0;
$startChar = '';
$combining = '';
- $x1 = ord(substr(UTF8_HANGUL_VBASE,0,1));
- $x2 = ord(substr(UTF8_HANGUL_TEND,0,1));
+ $x1 = ord(substr(UTF8_HANGUL_VBASE, 0, 1));
+ $x2 = ord(substr(UTF8_HANGUL_TEND, 0, 1));
for( $i = 0; $i < $len; $i++ ) {
$c = $string[$i];
$n = ord( $c );
* $wgAllowSpecialInclusion
* $wgInterwikiMagic
* $wgMaxArticleSize
- * $wgUseDynamicDates
*
* @ingroup Parser
*/
$text = $this->doDoubleUnderscore( $text );
$text = $this->doHeadings( $text );
- if ( $this->mOptions->getUseDynamicDates() ) {
- $df = DateFormatter::getInstance();
- $text = $df->reformat( $this->mOptions->getDateFormat(), $text );
- }
$text = $this->replaceInternalLinks( $text );
$text = $this->doAllQuotes( $text );
$text = $this->replaceExternalLinks( $text );
*/
class ParserOptions {
- /**
- * Use DateFormatter to format dates
- */
- var $mUseDynamicDates;
-
/**
* Interlanguage links are removed and returned in an array
*/
*/
protected $onAccessCallback = null;
- function getUseDynamicDates() { return $this->mUseDynamicDates; }
function getInterwikiMagic() { return $this->mInterwikiMagic; }
function getAllowExternalImages() { return $this->mAllowExternalImages; }
function getAllowExternalImagesFrom() { return $this->mAllowExternalImagesFrom; }
return $this->getUserLangObj()->getCode();
}
- function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates, $x ); }
function setInterwikiMagic( $x ) { return wfSetVar( $this->mInterwikiMagic, $x ); }
function setAllowExternalImages( $x ) { return wfSetVar( $this->mAllowExternalImages, $x ); }
function setAllowExternalImagesFrom( $x ) { return wfSetVar( $this->mAllowExternalImagesFrom, $x ); }
* @param $lang Language object
*/
private function initialiseFromUser( $user, $lang ) {
- global $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages,
+ global $wgInterwikiMagic, $wgAllowExternalImages,
$wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
$wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
$wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
wfProfileIn( __METHOD__ );
- $this->mUseDynamicDates = $wgUseDynamicDates;
$this->mInterwikiMagic = $wgInterwikiMagic;
$this->mAllowExternalImages = $wgAllowExternalImages;
$this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
* @return array
*/
public static function legacyOptions() {
- global $wgUseDynamicDates;
- $legacyOpts = array( 'math', 'stubthreshold', 'numberheadings', 'userlang', 'thumbsize', 'editsection', 'printable' );
- if ( $wgUseDynamicDates ) {
- $legacyOpts[] = 'dateformat';
- }
- return $legacyOpts;
+ return array( 'math', 'stubthreshold', 'numberheadings', 'userlang', 'thumbsize', 'editsection', 'printable' );
}
/**
$piece->parts = array( new PPDPart );
$piece->count -= $matchingCount;
# do we still qualify for any callback with remaining count?
- $names = $rules[$piece->open]['names'];
- $skippedBraces = 0;
- $enclosingAccum =& $accum;
- while ( $piece->count ) {
- if ( array_key_exists( $piece->count, $names ) ) {
- $stack->push( $piece );
- $accum =& $stack->getAccum();
- break;
- }
- --$piece->count;
- $skippedBraces ++;
+ $min = $rules[$piece->open]['min'];
+ if ( $piece->count >= $min ) {
+ $stack->push( $piece );
+ $accum =& $stack->getAccum();
+ } else {
+ $accum .= str_repeat( $piece->open, $piece->count );
}
- $enclosingAccum .= str_repeat( $piece->open, $skippedBraces );
}
$flags = $stack->getFlags();
extract( $flags );
$piece->parts = array( new PPDPart_Hash );
$piece->count -= $matchingCount;
# do we still qualify for any callback with remaining count?
- $names = $rules[$piece->open]['names'];
- $skippedBraces = 0;
- $enclosingAccum =& $accum;
- while ( $piece->count ) {
- if ( array_key_exists( $piece->count, $names ) ) {
- $stack->push( $piece );
- $accum =& $stack->getAccum();
- break;
- }
- --$piece->count;
- $skippedBraces ++;
+ $min = $rules[$piece->open]['min'];
+ if ( $piece->count >= $min ) {
+ $stack->push( $piece );
+ $accum =& $stack->getAccum();
+ } else {
+ $accum->addLiteral( str_repeat( $piece->open, $piece->count ) );
}
- $enclosingAccum->addLiteral( str_repeat( $piece->open, $skippedBraces ) );
}
extract( $stack->getFlags() );
+++ /dev/null
-<?php
-/**
- * A preprocessor optimised for HipHop, using HipHop-specific syntax.
- * vim: ft=php
- *
- * 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 Parser
- */
-
-/**
- * @ingroup Parser
- */
-class Preprocessor_HipHop implements Preprocessor {
- /**
- * @var Parser
- */
- var $parser;
-
- const CACHE_VERSION = 1;
-
- /**
- * @param $parser Parser
- */
- function __construct( $parser ) {
- $this->parser = $parser;
- }
-
- /**
- * @return PPFrame_HipHop
- */
- function newFrame() {
- return new PPFrame_HipHop( $this );
- }
-
- /**
- * @param $args array
- * @return PPCustomFrame_HipHop
- */
- function newCustomFrame( $args ) {
- return new PPCustomFrame_HipHop( $this, $args );
- }
-
- /**
- * @param $values array
- * @return PPNode_HipHop_Array
- */
- function newPartNodeArray( $values ) {
- $list = array();
-
- foreach ( $values as $k => $val ) {
- $partNode = new PPNode_HipHop_Tree( 'part' );
- $nameNode = new PPNode_HipHop_Tree( 'name' );
-
- if ( is_int( $k ) ) {
- $nameNode->addChild( new PPNode_HipHop_Attr( 'index', $k ) );
- $partNode->addChild( $nameNode );
- } else {
- $nameNode->addChild( new PPNode_HipHop_Text( $k ) );
- $partNode->addChild( $nameNode );
- $partNode->addChild( new PPNode_HipHop_Text( '=' ) );
- }
-
- $valueNode = new PPNode_HipHop_Tree( 'value' );
- $valueNode->addChild( new PPNode_HipHop_Text( $val ) );
- $partNode->addChild( $valueNode );
-
- $list[] = $partNode;
- }
-
- $node = new PPNode_HipHop_Array( $list );
- return $node;
- }
-
- /**
- * Preprocess some wikitext and return the document tree.
- * This is the ghost of Parser::replace_variables().
- *
- * @param $text String: the text to parse
- * @param $flags Integer: bitwise combination of:
- * Parser::PTD_FOR_INCLUSION Handle <noinclude>/<includeonly> as if the text is being
- * included. Default is to assume a direct page view.
- *
- * The generated DOM tree must depend only on the input text and the flags.
- * The DOM tree must be the same in OT_HTML and OT_WIKI mode, to avoid a regression of bug 4899.
- *
- * Any flag added to the $flags parameter here, or any other parameter liable to cause a
- * change in the DOM tree for a given text, must be passed through the section identifier
- * in the section edit link and thus back to extractSections().
- *
- * The output of this function is currently only cached in process memory, but a persistent
- * cache may be implemented at a later date which takes further advantage of these strict
- * dependency requirements.
- *
- * @throws MWException
- * @return PPNode_HipHop_Tree
- */
- function preprocessToObj( $text, $flags = 0 ) {
- wfProfileIn( __METHOD__ );
-
- // Check cache.
- global $wgMemc, $wgPreprocessorCacheThreshold;
-
- $lengthText = strlen( $text );
-
- $cacheable = ($wgPreprocessorCacheThreshold !== false && $lengthText > $wgPreprocessorCacheThreshold);
- if ( $cacheable ) {
- wfProfileIn( __METHOD__.'-cacheable' );
-
- $cacheKey = strval( wfMemcKey( 'preprocess-hash', md5( $text ), $flags ) );
- $cacheValue = strval( $wgMemc->get( $cacheKey ) );
- if ( $cacheValue !== '' ) {
- $version = substr( $cacheValue, 0, 8 );
- if ( intval( $version ) == self::CACHE_VERSION ) {
- $hash = unserialize( substr( $cacheValue, 8 ) );
- // From the cache
- wfDebugLog( "Preprocessor",
- "Loaded preprocessor hash from memcached (key $cacheKey)" );
- wfProfileOut( __METHOD__.'-cacheable' );
- wfProfileOut( __METHOD__ );
- return $hash;
- }
- }
- wfProfileIn( __METHOD__.'-cache-miss' );
- }
-
- $rules = array(
- '{' => array(
- 'end' => '}',
- 'names' => array(
- 2 => 'template',
- 3 => 'tplarg',
- ),
- 'min' => 2,
- 'max' => 3,
- ),
- '[' => array(
- 'end' => ']',
- 'names' => array( 2 => 'LITERAL' ),
- 'min' => 2,
- 'max' => 2,
- )
- );
-
- $forInclusion = (bool)( $flags & Parser::PTD_FOR_INCLUSION );
-
- $xmlishElements = (array)$this->parser->getStripList();
- $enableOnlyinclude = false;
- if ( $forInclusion ) {
- $ignoredTags = array( 'includeonly', '/includeonly' );
- $ignoredElements = array( 'noinclude' );
- $xmlishElements[] = 'noinclude';
- if ( strpos( $text, '<onlyinclude>' ) !== false && strpos( $text, '</onlyinclude>' ) !== false ) {
- $enableOnlyinclude = true;
- }
- } else if ( $this->parser->ot['wiki'] ) {
- $ignoredTags = array( 'noinclude', '/noinclude', 'onlyinclude', '/onlyinclude', 'includeonly', '/includeonly' );
- $ignoredElements = array();
- } else {
- $ignoredTags = array( 'noinclude', '/noinclude', 'onlyinclude', '/onlyinclude' );
- $ignoredElements = array( 'includeonly' );
- $xmlishElements[] = 'includeonly';
- }
- $xmlishRegex = implode( '|', array_merge( $xmlishElements, $ignoredTags ) );
-
- // Use "A" modifier (anchored) instead of "^", because ^ doesn't work with an offset
- $elementsRegex = "~($xmlishRegex)(?:\s|\/>|>)|(!--)~iA";
-
- $stack = new PPDStack_HipHop;
-
- $searchBase = "[{<\n";
- $revText = strrev( $text ); // For fast reverse searches
-
- $i = 0; # Input pointer, starts out pointing to a pseudo-newline before the start
- $accum = $stack->getAccum(); # Current accumulator
- $headingIndex = 1;
- $stackFlags = array(
- 'findPipe' => false, # True to take notice of pipe characters
- 'findEquals' => false, # True to find equals signs in arguments
- 'inHeading' => false, # True if $i is inside a possible heading
- );
- $noMoreGT = false; # True if there are no more greater-than (>) signs right of $i
- $findOnlyinclude = $enableOnlyinclude; # True to ignore all input up to the next <onlyinclude>
- $fakeLineStart = true; # Do a line-start run without outputting an LF character
-
- while ( true ) {
- //$this->memCheck();
-
- if ( $findOnlyinclude ) {
- // Ignore all input up to the next <onlyinclude>
- $variantStartPos = strpos( $text, '<onlyinclude>', $i );
- if ( $variantStartPos === false ) {
- // Ignored section runs to the end
- $accum->addNodeWithText( 'ignore', strval( substr( $text, $i ) ) );
- break;
- }
- $startPos1 = intval( $variantStartPos );
- $tagEndPos = $startPos1 + strlen( '<onlyinclude>' ); // past-the-end
- $accum->addNodeWithText( 'ignore', strval( substr( $text, $i, $tagEndPos - $i ) ) );
- $i = $tagEndPos;
- $findOnlyinclude = false;
- }
-
- if ( $fakeLineStart ) {
- $found = 'line-start';
- $curChar = '';
- } else {
- # Find next opening brace, closing brace or pipe
- $search = $searchBase;
- if ( $stack->top === false ) {
- $currentClosing = '';
- } else {
- $currentClosing = strval( $stack->getTop()->close );
- $search .= $currentClosing;
- }
- if ( $stackFlags['findPipe'] ) {
- $search .= '|';
- }
- if ( $stackFlags['findEquals'] ) {
- // First equals will be for the template
- $search .= '=';
- }
- $rule = null;
- # Output literal section, advance input counter
- $literalLength = intval( strcspn( $text, $search, $i ) );
- if ( $literalLength > 0 ) {
- $accum->addLiteral( strval( substr( $text, $i, $literalLength ) ) );
- $i += $literalLength;
- }
- if ( $i >= $lengthText ) {
- if ( $currentClosing === "\n" ) {
- // Do a past-the-end run to finish off the heading
- $curChar = '';
- $found = 'line-end';
- } else {
- # All done
- break;
- }
- } else {
- $curChar = $text[$i];
- if ( $curChar === '|' ) {
- $found = 'pipe';
- } elseif ( $curChar === '=' ) {
- $found = 'equals';
- } elseif ( $curChar === '<' ) {
- $found = 'angle';
- } elseif ( $curChar === "\n" ) {
- if ( $stackFlags['inHeading'] ) {
- $found = 'line-end';
- } else {
- $found = 'line-start';
- }
- } elseif ( $curChar === $currentClosing ) {
- $found = 'close';
- } elseif ( isset( $rules[$curChar] ) ) {
- $found = 'open';
- $rule = $rules[$curChar];
- } else {
- # Some versions of PHP have a strcspn which stops on null characters
- # Ignore and continue
- ++$i;
- continue;
- }
- }
- }
-
- if ( $found === 'angle' ) {
- $matches = false;
- // Handle </onlyinclude>
- if ( $enableOnlyinclude
- && substr( $text, $i, strlen( '</onlyinclude>' ) ) === '</onlyinclude>' )
- {
- $findOnlyinclude = true;
- continue;
- }
-
- // Determine element name
- if ( !preg_match( $elementsRegex, $text, $matches, 0, $i + 1 ) ) {
- // Element name missing or not listed
- $accum->addLiteral( '<' );
- ++$i;
- continue;
- }
- // Handle comments
- if ( isset( $matches[2] ) && $matches[2] === '!--' ) {
- // To avoid leaving blank lines, when a comment is both preceded
- // and followed by a newline (ignoring spaces), trim leading and
- // trailing spaces and one of the newlines.
-
- // Find the end
- $variantEndPos = strpos( $text, '-->', $i + 4 );
- if ( $variantEndPos === false ) {
- // Unclosed comment in input, runs to end
- $inner = strval( substr( $text, $i ) );
- $accum->addNodeWithText( 'comment', $inner );
- $i = $lengthText;
- } else {
- $endPos = intval( $variantEndPos );
- // Search backwards for leading whitespace
- if ( $i ) {
- $wsStart = $i - intval( strspn( $revText, ' ', $lengthText - $i ) );
- } else {
- $wsStart = 0;
- }
- // Search forwards for trailing whitespace
- // $wsEnd will be the position of the last space (or the '>' if there's none)
- $wsEnd = $endPos + 2 + intval( strspn( $text, ' ', $endPos + 3 ) );
- // Eat the line if possible
- // TODO: This could theoretically be done if $wsStart == 0, i.e. for comments at
- // the overall start. That's not how Sanitizer::removeHTMLcomments() did it, but
- // it's a possible beneficial b/c break.
- if ( $wsStart > 0 && substr( $text, $wsStart - 1, 1 ) === "\n"
- && substr( $text, $wsEnd + 1, 1 ) === "\n" )
- {
- $startPos2 = $wsStart;
- $endPos = $wsEnd + 1;
- // Remove leading whitespace from the end of the accumulator
- // Sanity check first though
- $wsLength = $i - $wsStart;
- if ( $wsLength > 0
- && $accum->lastNode instanceof PPNode_HipHop_Text
- && substr( $accum->lastNode->value, -$wsLength ) === str_repeat( ' ', $wsLength ) )
- {
- $accum->lastNode->value = strval( substr( $accum->lastNode->value, 0, -$wsLength ) );
- }
- // Do a line-start run next time to look for headings after the comment
- $fakeLineStart = true;
- } else {
- // No line to eat, just take the comment itself
- $startPos2 = $i;
- $endPos += 2;
- }
-
- if ( $stack->top ) {
- $part = $stack->getTop()->getCurrentPart();
- if ( !(isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 )) {
- $part->visualEnd = $wsStart;
- }
- // Else comments abutting, no change in visual end
- $part->commentEnd = $endPos;
- }
- $i = $endPos + 1;
- $inner = strval( substr( $text, $startPos2, $endPos - $startPos2 + 1 ) );
- $accum->addNodeWithText( 'comment', $inner );
- }
- continue;
- }
- $name = strval( $matches[1] );
- $lowerName = strtolower( $name );
- $attrStart = $i + strlen( $name ) + 1;
-
- // Find end of tag
- $variantTagEndPos = $noMoreGT ? false : strpos( $text, '>', $attrStart );
- if ( $variantTagEndPos === false ) {
- // Infinite backtrack
- // Disable tag search to prevent worst-case O(N^2) performance
- $noMoreGT = true;
- $accum->addLiteral( '<' );
- ++$i;
- continue;
- }
- $tagEndPos = intval( $variantTagEndPos );
-
- // Handle ignored tags
- if ( in_array( $lowerName, $ignoredTags ) ) {
- $accum->addNodeWithText( 'ignore', strval( substr( $text, $i, $tagEndPos - $i + 1 ) ) );
- $i = $tagEndPos + 1;
- continue;
- }
-
- $tagStartPos = $i;
- $close = '';
- if ( $text[$tagEndPos-1] === '/' ) {
- // Short end tag
- $attrEnd = $tagEndPos - 1;
- $shortEnd = true;
- $inner = '';
- $i = $tagEndPos + 1;
- $haveClose = false;
- } else {
- $attrEnd = $tagEndPos;
- $shortEnd = false;
- // Find closing tag
- if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
- $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 ) )
- {
- $inner = strval( substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 ) );
- $i = intval( $matches[0][1] ) + strlen( $matches[0][0] );
- $close = strval( $matches[0][0] );
- $haveClose = true;
- } else {
- // No end tag -- let it run out to the end of the text.
- $inner = strval( substr( $text, $tagEndPos + 1 ) );
- $i = $lengthText;
- $haveClose = false;
- }
- }
- // <includeonly> and <noinclude> just become <ignore> tags
- if ( in_array( $lowerName, $ignoredElements ) ) {
- $accum->addNodeWithText( 'ignore', strval( substr( $text, $tagStartPos, $i - $tagStartPos ) ) );
- continue;
- }
-
- if ( $attrEnd <= $attrStart ) {
- $attr = '';
- } else {
- // Note that the attr element contains the whitespace between name and attribute,
- // this is necessary for precise reconstruction during pre-save transform.
- $attr = strval( substr( $text, $attrStart, $attrEnd - $attrStart ) );
- }
-
- $extNode = new PPNode_HipHop_Tree( 'ext' );
- $extNode->addChild( PPNode_HipHop_Tree::newWithText( 'name', $name ) );
- $extNode->addChild( PPNode_HipHop_Tree::newWithText( 'attr', $attr ) );
- if ( !$shortEnd ) {
- $extNode->addChild( PPNode_HipHop_Tree::newWithText( 'inner', $inner ) );
- }
- if ( $haveClose ) {
- $extNode->addChild( PPNode_HipHop_Tree::newWithText( 'close', $close ) );
- }
- $accum->addNode( $extNode );
- }
-
- elseif ( $found === 'line-start' ) {
- // Is this the start of a heading?
- // Line break belongs before the heading element in any case
- if ( $fakeLineStart ) {
- $fakeLineStart = false;
- } else {
- $accum->addLiteral( $curChar );
- $i++;
- }
-
- $count = intval( strspn( $text, '=', $i, 6 ) );
- if ( $count == 1 && $stackFlags['findEquals'] ) {
- // DWIM: This looks kind of like a name/value separator
- // Let's let the equals handler have it and break the potential heading
- // This is heuristic, but AFAICT the methods for completely correct disambiguation are very complex.
- } elseif ( $count > 0 ) {
- $partData = array(
- 'open' => "\n",
- 'close' => "\n",
- 'parts' => array( new PPDPart_HipHop( str_repeat( '=', $count ) ) ),
- 'startPos' => $i,
- 'count' => $count );
- $stack->push( $partData );
- $accum = $stack->getAccum();
- $stackFlags = $stack->getFlags();
- $i += $count;
- }
- } elseif ( $found === 'line-end' ) {
- $piece = $stack->getTop();
- // A heading must be open, otherwise \n wouldn't have been in the search list
- assert( $piece->open === "\n" ); // Passing the assert condition directly instead of string, as
- // HPHP /compiler/ chokes on strings when ASSERT_ACTIVE != 0.
- $part = $piece->getCurrentPart();
- // Search back through the input to see if it has a proper close
- // Do this using the reversed string since the other solutions (end anchor, etc.) are inefficient
- $wsLength = intval( strspn( $revText, " \t", $lengthText - $i ) );
- $searchStart = $i - $wsLength;
- if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
- // Comment found at line end
- // Search for equals signs before the comment
- $searchStart = intval( $part->visualEnd );
- $searchStart -= intval( strspn( $revText, " \t", $lengthText - $searchStart ) );
- }
- $count = intval( $piece->count );
- $equalsLength = intval( strspn( $revText, '=', $lengthText - $searchStart ) );
- $isTreeNode = false;
- $resultAccum = $accum;
- if ( $equalsLength > 0 ) {
- if ( $searchStart - $equalsLength == $piece->startPos ) {
- // This is just a single string of equals signs on its own line
- // Replicate the doHeadings behaviour /={count}(.+)={count}/
- // First find out how many equals signs there really are (don't stop at 6)
- $count = $equalsLength;
- if ( $count < 3 ) {
- $count = 0;
- } else {
- $count = intval( ( $count - 1 ) / 2 );
- if ( $count > 6 ) {
- $count = 6;
- }
- }
- } else {
- if ( $count > $equalsLength ) {
- $count = $equalsLength;
- }
- }
- if ( $count > 0 ) {
- // Normal match, output <h>
- $tree = new PPNode_HipHop_Tree( 'possible-h' );
- $tree->addChild( new PPNode_HipHop_Attr( 'level', $count ) );
- $tree->addChild( new PPNode_HipHop_Attr( 'i', $headingIndex++ ) );
- $tree->lastChild->nextSibling = $accum->firstNode;
- $tree->lastChild = $accum->lastNode;
- $isTreeNode = true;
- } else {
- // Single equals sign on its own line, count=0
- // Output $resultAccum
- }
- } else {
- // No match, no <h>, just pass down the inner text
- // Output $resultAccum
- }
- // Unwind the stack
- $stack->pop();
- $accum = $stack->getAccum();
- $stackFlags = $stack->getFlags();
-
- // Append the result to the enclosing accumulator
- if ( $isTreeNode ) {
- $accum->addNode( $tree );
- } else {
- $accum->addAccum( $resultAccum );
- }
- // Note that we do NOT increment the input pointer.
- // This is because the closing linebreak could be the opening linebreak of
- // another heading. Infinite loops are avoided because the next iteration MUST
- // hit the heading open case above, which unconditionally increments the
- // input pointer.
- } elseif ( $found === 'open' ) {
- # count opening brace characters
- $count = intval( strspn( $text, $curChar, $i ) );
-
- # we need to add to stack only if opening brace count is enough for one of the rules
- if ( $count >= $rule['min'] ) {
- # Add it to the stack
- $partData = array(
- 'open' => $curChar,
- 'close' => $rule['end'],
- 'count' => $count,
- 'lineStart' => ($i == 0 || $text[$i-1] === "\n"),
- );
-
- $stack->push( $partData );
- $accum = $stack->getAccum();
- $stackFlags = $stack->getFlags();
- } else {
- # Add literal brace(s)
- $accum->addLiteral( str_repeat( $curChar, $count ) );
- }
- $i += $count;
- } elseif ( $found === 'close' ) {
- $piece = $stack->getTop();
- # lets check if there are enough characters for closing brace
- $maxCount = intval( $piece->count );
- $count = intval( strspn( $text, $curChar, $i, $maxCount ) );
-
- # check for maximum matching characters (if there are 5 closing
- # characters, we will probably need only 3 - depending on the rules)
- $rule = $rules[$piece->open];
- if ( $count > $rule['max'] ) {
- # The specified maximum exists in the callback array, unless the caller
- # has made an error
- $matchingCount = intval( $rule['max'] );
- } else {
- # Count is less than the maximum
- # Skip any gaps in the callback array to find the true largest match
- # Need to use array_key_exists not isset because the callback can be null
- $matchingCount = $count;
- while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule['names'] ) ) {
- --$matchingCount;
- }
- }
-
- if ( $matchingCount <= 0 ) {
- # No matching element found in callback array
- # Output a literal closing brace and continue
- $accum->addLiteral( str_repeat( $curChar, $count ) );
- $i += $count;
- continue;
- }
- $name = strval( $rule['names'][$matchingCount] );
- $isTreeNode = false;
- if ( $name === 'LITERAL' ) {
- // No element, just literal text
- $resultAccum = $piece->breakSyntax( $matchingCount );
- $resultAccum->addLiteral( str_repeat( $rule['end'], $matchingCount ) );
- } else {
- # Create XML element
- # Note: $parts is already XML, does not need to be encoded further
- $isTreeNode = true;
- $parts = $piece->parts;
- $titleAccum = PPDAccum_HipHop::cast( $parts[0]->out );
- unset( $parts[0] );
-
- $tree = new PPNode_HipHop_Tree( $name );
-
- # The invocation is at the start of the line if lineStart is set in
- # the stack, and all opening brackets are used up.
- if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
- $tree->addChild( new PPNode_HipHop_Attr( 'lineStart', 1 ) );
- }
- $titleNode = new PPNode_HipHop_Tree( 'title' );
- $titleNode->firstChild = $titleAccum->firstNode;
- $titleNode->lastChild = $titleAccum->lastNode;
- $tree->addChild( $titleNode );
- $argIndex = 1;
- foreach ( $parts as $variantPart ) {
- $part = PPDPart_HipHop::cast( $variantPart );
- if ( isset( $part->eqpos ) ) {
- // Find equals
- $lastNode = false;
- for ( $node = $part->out->firstNode; $node; $node = $node->nextSibling ) {
- if ( $node === $part->eqpos ) {
- break;
- }
- $lastNode = $node;
- }
- if ( !$node ) {
- throw new MWException( __METHOD__. ': eqpos not found' );
- }
- if ( $node->name !== 'equals' ) {
- throw new MWException( __METHOD__ .': eqpos is not equals' );
- }
- $equalsNode = $node;
-
- // Construct name node
- $nameNode = new PPNode_HipHop_Tree( 'name' );
- if ( $lastNode !== false ) {
- $lastNode->nextSibling = false;
- $nameNode->firstChild = $part->out->firstNode;
- $nameNode->lastChild = $lastNode;
- }
-
- // Construct value node
- $valueNode = new PPNode_HipHop_Tree( 'value' );
- if ( $equalsNode->nextSibling !== false ) {
- $valueNode->firstChild = $equalsNode->nextSibling;
- $valueNode->lastChild = $part->out->lastNode;
- }
- $partNode = new PPNode_HipHop_Tree( 'part' );
- $partNode->addChild( $nameNode );
- $partNode->addChild( $equalsNode->firstChild );
- $partNode->addChild( $valueNode );
- $tree->addChild( $partNode );
- } else {
- $partNode = new PPNode_HipHop_Tree( 'part' );
- $nameNode = new PPNode_HipHop_Tree( 'name' );
- $nameNode->addChild( new PPNode_HipHop_Attr( 'index', $argIndex++ ) );
- $valueNode = new PPNode_HipHop_Tree( 'value' );
- $valueNode->firstChild = $part->out->firstNode;
- $valueNode->lastChild = $part->out->lastNode;
- $partNode->addChild( $nameNode );
- $partNode->addChild( $valueNode );
- $tree->addChild( $partNode );
- }
- }
- }
-
- # Advance input pointer
- $i += $matchingCount;
-
- # Unwind the stack
- $stack->pop();
- $accum = $stack->getAccum();
-
- # Re-add the old stack element if it still has unmatched opening characters remaining
- if ( $matchingCount < $piece->count ) {
- $piece->parts = array( new PPDPart_HipHop );
- $piece->count -= $matchingCount;
- # do we still qualify for any callback with remaining count?
- $names = $rules[$piece->open]['names'];
- $skippedBraces = 0;
- $enclosingAccum = $accum;
- while ( $piece->count ) {
- if ( array_key_exists( $piece->count, $names ) ) {
- $stack->push( $piece );
- $accum = $stack->getAccum();
- break;
- }
- --$piece->count;
- $skippedBraces ++;
- }
- $enclosingAccum->addLiteral( str_repeat( $piece->open, $skippedBraces ) );
- }
-
- $stackFlags = $stack->getFlags();
-
- # Add XML element to the enclosing accumulator
- if ( $isTreeNode ) {
- $accum->addNode( $tree );
- } else {
- $accum->addAccum( $resultAccum );
- }
- } elseif ( $found === 'pipe' ) {
- $stackFlags['findEquals'] = true; // shortcut for getFlags()
- $stack->addPart();
- $accum = $stack->getAccum();
- ++$i;
- } elseif ( $found === 'equals' ) {
- $stackFlags['findEquals'] = false; // shortcut for getFlags()
- $accum->addNodeWithText( 'equals', '=' );
- $stack->getCurrentPart()->eqpos = $accum->lastNode;
- ++$i;
- }
- }
-
- # Output any remaining unclosed brackets
- foreach ( $stack->stack as $variantPiece ) {
- $piece = PPDStackElement_HipHop::cast( $variantPiece );
- $stack->rootAccum->addAccum( $piece->breakSyntax() );
- }
-
- # Enable top-level headings
- for ( $node = $stack->rootAccum->firstNode; $node; $node = $node->nextSibling ) {
- if ( isset( $node->name ) && $node->name === 'possible-h' ) {
- $node->name = 'h';
- }
- }
-
- $rootNode = new PPNode_HipHop_Tree( 'root' );
- $rootNode->firstChild = $stack->rootAccum->firstNode;
- $rootNode->lastChild = $stack->rootAccum->lastNode;
-
- // Cache
- if ( $cacheable ) {
- $cacheValue = sprintf( "%08d", self::CACHE_VERSION ) . serialize( $rootNode );
- $wgMemc->set( $cacheKey, $cacheValue, 86400 );
- wfProfileOut( __METHOD__.'-cache-miss' );
- wfProfileOut( __METHOD__.'-cacheable' );
- wfDebugLog( "Preprocessor", "Saved preprocessor Hash to memcached (key $cacheKey)" );
- }
-
- wfProfileOut( __METHOD__ );
- return $rootNode;
- }
-}
-
-
-
-/**
- * Stack class to help Preprocessor::preprocessToObj()
- * @ingroup Parser
- */
-class PPDStack_HipHop {
- var $stack, $rootAccum;
-
- /**
- * @var PPDStack
- */
- var $top;
- var $out;
-
- static $false = false;
-
- function __construct() {
- $this->stack = array();
- $this->top = false;
- $this->rootAccum = new PPDAccum_HipHop;
- $this->accum = $this->rootAccum;
- }
-
- /**
- * @return int
- */
- function count() {
- return count( $this->stack );
- }
-
- function getAccum() {
- return PPDAccum_HipHop::cast( $this->accum );
- }
-
- function getCurrentPart() {
- return $this->getTop()->getCurrentPart();
- }
-
- function getTop() {
- return PPDStackElement_HipHop::cast( $this->top );
- }
-
- function push( $data ) {
- if ( $data instanceof PPDStackElement_HipHop ) {
- $this->stack[] = $data;
- } else {
- $this->stack[] = new PPDStackElement_HipHop( $data );
- }
- $this->top = $this->stack[ count( $this->stack ) - 1 ];
- $this->accum = $this->top->getAccum();
- }
-
- function pop() {
- if ( !count( $this->stack ) ) {
- throw new MWException( __METHOD__.': no elements remaining' );
- }
- $temp = array_pop( $this->stack );
-
- if ( count( $this->stack ) ) {
- $this->top = $this->stack[ count( $this->stack ) - 1 ];
- $this->accum = $this->top->getAccum();
- } else {
- $this->top = self::$false;
- $this->accum = $this->rootAccum;
- }
- return $temp;
- }
-
- function addPart( $s = '' ) {
- $this->top->addPart( $s );
- $this->accum = $this->top->getAccum();
- }
-
- /**
- * @return array
- */
- function getFlags() {
- if ( !count( $this->stack ) ) {
- return array(
- 'findEquals' => false,
- 'findPipe' => false,
- 'inHeading' => false,
- );
- } else {
- return $this->top->getFlags();
- }
- }
-}
-
-/**
- * @ingroup Parser
- */
-class PPDStackElement_HipHop {
- var $open, // Opening character (\n for heading)
- $close, // Matching closing character
- $count, // Number of opening characters found (number of "=" for heading)
- $parts, // Array of PPDPart objects describing pipe-separated parts.
- $lineStart; // True if the open char appeared at the start of the input line. Not set for headings.
-
- /**
- * @param $obj PPDStackElement_HipHop
- * @return PPDStackElement_HipHop
- */
- static function cast( PPDStackElement_HipHop $obj ) {
- return $obj;
- }
-
- /**
- * @param $data array
- */
- function __construct( $data = array() ) {
- $this->parts = array( new PPDPart_HipHop );
-
- foreach ( $data as $name => $value ) {
- $this->$name = $value;
- }
- }
-
- /**
- * @return PPDAccum_HipHop
- */
- function getAccum() {
- return PPDAccum_HipHop::cast( $this->parts[count( $this->parts ) - 1]->out );
- }
-
- /**
- * @param $s string
- */
- function addPart( $s = '' ) {
- $this->parts[] = new PPDPart_HipHop( $s );
- }
-
- /**
- * @return PPDPart_HipHop
- */
- function getCurrentPart() {
- return PPDPart_HipHop::cast( $this->parts[count( $this->parts ) - 1] );
- }
-
- /**
- * @return array
- */
- function getFlags() {
- $partCount = count( $this->parts );
- $findPipe = $this->open !== "\n" && $this->open !== '[';
- return array(
- 'findPipe' => $findPipe,
- 'findEquals' => $findPipe && $partCount > 1 && !isset( $this->parts[$partCount - 1]->eqpos ),
- 'inHeading' => $this->open === "\n",
- );
- }
-
- /**
- * Get the accumulator that would result if the close is not found.
- *
- * @param $openingCount bool
- * @return PPDAccum_HipHop
- */
- function breakSyntax( $openingCount = false ) {
- if ( $this->open === "\n" ) {
- $accum = PPDAccum_HipHop::cast( $this->parts[0]->out );
- } else {
- if ( $openingCount === false ) {
- $openingCount = $this->count;
- }
- $accum = new PPDAccum_HipHop;
- $accum->addLiteral( str_repeat( $this->open, $openingCount ) );
- $first = true;
- foreach ( $this->parts as $part ) {
- if ( $first ) {
- $first = false;
- } else {
- $accum->addLiteral( '|' );
- }
- $accum->addAccum( $part->out );
- }
- }
- return $accum;
- }
-}
-
-/**
- * @ingroup Parser
- */
-class PPDPart_HipHop {
- var $out; // Output accumulator object
-
- // Optional member variables:
- // eqpos Position of equals sign in output accumulator
- // commentEnd Past-the-end input pointer for the last comment encountered
- // visualEnd Past-the-end input pointer for the end of the accumulator minus comments
-
- function __construct( $out = '' ) {
- $this->out = new PPDAccum_HipHop;
- if ( $out !== '' ) {
- $this->out->addLiteral( $out );
- }
- }
-
- static function cast( PPDPart_HipHop $obj ) {
- return $obj;
- }
-}
-
-/**
- * @ingroup Parser
- */
-class PPDAccum_HipHop {
- var $firstNode, $lastNode;
-
- function __construct() {
- $this->firstNode = $this->lastNode = false;
- }
-
- static function cast( PPDAccum_HipHop $obj ) {
- return $obj;
- }
-
- /**
- * Append a string literal
- */
- function addLiteral( string $s ) {
- if ( $this->lastNode === false ) {
- $this->firstNode = $this->lastNode = new PPNode_HipHop_Text( $s );
- } elseif ( $this->lastNode instanceof PPNode_HipHop_Text ) {
- $this->lastNode->value .= $s;
- } else {
- $this->lastNode->nextSibling = new PPNode_HipHop_Text( $s );
- $this->lastNode = $this->lastNode->nextSibling;
- }
- }
-
- /**
- * Append a PPNode
- */
- function addNode( PPNode $node ) {
- if ( $this->lastNode === false ) {
- $this->firstNode = $this->lastNode = $node;
- } else {
- $this->lastNode->nextSibling = $node;
- $this->lastNode = $node;
- }
- }
-
- /**
- * Append a tree node with text contents
- */
- function addNodeWithText( string $name, string $value ) {
- $node = PPNode_HipHop_Tree::newWithText( $name, $value );
- $this->addNode( $node );
- }
-
- /**
- * Append a PPDAccum_HipHop
- * Takes over ownership of the nodes in the source argument. These nodes may
- * subsequently be modified, especially nextSibling.
- */
- function addAccum( PPDAccum_HipHop $accum ) {
- if ( $accum->lastNode === false ) {
- // nothing to add
- } elseif ( $this->lastNode === false ) {
- $this->firstNode = $accum->firstNode;
- $this->lastNode = $accum->lastNode;
- } else {
- $this->lastNode->nextSibling = $accum->firstNode;
- $this->lastNode = $accum->lastNode;
- }
- }
-}
-
-/**
- * An expansion frame, used as a context to expand the result of preprocessToObj()
- * @ingroup Parser
- */
-class PPFrame_HipHop implements PPFrame {
-
- /**
- * @var Parser
- */
- var $parser;
-
- /**
- * @var Preprocessor
- */
- var $preprocessor;
-
- /**
- * @var Title
- */
- var $title;
- var $titleCache;
-
- /**
- * Hashtable listing templates which are disallowed for expansion in this frame,
- * having been encountered previously in parent frames.
- */
- var $loopCheckHash;
-
- /**
- * Recursion depth of this frame, top = 0
- * Note that this is NOT the same as expansion depth in expand()
- */
- var $depth;
-
- /**
- * Construct a new preprocessor frame.
- * @param $preprocessor Preprocessor: the parent preprocessor
- */
- function __construct( $preprocessor ) {
- $this->preprocessor = $preprocessor;
- $this->parser = $preprocessor->parser;
- $this->title = $this->parser->mTitle;
- $this->titleCache = array( $this->title ? $this->title->getPrefixedDBkey() : false );
- $this->loopCheckHash = array();
- $this->depth = 0;
- }
-
- /**
- * Create a new child frame
- * $args is optionally a multi-root PPNode or array containing the template arguments
- *
- * @param $args PPNode_HipHop_Array|array|bool
- * @param $title Title|bool
- * @param $indexOffset A number subtracted from the index attributes of the arguments
- *
- * @throws MWException
- * @return PPTemplateFrame_HipHop
- */
- function newChild( $args = false, $title = false, $indexOffset = 0 ) {
- $namedArgs = array();
- $numberedArgs = array();
- if ( $title === false ) {
- $title = $this->title;
- }
- if ( $args !== false ) {
- if ( $args instanceof PPNode_HipHop_Array ) {
- $args = $args->value;
- } elseif ( !is_array( $args ) ) {
- throw new MWException( __METHOD__ . ': $args must be array or PPNode_HipHop_Array' );
- }
- foreach ( $args as $arg ) {
- $bits = $arg->splitArg();
- if ( $bits['index'] !== '' ) {
- // Numbered parameter
- $numberedArgs[$bits['index']] = $bits['value'];
- unset( $namedArgs[$bits['index']] );
- } else {
- // Named parameter
- $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
- $namedArgs[$name] = $bits['value'];
- unset( $numberedArgs[$name] );
- }
- }
- }
- return new PPTemplateFrame_HipHop( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
- }
-
- /**
- * @throws MWException
- * @param $root
- * @param $flags int
- * @return string
- */
- function expand( $root, $flags = 0 ) {
- static $expansionDepth = 0;
- if ( is_string( $root ) ) {
- return $root;
- }
-
- if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
- $this->parser->limitationWarn( 'node-count-exceeded',
- $this->parser->mPPNodeCount,
- $this->parser->mOptions->getMaxPPNodeCount()
- );
- return '<span class="error">Node-count limit exceeded</span>';
- }
- if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
- $this->parser->limitationWarn( 'expansion-depth-exceeded',
- $expansionDepth,
- $this->parser->mOptions->getMaxPPExpandDepth()
- );
- return '<span class="error">Expansion depth limit exceeded</span>';
- }
- ++$expansionDepth;
- if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
- $this->parser->mHighestExpansionDepth = $expansionDepth;
- }
-
- $outStack = array( '', '' );
- $iteratorStack = array( false, $root );
- $indexStack = array( 0, 0 );
-
- while ( count( $iteratorStack ) > 1 ) {
- $level = count( $outStack ) - 1;
- $iteratorNode =& $iteratorStack[ $level ];
- $out =& $outStack[$level];
- $index =& $indexStack[$level];
-
- if ( is_array( $iteratorNode ) ) {
- if ( $index >= count( $iteratorNode ) ) {
- // All done with this iterator
- $iteratorStack[$level] = false;
- $contextNode = false;
- } else {
- $contextNode = $iteratorNode[$index];
- $index++;
- }
- } elseif ( $iteratorNode instanceof PPNode_HipHop_Array ) {
- if ( $index >= $iteratorNode->getLength() ) {
- // All done with this iterator
- $iteratorStack[$level] = false;
- $contextNode = false;
- } else {
- $contextNode = $iteratorNode->item( $index );
- $index++;
- }
- } else {
- // Copy to $contextNode and then delete from iterator stack,
- // because this is not an iterator but we do have to execute it once
- $contextNode = $iteratorStack[$level];
- $iteratorStack[$level] = false;
- }
-
- $newIterator = false;
-
- if ( $contextNode === false ) {
- // nothing to do
- } elseif ( is_string( $contextNode ) ) {
- $out .= $contextNode;
- } elseif ( is_array( $contextNode ) || $contextNode instanceof PPNode_HipHop_Array ) {
- $newIterator = $contextNode;
- } elseif ( $contextNode instanceof PPNode_HipHop_Attr ) {
- // No output
- } elseif ( $contextNode instanceof PPNode_HipHop_Text ) {
- $out .= $contextNode->value;
- } elseif ( $contextNode instanceof PPNode_HipHop_Tree ) {
- if ( $contextNode->name === 'template' ) {
- # Double-brace expansion
- $bits = $contextNode->splitTemplate();
- if ( $flags & PPFrame::NO_TEMPLATES ) {
- $newIterator = $this->virtualBracketedImplode( '{{', '|', '}}', $bits['title'], $bits['parts'] );
- } else {
- $ret = $this->parser->braceSubstitution( $bits, $this );
- if ( isset( $ret['object'] ) ) {
- $newIterator = $ret['object'];
- } else {
- $out .= $ret['text'];
- }
- }
- } elseif ( $contextNode->name === 'tplarg' ) {
- # Triple-brace expansion
- $bits = $contextNode->splitTemplate();
- if ( $flags & PPFrame::NO_ARGS ) {
- $newIterator = $this->virtualBracketedImplode( '{{{', '|', '}}}', $bits['title'], $bits['parts'] );
- } else {
- $ret = $this->parser->argSubstitution( $bits, $this );
- if ( isset( $ret['object'] ) ) {
- $newIterator = $ret['object'];
- } else {
- $out .= $ret['text'];
- }
- }
- } elseif ( $contextNode->name === 'comment' ) {
- # HTML-style comment
- # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
- if ( $this->parser->ot['html']
- || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
- || ( $flags & PPFrame::STRIP_COMMENTS ) )
- {
- $out .= '';
- }
- # Add a strip marker in PST mode so that pstPass2() can run some old-fashioned regexes on the result
- # Not in RECOVER_COMMENTS mode (extractSections) though
- elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
- $out .= $this->parser->insertStripItem( $contextNode->firstChild->value );
- }
- # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
- else {
- $out .= $contextNode->firstChild->value;
- }
- } elseif ( $contextNode->name === 'ignore' ) {
- # Output suppression used by <includeonly> etc.
- # OT_WIKI will only respect <ignore> in substed templates.
- # The other output types respect it unless NO_IGNORE is set.
- # extractSections() sets NO_IGNORE and so never respects it.
- if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] ) || ( $flags & PPFrame::NO_IGNORE ) ) {
- $out .= $contextNode->firstChild->value;
- } else {
- //$out .= '';
- }
- } elseif ( $contextNode->name === 'ext' ) {
- # Extension tag
- $bits = $contextNode->splitExt() + array( 'attr' => null, 'inner' => null, 'close' => null );
- $out .= $this->parser->extensionSubstitution( $bits, $this );
- } elseif ( $contextNode->name === 'h' ) {
- # Heading
- if ( $this->parser->ot['html'] ) {
- # Expand immediately and insert heading index marker
- $s = '';
- for ( $node = $contextNode->firstChild; $node; $node = $node->nextSibling ) {
- $s .= $this->expand( $node, $flags );
- }
-
- $bits = $contextNode->splitHeading();
- $titleText = $this->title->getPrefixedDBkey();
- $this->parser->mHeadings[] = array( $titleText, $bits['i'] );
- $serial = count( $this->parser->mHeadings ) - 1;
- $marker = "{$this->parser->mUniqPrefix}-h-$serial-" . Parser::MARKER_SUFFIX;
- $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
- $this->parser->mStripState->addGeneral( $marker, '' );
- $out .= $s;
- } else {
- # Expand in virtual stack
- $newIterator = $contextNode->getChildren();
- }
- } else {
- # Generic recursive expansion
- $newIterator = $contextNode->getChildren();
- }
- } else {
- throw new MWException( __METHOD__.': Invalid parameter type' );
- }
-
- if ( $newIterator !== false ) {
- $outStack[] = '';
- $iteratorStack[] = $newIterator;
- $indexStack[] = 0;
- } elseif ( $iteratorStack[$level] === false ) {
- // Return accumulated value to parent
- // With tail recursion
- while ( $iteratorStack[$level] === false && $level > 0 ) {
- $outStack[$level - 1] .= $out;
- array_pop( $outStack );
- array_pop( $iteratorStack );
- array_pop( $indexStack );
- $level--;
- }
- }
- }
- --$expansionDepth;
- return $outStack[0];
- }
-
- /**
- * @param $sep
- * @param $flags
- * @return string
- */
- function implodeWithFlags( $sep, $flags /*, ... */ ) {
- $args = array_slice( func_get_args(), 2 );
-
- $first = true;
- $s = '';
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_HipHop_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = array( $root );
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= $sep;
- }
- $s .= $this->expand( $node, $flags );
- }
- }
- return $s;
- }
-
- /**
- * Implode with no flags specified
- * This previously called implodeWithFlags but has now been inlined to reduce stack depth
- * @param $sep
- * @return string
- */
- function implode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
-
- $first = true;
- $s = '';
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_HipHop_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = array( $root );
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= $sep;
- }
- $s .= $this->expand( $node );
- }
- }
- return $s;
- }
-
- /**
- * Makes an object that, when expand()ed, will be the same as one obtained
- * with implode()
- *
- * @param $sep
- * @return PPNode_HipHop_Array
- */
- function virtualImplode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
- $out = array();
- $first = true;
-
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_HipHop_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = array( $root );
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $out[] = $sep;
- }
- $out[] = $node;
- }
- }
- return new PPNode_HipHop_Array( $out );
- }
-
- /**
- * Virtual implode with brackets
- *
- * @param $start
- * @param $sep
- * @param $end
- * @return PPNode_HipHop_Array
- */
- function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
- $args = array_slice( func_get_args(), 3 );
- $out = array( $start );
- $first = true;
-
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_HipHop_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = array( $root );
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $out[] = $sep;
- }
- $out[] = $node;
- }
- }
- $out[] = $end;
- return new PPNode_HipHop_Array( $out );
- }
-
- function __toString() {
- return 'frame{}';
- }
-
- /**
- * @param $level bool
- * @return array|bool|String
- */
- function getPDBK( $level = false ) {
- if ( $level === false ) {
- return $this->title->getPrefixedDBkey();
- } else {
- return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] : false;
- }
- }
-
- /**
- * @return array
- */
- function getArguments() {
- return array();
- }
-
- /**
- * @return array
- */
- function getNumberedArguments() {
- return array();
- }
-
- /**
- * @return array
- */
- function getNamedArguments() {
- return array();
- }
-
- /**
- * Returns true if there are no arguments in this frame
- *
- * @return bool
- */
- function isEmpty() {
- return true;
- }
-
- /**
- * @param $name
- * @return bool
- */
- function getArgument( $name ) {
- return false;
- }
-
- /**
- * Returns true if the infinite loop check is OK, false if a loop is detected
- *
- * @param $title Title
- *
- * @return bool
- */
- function loopCheck( $title ) {
- return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
- }
-
- /**
- * Return true if the frame is a template frame
- *
- * @return bool
- */
- function isTemplate() {
- return false;
- }
-
- /**
- * Get a title of frame
- *
- * @return Title
- */
- function getTitle() {
- return $this->title;
- }
-}
-
-/**
- * Expansion frame with template arguments
- * @ingroup Parser
- */
-class PPTemplateFrame_HipHop extends PPFrame_HipHop {
- var $numberedArgs, $namedArgs, $parent;
- var $numberedExpansionCache, $namedExpansionCache;
-
- /**
- * @param $preprocessor Preprocessor_HipHop
- * @param $parent bool
- * @param $numberedArgs array
- * @param $namedArgs array
- * @param $title Title|bool
- */
- function __construct( $preprocessor, $parent = false, $numberedArgs = array(), $namedArgs = array(), $title = false ) {
- parent::__construct( $preprocessor );
-
- $this->parent = $parent;
- $this->numberedArgs = $numberedArgs;
- $this->namedArgs = $namedArgs;
- $this->title = $title;
- $pdbk = $title ? $title->getPrefixedDBkey() : false;
- $this->titleCache = $parent->titleCache;
- $this->titleCache[] = $pdbk;
- $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
- if ( $pdbk !== false ) {
- $this->loopCheckHash[$pdbk] = true;
- }
- $this->depth = $parent->depth + 1;
- $this->numberedExpansionCache = $this->namedExpansionCache = array();
- }
-
- function __toString() {
- $s = 'tplframe{';
- $first = true;
- $args = $this->numberedArgs + $this->namedArgs;
- foreach ( $args as $name => $value ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= ', ';
- }
- $s .= "\"$name\":\"" .
- str_replace( '"', '\\"', $value->__toString() ) . '"';
- }
- $s .= '}';
- return $s;
- }
- /**
- * Returns true if there are no arguments in this frame
- *
- * @return bool
- */
- function isEmpty() {
- return !count( $this->numberedArgs ) && !count( $this->namedArgs );
- }
-
- /**
- * @return array
- */
- function getArguments() {
- $arguments = array();
- foreach ( array_merge(
- array_keys($this->numberedArgs),
- array_keys($this->namedArgs)) as $key ) {
- $arguments[$key] = $this->getArgument($key);
- }
- return $arguments;
- }
-
- /**
- * @return array
- */
- function getNumberedArguments() {
- $arguments = array();
- foreach ( array_keys( $this->numberedArgs ) as $key ) {
- $arguments[$key] = $this->getArgument( $key );
- }
- return $arguments;
- }
-
- /**
- * @return array
- */
- function getNamedArguments() {
- $arguments = array();
- foreach ( array_keys( $this->namedArgs ) as $key ) {
- $arguments[$key] = $this->getArgument( $key );
- }
- return $arguments;
- }
-
- /**
- * @param $index
- * @return array|bool
- */
- function getNumberedArgument( $index ) {
- if ( !isset( $this->numberedArgs[$index] ) ) {
- return false;
- }
- if ( !isset( $this->numberedExpansionCache[$index] ) ) {
- # No trimming for unnamed arguments
- $this->numberedExpansionCache[$index] = $this->parent->expand( $this->numberedArgs[$index], PPFrame::STRIP_COMMENTS );
- }
- return $this->numberedExpansionCache[$index];
- }
-
- /**
- * @param $name
- * @return bool
- */
- function getNamedArgument( $name ) {
- if ( !isset( $this->namedArgs[$name] ) ) {
- return false;
- }
- if ( !isset( $this->namedExpansionCache[$name] ) ) {
- # Trim named arguments post-expand, for backwards compatibility
- $this->namedExpansionCache[$name] = trim(
- $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
- }
- return $this->namedExpansionCache[$name];
- }
-
- /**
- * @param $name
- * @return array|bool
- */
- function getArgument( $name ) {
- $text = $this->getNumberedArgument( $name );
- if ( $text === false ) {
- $text = $this->getNamedArgument( $name );
- }
- return $text;
- }
-
- /**
- * Return true if the frame is a template frame
- *
- * @return bool
- */
- function isTemplate() {
- return true;
- }
-}
-
-/**
- * Expansion frame with custom arguments
- * @ingroup Parser
- */
-class PPCustomFrame_HipHop extends PPFrame_HipHop {
- var $args;
-
- function __construct( $preprocessor, $args ) {
- parent::__construct( $preprocessor );
- $this->args = $args;
- }
-
- function __toString() {
- $s = 'cstmframe{';
- $first = true;
- foreach ( $this->args as $name => $value ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= ', ';
- }
- $s .= "\"$name\":\"" .
- str_replace( '"', '\\"', $value->__toString() ) . '"';
- }
- $s .= '}';
- return $s;
- }
-
- /**
- * @return bool
- */
- function isEmpty() {
- return !count( $this->args );
- }
-
- /**
- * @param $index
- * @return bool
- */
- function getArgument( $index ) {
- if ( !isset( $this->args[$index] ) ) {
- return false;
- }
- return $this->args[$index];
- }
-}
-
-/**
- * @ingroup Parser
- */
-class PPNode_HipHop_Tree implements PPNode {
- var $name, $firstChild, $lastChild, $nextSibling;
-
- function __construct( $name ) {
- $this->name = $name;
- $this->firstChild = $this->lastChild = $this->nextSibling = false;
- }
-
- function __toString() {
- $inner = '';
- $attribs = '';
- for ( $node = $this->firstChild; $node; $node = $node->nextSibling ) {
- if ( $node instanceof PPNode_HipHop_Attr ) {
- $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
- } else {
- $inner .= $node->__toString();
- }
- }
- if ( $inner === '' ) {
- return "<{$this->name}$attribs/>";
- } else {
- return "<{$this->name}$attribs>$inner</{$this->name}>";
- }
- }
-
- /**
- * @param $name
- * @param $text
- * @return PPNode_HipHop_Tree
- */
- static function newWithText( $name, $text ) {
- $obj = new self( $name );
- $obj->addChild( new PPNode_HipHop_Text( $text ) );
- return $obj;
- }
-
- function addChild( $node ) {
- if ( $this->lastChild === false ) {
- $this->firstChild = $this->lastChild = $node;
- } else {
- $this->lastChild->nextSibling = $node;
- $this->lastChild = $node;
- }
- }
-
- /**
- * @return PPNode_HipHop_Array
- */
- function getChildren() {
- $children = array();
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- $children[] = $child;
- }
- return new PPNode_HipHop_Array( $children );
- }
-
- function getFirstChild() {
- return $this->firstChild;
- }
-
- function getNextSibling() {
- return $this->nextSibling;
- }
-
- /**
- * @param $name string
- * @return array
- */
- function getChildrenOfType( $name ) {
- $children = array();
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- if ( isset( $child->name ) && $child->name === $name ) {
- $children[] = $child;
- }
- }
- return $children;
- }
-
- /**
- * @return bool
- */
- function getLength() {
- return false;
- }
-
- /**
- * @param $i
- * @return bool
- */
- function item( $i ) {
- return false;
- }
-
- /**
- * @return string
- */
- function getName() {
- return $this->name;
- }
-
- /**
- * Split a <part> node into an associative array containing:
- * name PPNode name
- * index String index
- * value PPNode value
- *
- * @throws MWException
- * @return array
- */
- function splitArg() {
- $bits = array();
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- if ( !isset( $child->name ) ) {
- continue;
- }
- if ( $child->name === 'name' ) {
- $bits['name'] = $child;
- if ( $child->firstChild instanceof PPNode_HipHop_Attr
- && $child->firstChild->name === 'index' )
- {
- $bits['index'] = $child->firstChild->value;
- }
- } elseif ( $child->name === 'value' ) {
- $bits['value'] = $child;
- }
- }
-
- if ( !isset( $bits['name'] ) ) {
- throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
- }
- if ( !isset( $bits['index'] ) ) {
- $bits['index'] = '';
- }
- return $bits;
- }
-
- /**
- * Split an <ext> node into an associative array containing name, attr, inner and close
- * All values in the resulting array are PPNodes. Inner and close are optional.
- *
- * @throws MWException
- * @return array
- */
- function splitExt() {
- $bits = array();
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- if ( !isset( $child->name ) ) {
- continue;
- }
- if ( $child->name === 'name' ) {
- $bits['name'] = $child;
- } elseif ( $child->name === 'attr' ) {
- $bits['attr'] = $child;
- } elseif ( $child->name === 'inner' ) {
- $bits['inner'] = $child;
- } elseif ( $child->name === 'close' ) {
- $bits['close'] = $child;
- }
- }
- if ( !isset( $bits['name'] ) ) {
- throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
- }
- return $bits;
- }
-
- /**
- * Split an <h> node
- *
- * @throws MWException
- * @return array
- */
- function splitHeading() {
- if ( $this->name !== 'h' ) {
- throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
- }
- $bits = array();
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- if ( !isset( $child->name ) ) {
- continue;
- }
- if ( $child->name === 'i' ) {
- $bits['i'] = $child->value;
- } elseif ( $child->name === 'level' ) {
- $bits['level'] = $child->value;
- }
- }
- if ( !isset( $bits['i'] ) ) {
- throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
- }
- return $bits;
- }
-
- /**
- * Split a <template> or <tplarg> node
- *
- * @throws MWException
- * @return array
- */
- function splitTemplate() {
- $parts = array();
- $bits = array( 'lineStart' => '' );
- for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
- if ( !isset( $child->name ) ) {
- continue;
- }
- if ( $child->name === 'title' ) {
- $bits['title'] = $child;
- }
- if ( $child->name === 'part' ) {
- $parts[] = $child;
- }
- if ( $child->name === 'lineStart' ) {
- $bits['lineStart'] = '1';
- }
- }
- if ( !isset( $bits['title'] ) ) {
- throw new MWException( 'Invalid node passed to ' . __METHOD__ );
- }
- $bits['parts'] = new PPNode_HipHop_Array( $parts );
- return $bits;
- }
-}
-
-/**
- * @ingroup Parser
- */
-class PPNode_HipHop_Text implements PPNode {
- var $value, $nextSibling;
-
- function __construct( $value ) {
- if ( is_object( $value ) ) {
- throw new MWException( __CLASS__ . ' given object instead of string' );
- }
- $this->value = $value;
- }
-
- function __toString() {
- return htmlspecialchars( $this->value );
- }
-
- function getNextSibling() {
- return $this->nextSibling;
- }
-
- function getChildren() { return false; }
- function getFirstChild() { return false; }
- function getChildrenOfType( $name ) { return false; }
- function getLength() { return false; }
- function item( $i ) { return false; }
- function getName() { return '#text'; }
- function splitArg() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitExt() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitHeading() { throw new MWException( __METHOD__ . ': not supported' ); }
-}
-
-/**
- * @ingroup Parser
- */
-class PPNode_HipHop_Array implements PPNode {
- var $value, $nextSibling;
-
- function __construct( $value ) {
- $this->value = $value;
- }
-
- function __toString() {
- return var_export( $this, true );
- }
-
- function getLength() {
- return count( $this->value );
- }
-
- function item( $i ) {
- return $this->value[$i];
- }
-
- function getName() { return '#nodelist'; }
-
- function getNextSibling() {
- return $this->nextSibling;
- }
-
- function getChildren() { return false; }
- function getFirstChild() { return false; }
- function getChildrenOfType( $name ) { return false; }
- function splitArg() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitExt() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitHeading() { throw new MWException( __METHOD__ . ': not supported' ); }
-}
-
-/**
- * @ingroup Parser
- */
-class PPNode_HipHop_Attr implements PPNode {
- var $name, $value, $nextSibling;
-
- function __construct( $name, $value ) {
- $this->name = $name;
- $this->value = $value;
- }
-
- function __toString() {
- return "<@{$this->name}>" . htmlspecialchars( $this->value ) . "</@{$this->name}>";
- }
-
- function getName() {
- return $this->name;
- }
-
- function getNextSibling() {
- return $this->nextSibling;
- }
-
- function getChildren() { return false; }
- function getFirstChild() { return false; }
- function getChildrenOfType( $name ) { return false; }
- function getLength() { return false; }
- function item( $i ) { return false; }
- function splitArg() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitExt() { throw new MWException( __METHOD__ . ': not supported' ); }
- function splitHeading() { throw new MWException( __METHOD__ . ': not supported' ); }
-}
* @var string A string uniquely identifying the version of the serialization structure,
* not including any sub-structures.
*/
- const SERIAL_VERSION_ID = '2013-01-23';
+ const SERIAL_VERSION_ID = '2013-02-07';
/**
* Returns the version ID that identifies the serialization structure used by
global $wgEnableEmail, $wgEnableUserEmail;
global $wgHiddenPrefs, $wgLoginLanguageSelector;
global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
- global $wgSecureLogin, $wgPasswordResetRoutes;
+ global $wgSecureLogin, $wgSecureLoginDefaultHTTPS, $wgPasswordResetRoutes;
$titleObj = $this->getTitle();
$user = $this->getUser();
$template->set( 'link', '' );
}
+ // Decide if we default stickHTTPS on
+ if ( $wgSecureLoginDefaultHTTPS && $this->mAction != 'submitlogin' && !$this->mLoginattempt ) {
+ $this->mStickHTTPS = true;
+ }
+
$resetLink = $this->mType == 'signup'
? null
: is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) );
if ( $start < 0 ) {
$start = 0;
}
- $groupedNumber = substr( $number , $start, $end -$start ) . $groupedNumber ;
+ $groupedNumber = substr( $number, $start, $end -$start ) . $groupedNumber ;
$end = $start;
if ( $numMatches > 1 ) {
// use the last pattern for the rest of the number
'nah' => 'Nāhuatl', # Nahuatl, en:Wikipedia writes Nahuatlahtolli, while another form is Náhuatl
'nan' => 'Bân-lâm-gú', # Min-nan -- (bug 8217) nan instead of zh-min-nan, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=n
'nap' => 'Nnapulitano', # Neapolitan
- 'nb' => "norsk (bokmål)\xE2\x80\x8E", # Norwegian (Bokmal)
+ 'nb' => "norsk bokmål", # Norwegian (Bokmal)
'nds' => 'Plattdüütsch', # Low German ''or'' Low Saxon
'nds-nl' => 'Nedersaksies', # aka Nedersaksisch: Dutch Low Saxon
'ne' => 'नेपाली', # Nepali
'niu' => 'Niuē', # Niuean
'nl' => 'Nederlands', # Dutch
'nl-informal' => "Nederlands (informeel)\xE2\x80\x8E", # Dutch (informal address ("je"))
- 'nn' => "norsk (nynorsk)\xE2\x80\x8E", # Norwegian (Nynorsk)
- 'no' => "norsk (bokmål)\xE2\x80\x8E", # Norwegian (falls back to nb).
+ 'nn' => "norsk nynorsk", # Norwegian (Nynorsk)
+ 'no' => "norsk bokmål", # Norwegian (falls back to nb).
'nov' => 'Novial', # Novial
'nrm' => 'Nouormand', # Norman
'nso' => 'Sesotho sa Leboa', # Northern Sotho
* @return string
*/
function translate( $text, $toVariant ) {
+ $this->loadTables();
/* From Kazakh interface, maybe we need it later
$breaks = '[^\w\x80-\xff]';
// regexp for roman numbers
$matches = preg_split( $reg, $text, -1, PREG_SPLIT_OFFSET_CAPTURE );
$m = array_shift( $matches );
+ $this->loadTables();
if ( !isset( $this->mTables[$toVariant] ) ) {
throw new MWException( "Broken variant table: " . implode( ',', array_keys( $this->mTables ) ) );
}
'tog-underline' => 'ܪܫܘܡ ܣܪܛܐ ܬܚܝܬ ܐܣܪܐ:',
'tog-justify' => 'ܫܘܐ ܦܬܓܡ̈ܐ',
'tog-hideminor' => 'ܛܫܝ ܫܘܚܠܦ̈ܐ ܙܥܘܪ̈ܐ ܒܫܘܚܠܦ̈ܐ ܚܕ̈ܬܐ',
+'tog-hidepatrolled' => 'ܛܫܝ ܫܘܚܠܦ̈ܐ ܟܪ̈ܝܟܐ ܒܫܘܚܠܦ̈ܐ ܚܕ̈ܬܐ',
+'tog-newpageshidepatrolled' => 'ܛܫܝ ܦܐܬܬ̈ܐ ܟܪ̈ܝܟܬܐ ܡܢ ܡܟܬܒܘܬܐ ܕܦܐܬܐ ܚܕܬܐ',
'tog-extendwatchlist' => 'ܐܪܘܚ ܪ̈ܗܝܬܐ ܠܚܘܘܝܐ ܕܟܠܗܘܢ ܫܘܚܠܦ̈ܐ، ܠܐ ܚܕ̈ܬܐ ܒܠܚܘܕ',
'tog-editondblclick' => 'ܫܚܠܦ ܦܐܬ̈ܐ ܬܪ ܢܩܪܐ ܙܘܓܢܝܐ (ܣܢܝܩ ܠ JavaScript)',
'tog-editsection' => 'ܡܫܟܚ ܫܘܚܠܦܐ ܕܦܘܣܩ̈ܐ ܒܐܘܪܚܐ ܕܐܝܨܘܪ̈ܐ [ܫܚܠܦ]',
# General errors
'error' => 'ܦܘܕܐ',
'databaseerror' => 'ܦܘܕܐ ܒܐܣ ܝܕ̈ܥܬܐ',
-'missingarticle-rev' => '(ܬܢÜ\9dܬÜ\90#: $1)',
+'missingarticle-rev' => '(Ü¡Ü¢Ü\9dÜ¢Ü\90 Ü\95ܬܢÜ\9dܬÜ\90: $1)',
'missingarticle-diff' => '(ܦܘܪܫܐ: $1, $2)',
'internalerror' => 'ܦܘܕܐ ܓܘܝܐ',
'internalerror_info' => 'ܦܘܕܐ ܓܘܝܐ: $1',
'movethispage' => 'ܫܢܝ ܦܐܬܐ ܗܕܐ',
'notargettitle' => 'ܕܠܐ ܢܘܦܐ',
'nopagetitle' => 'ܠܝܬ ܗܟܘܬ ܦܐܬܐ ܕܢܘܦܐ',
+'nopagetext' => 'ܦܐܬܐ ܕܢܘܦܐ ܕܬܬܚܡܬ ܠܝܬ ܠܗ ܐܝܬܘܬܐ.',
'pager-newer-n' => '{{PLURAL:$1|1 1 ܚܕܬܐ|$1 ܚܕ̈ܬܐ}}',
'pager-older-n' => '{{PLURAL:$1|1 ܥܬܝܩܐ|$1 ܥܬܝܩ̈ܐ}}',
'suppress' => 'ܚܝܘܪܐ',
'speciallogtitlelabel' => 'ܢܘܦܐ (ܟܘܢܝܐ ܐܘ ܡܦܠܚܢܐ):',
'log' => 'ܣܓܠ̈ܐ',
'all-logs-page' => 'ܟܠ ܣܓܠ̈ܐ ܓܘܢܝ̈ܐ',
+'alllogstext' => 'ܓܠܚܐ ܟܠܢܝܐ ܠܟܠ ܣܓܠ̈ܐ ܡܪ ܐܝܬܘܬܐ ܒ{{SITENAME}}.
+ܡܨܬ ܕܬܙܥܪ ܠܦܠܛܐ ܒܓܒܝܬܐ ܕܐܕܫܐ ܕܣܓܠܐ ܐܘ ܫܡܐ ܕܡܦܠܚܢܐ (ܪܓܫܬܢܐ ܠܐܕܫܐ ܕܐܬܘܬܐ) ܐܘ ܦܐܬܐ ܬܘܥܒܕܬܐ (ܐܦ ܪܓܫܬܢܐ ܠܐܕܫܐ ܕܐܬܘܬܐ).',
'logempty' => 'ܠܝܬ ܡܠܘܐ̈ܐ ܠܐ̈ܝܡܐ ܒܣܓܠܐ ܗܢܐ.',
'log-title-wildcard' => 'ܒܨܝ ܥܠ ܟܘܢܝ̈ܐ ܕܫܪܝܢ ܥܡ ܟܬܒܬܐ ܗܕܐ',
'showhideselectedlogentries' => 'ܚܘܝ/ܛܫܝ ܣܓܠ̈ܐ ܕܥܠܠܐ ܓܒܝ̈ܐ',
'logentry-move-move-noredirect' => '$1 ܫܢܐ ܦܐܬܐ ܕ $3 ܠ $4 ܕܠܐ ܫܒܩܐ ܦܐܬܐ ܕܨܘܝܒܐ',
'logentry-move-move_redir' => '$1 ܫܢܐ ܦܐܬܐ ܕ $3 ܠ $4 ܕܐܝܬܘܗܝ ܦܐܬܐ ܕܨܘܝܒܐ',
'logentry-move-move_redir-noredirect' => '$1 ܫܢܐ ܦܐܬܐ ܕ $3 ܠ $4 ܕܐܝܬܘܗܝ ܦܐܬܐ ܕܨܘܝܒܐ ܘܕܠܐ ܫܒܩܐ ܦܐܬܐ ܕܨܘܝܒܐ',
+'logentry-patrol-patrol-auto' => '$1 ܝܬܐܝܬ ܫܘܕܥ ܬܢܝܬܐ $4 ܕܦܐܬܐ $3 ܟܪܝܟܬܐ',
'logentry-newusers-newusers' => 'ܚܘܫܒܢܐ ܕܡܦܠܚܢܐ $1 ܐܬܒܪܐ',
'logentry-newusers-create' => 'ܚܘܫܒܢܐ ܕܡܦܠܚܢܐ $1 ܐܬܒܪܐ',
'logentry-newusers-create2' => 'ܚܘܫܒܢܐ ܕܡܦܠܚܢܐ $3 ܐܬܒܪܐ ܒܝܕ $1',
Si prefieres nun lo facer, asegúrate de que nun dexes [[Special:DoubleRedirects|redireiciones dobles]] o [[Special:BrokenRedirects|rotes]].
Tu yes el responsable de facer que los enllaces queden apuntando au se supón que tienen d'apuntar.
-Recuerda que la páxina '''nun''' va movese si yá hai una páxina col nuevu títulu, a nun ser que seya una redireición y nun tenga historial.
+Recuerda que la páxina '''nun''' va movese si yá hai una páxina col nuevu títulu, a nun ser que la mesma seya una redireición y nun tenga historial.
Esto significa que pues volver a renomar una páxina col nome orixinal si t'enquivoques, y nun pues sobreescribir una páxina yá esistente.
-¡AVISU!'''
-Esti pue ser un cambéu importante y inesperáu pa una páxina popular;
+¡Avisu!'''
+Esti pue ser un cambéu importante ya inesperáu pa una páxina popular;
por favor, asegúrate d'entender les consecuencies de lo que vas facer enantes de siguir.",
'movepagetext-noredirectfixer' => "Usando'l siguiente formulariu vas renomar una páxina, treslladando'l so historial al nuevu nome.
El nome vieyu va convertise nuna redireición al nuevu.
'disclaimerpage' => 'Project:सामान्य अस्विकरण',
'edithelp' => 'मदद सम्पादन',
'edithelppage' => 'Help:सम्पादन',
+'helppage' => 'मदद:सामग्री',
'mainpage' => 'मुख्य पन्ना',
'mainpage-description' => 'पहिलका पन्ना',
'portal' => 'सामुदायिक पन्ना',
'tooltip-pt-mytalk' => 'राउर वार्ता पन्ना',
'tooltip-pt-preferences' => 'राउर पसन्द',
'tooltip-pt-mycontris' => 'राउर योगदान के सूची',
-'tooltip-pt-login' => 'रà¤\89à¤\86 à¤\95à¥\87 à¤\96ाता पà¥\8dरवà¥\87श à¤\96ातिर पà¥\8dरà¥\8bतà¥\8dसाहित à¤\95रल à¤\9cा रहल बा, बाà¤\81à¤\95ि à¤\88 à¤\85निवारà¥\8dय नà¤\88à¤\96à¥\87',
+'tooltip-pt-login' => 'रउआ के खाता प्रवेश खातिर प्रोत्साहित करल जा रहल बा, बाकि ई अनिवार्य नईखे',
'tooltip-pt-anonlogin' => 'रउआ के खाता प्रवेश खातिर प्रोत्साहित करल जा रहल बा, बाँकि ई अनिवार्य नईखे',
'tooltip-pt-logout' => 'खाता से बाहर',
'tooltip-ca-talk' => 'सामग्री पन्ना के बारे में बात-चीत',
'file-anchor-link' => 'Dosya',
'filehist' => 'Ravêrdê dosya',
'filehist-help' => 'bıploxne ser yew tarih u aye tarih dı versionê dosya bıvin.',
-'filehist-deleteall' => 'hemî biestere',
+'filehist-deleteall' => 'pêro bestere',
'filehist-deleteone' => 'bestere',
'filehist-revert' => 'reyna biyere',
'filehist-current' => 'nıkayên',
'movenosubpage' => 'pelê bınıni yê no peli çino.',
'movereason' => 'Sebeb:',
'revertmove' => 'peyser bia',
-'delete_and_move' => 'Biestere u bere',
+'delete_and_move' => 'Bestere u bere',
'delete_and_move_text' => '==gani hewn a bıbıo/bıesteriyo==
" no [[:$1]]" name de yew pel ca ra esto. şıma wazeni pê hewn a kerdışê ey peli vurnayişê nameyi bıkeri?',
-'delete_and_move_confirm' => 'Ya, ena pele biestere',
+'delete_and_move_confirm' => 'Heya, na pele bestere',
'delete_and_move_reason' => '"[[$1]]" qey vurnayişê nameyi esteriya',
'selfmove' => 'name yo ke şıma wazeni bıbo, ın name û name yo ke ca ra esto eyni yê /zepê yê. vurnayiş mumkin niyo.',
'immobile-source-namespace' => '"$1" pelê cayi de nameyi nêkırışyenî',
# action=purge
'confirm_purge_button' => 'Temam',
-'confirm-purge-top' => 'Cacheyê eno pel biestere?',
+'confirm-purge-top' => 'Vervirê na pele bestere?',
'confirm-purge-bottom' => 'Purge kerdişê yew pel cacheyî estereno u revizyonê penîyî mucneno.',
# action=watch/unwatch
'gotaccount' => "Maš južo wužywarske konto? '''$1'''.",
'gotaccountlink' => 'Pśizjawiś se',
'userlogin-resetlink' => 'Sy pśizjawjeńske daty zabył?',
-'createaccountmail' => 'z e-mailku',
+'createaccountmail' => 'Nachylne pśidatne gronidło wužywaś a jo na slědujucu e-mailowu adresu pósłaś',
'createaccountreason' => 'Pśicyna:',
'badretype' => 'Šćitnej gronidle, kótarejž sy zapódał, se njemakajotej.',
'userexists' => 'Wužywarske mě se južo wužywa.
'''NJEWÓZJAW WÓT COPYRIGHTA ŠĆITANE ŹĚŁA MIMO DOWÓLNOSĆI!'''",
'copyrightwarning2' => "Pšosym buź se togo wědobny, až wšykne pśinoski na {{SITENAME}} mógu wót drugich wužywarjow se wobźěłaś, narownaś abo wulašowaś. Jolic až njocoš, až twój tekst se mimo zmilnosći wobźěłujo, ga pón jen how njeskładuj.<br /> Ty teke wobkšuśijoš, až sy tekst sam napisał abo sy jen wót public domainy resp. wót pódobneje lichotneje resursy kopěrował (glědaj $1 za dalše detaile). '''NJEWÓZJAW WÓT COPYRIGHTA ŠĆITANE ŹĚŁA MIMO DOWÓLNOSĆI!'''",
'longpageerror' => "'''Zmólka: Tekst, kótaryž coš składowaś, jo {{PLURAL:$1| jaden kilobajt|$1 kilobajta|$1 kilobajty|$1 kilobajtow}} wjeliki. To jo wěcej ako dowólony maksimum {{PLURAL:$2|jaden kilobajt|$1 kilobajta|$1 kilobajty|$1 kilobajtow}}.''' Składowanje njejo móžno.",
-'readonlywarning' => "'''WARNOWANJE: Datowa banka jo se za wótwardowanje zacyniła, togodla njebuźo tuchylu móžno, twóje změny składowaś. Jolic až coš, ga móžoš tekst do tekstoweje dataje kopěrowaś a pózdźej składowaś.'''
+'readonlywarning' => "'''WARNOWANJE: Datowa banka jo se za wótwardowanje zacyniła, togodla njebuźo tuchylu móžno, twóje změny składowaś.'''
+Jolic coš, ga móžoš tekst do tekstoweje dataje kopěrowaś a pózdźej składowaś.
Administrator, kenž jo ju zastajił, su toś tu pśicynu pódał: $1",
'protectedpagewarning' => "'''Warnowanje: Toś ten bok jo se zastajił, tak až jano wužywarje z pšawami administratora mógu jen wobźěłaś.'''
# Special:ActiveUsers
'activeusers' => 'Lisćina aktiwnych wužywarjow',
'activeusers-intro' => 'To jo lisćina wužywarjow, kotrež su byli aktiwne za {{PLURAL:$1|slědny źeń|slědnej $1 dnja|slědne $1 dny|slědnych $1 dnjow}}.',
-'activeusers-count' => '$1 {{PLURAL:$1|změna|změnje|změny|změnow}} w {{PLURAL:$3|slědnem dnju|slědnyma $3 dnjoma|slědnych $3 dnjach|slědnych $3 dnjach}}',
+'activeusers-count' => '$1 {{PLURAL:$1|akcija|akciji|akcije|akcijow}} w {{PLURAL:$3|slědnem dnju|slědnyma $3 dnjoma|slědnych $3 dnjach}}',
'activeusers-from' => 'Wužywarjow zwobrazniś, zachopinajucy z:',
'activeusers-hidebots' => 'Boty schowaś',
'activeusers-hidesysops' => 'Administratorow schowaś',
Jolic njocoš, pśeglědaj za [[Special:DoubleRedirects|dwójnymi]] abo [[Special:BrokenRedirects|defektnymi daleposrědkowanjami]].
Sy zagronity, až wótkaze wjedu tam, źož maju wjasć.
-Źiwaj na to, až se bok '''nje'''pśesuwa, jolic jo južo bok z nowym titelom, snaźkuli jo prozny abo dalejpósrědnjenje a njama stare wobźěłane wersije. To ma groniś, až móžoš bok zasej slědk pśemjenjowaś, jolic cyniš zmólku, a njemóžoš eksistěrujucy bok pśepisaś.
+Źiwaj na to, až se bok '''nje'''pśesuwa, jolic jo južo bok z nowym titelom, snaźkuli slědny jo dalejpósrědnjenje a njama stare wobźěłane wersije. To ma groniś, až móžoš bok zasej slědk pśemjenjowaś, jolic cyniš zmólku, a njemóžoš eksistěrujucy bok pśepisaś.
'''WARNOWANJE!'''
To móžo byś drastiska a njewocakowana změna za popularny bok;
'specialpages-group-highuse' => 'Cesto wužywane boki',
'specialpages-group-pages' => 'Lisćiny bokow',
'specialpages-group-pagetools' => 'Rědy bokow',
-'specialpages-group-wiki' => 'Wikijowe daty a rědy',
+'specialpages-group-wiki' => 'Daty a rědy',
'specialpages-group-redirects' => 'Dalej pósrědnjajuce boki',
'specialpages-group-spam' => 'Spamowe rědy',
'logentry-newusers-newusers' => 'Wužywarske konto $1 jo se załožyło',
'logentry-newusers-create' => 'Wužywarske konto $1 jo se załožyło',
'logentry-newusers-create2' => '$1 jo załožył wužywarske konto $3',
+'logentry-newusers-byemail' => 'Wužywarske konto $3 jo se wót $1 załožyło a gronidło jo se pśez e-mail pósłało.',
'logentry-newusers-autocreate' => 'Konto $1 jo se awtomatiski załožyło',
'logentry-rights-rights' => '$1 jo kupkowe cłonkojstwo za $3 z $4 do $5 změnił',
'logentry-rights-rights-legacy' => '$1 jo kupkowe cłonkojstwo za $3 změnił',
'api-error-ok-but-empty' => 'Nutśikowna zmólka: Žedne wótegrono wót serwera.',
'api-error-overwrite' => 'Pśepisowanje eksistujuceje dataje njejo dowólone.',
'api-error-stashfailed' => 'Nutśikowna zmólka: Serwer njejo mógał temporernu dataju składowaś.',
+'api-error-publishfailed' => 'Nutśkowna zmólka: Serwer njejo mógł nachylnu dataju wozjawiś.',
'api-error-timeout' => 'Serwer njejo we wócakanem casu wótgronił.',
'api-error-unclassified' => 'Njeznata zmólka jo nastała.',
'api-error-unknown-code' => 'Njeznata zmólka: "$1"',
'pageinfo-robot-noindex' => 'Not indexable',
'pageinfo-views' => 'Number of views',
'pageinfo-watchers' => 'Number of page watchers',
+'pageinfo-few-watchers' => 'Fewer than $1 {{PLURAL:$1|watcher|watchers}}',
'pageinfo-redirects-name' => 'Redirects to this page',
'pageinfo-redirects-value' => '$1', # only translate this message to other languages if you have to change it
'pageinfo-subpages-name' => 'Subpages of this page',
Jos et halua tätä tehtävän automaattisesti, muista tehdä tarkistukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta.
Olet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.
-Huomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi milloin kyseessä on ohjaus, jolla ei ole muokkaushistoriaa.
+Huomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.
Tämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.
Tämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että tiedät seuraukset ennen kuin siirrät sivun.",
Tarkasta sivuun viittaavat ohjaukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta. Olet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.
-Huomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi milloin kyseessä on tyhjä sivu tai ohjaus, jolla ei ole muokkaushistoriaa. Tämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.
+Huomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.
+Tämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.
Tämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että tiedät seuraukset ennen kuin siirrät sivun.",
'movepagetalktext' => "Sivuun mahdollisesti kytketty keskustelusivu siirretään automaattisesti, '''paitsi jos''':
'dec' => 'déc',
# Categories related messages
-'pagecategories' => 'Catégorie{{PLURAL:$1||s}}',
+'pagecategories' => '{{PLURAL:$1|Catégorie|Catégories}}',
'category_header' => 'Pages dans la catégorie « $1 »',
'subcategories' => 'Sous-catégories',
'category-media-header' => 'Fichiers multimédias dans la catégorie « $1 »',
'move-page-legend' => 'Renommer une page',
'movepagetext' => "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom. L'ancien titre deviendra une page de redirection vers le nouveau titre. Vous pouvez mettre à jour automatiquement les redirections actuelles qui pointent vers le titre original. Si vous choisissez de ne pas le faire, assurez-vous de vérifier toute [[Special:DoubleRedirects|double redirection]] ou [[Special:BrokenRedirects|redirection cassée]]. Vous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.
-Notez que la page ne sera '''pas''' renommée s'il existe déjà une page avec le nouveau titre, sauf si cette dernière a un historique de modifications vierge et est une simple redirection. Ceci permet de renommer une page vers sa position d'origine si le déplacement s'avère erroné.
+Notez que la page ne sera '''pas''' renommée s'il existe déjà une page avec le nouveau titre, sauf si cette dernière est une simple redirection avec un historique de modifications vierge. Ceci permet de renommer une page vers sa position d'origine si le déplacement s'avère erroné.
'''Attention !'''
Ceci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d'en avoir compris les conséquences avant de continuer.",
'license-header' => 'Licence',
'nolicense' => 'Pas yona chouèsia',
'license-nopreview' => '(Apèrçu pas disponiblo)',
-'upload_source_url' => ' (una URL valida et accèssibla publicament)',
-'upload_source_file' => ' (un fichiér sur voutron ordenator)',
+'upload_source_url' => ' (n’URL justa et accèssibla publicament)',
+'upload_source_file' => ' (un fichiér sur voutron ordenator)',
# Special:ListFiles
'listfiles-summary' => 'Ceta pâge spèciâla montre tôs los fichiérs tèlèchargiês.
-Quand el est filtrâ per usanciér, solament los fichiérs que la vèrsion la ples novèla at étâ importâ per cél usanciér sont montrâs.',
-'listfiles_search_for' => 'Rechèrchiér un nom de mèdia :',
+Quand el est filtrâye per utilisator, solament los fichiérs que la vèrsion la ples novèla est étâye tèlèchargiêe per cél utilisator sont montrâs.',
+'listfiles_search_for' => 'Rechèrchiér un nom de fichiér mèdia :',
'imgfile' => 'fichiér',
-'listfiles' => 'Lista des fichiérs',
+'listfiles' => 'Lista de fichiérs',
'listfiles_thumb' => 'Figura',
'listfiles_date' => 'Dâta',
'listfiles_name' => 'Nom',
-'listfiles_user' => 'Usanciér',
+'listfiles_user' => 'Utilisator',
'listfiles_size' => 'Talye',
'listfiles_description' => 'Dèscripcion',
'listfiles_count' => 'Vèrsions',
# File description page
'file-anchor-link' => 'Fichiér',
'filehist' => 'Historico du fichiér',
-'filehist-help' => 'Clicar sur na dâta et hora por vêre lo fichiér coment il ére a cél moment.',
+'filehist-help' => 'Clicar sur na dâta / hora por vêre lo fichiér coment il ére a cél moment.',
'filehist-deleteall' => 'suprimar tot',
'filehist-deleteone' => 'suprimar',
-'filehist-revert' => 'rètablir',
-'filehist-current' => 'ora',
-'filehist-datetime' => 'Dâta et hora',
+'filehist-revert' => 'rèvocar',
+'filehist-current' => 'd’ora',
+'filehist-datetime' => 'Dâta / hora',
'filehist-thumb' => 'Figura',
-'filehist-thumbtext' => 'Figura por la vèrsion du $1',
-'filehist-nothumb' => 'Gins de figura',
-'filehist-user' => 'Usanciér',
+'filehist-thumbtext' => 'Figura por la vèrsion du $2 a $3',
+'filehist-nothumb' => 'Niona figura',
+'filehist-user' => 'Utilisator',
'filehist-dimensions' => 'Dimensions',
'filehist-filesize' => 'Talye du fichiér',
'filehist-comment' => 'Comentèro',
'filehist-missing' => 'Fichiér manquent',
'imagelinks' => 'Usâjo du fichiér',
-'linkstoimage' => '{{PLURAL:$1|Ceta pâge utilise|Cetes $1 pâges utilisont}} ceti fichiér :',
-'linkstoimage-more' => 'Més de {{PLURAL:$1|yona pâge utilise|$1 pâges utilisont}} ceti fichiér.
-Ceta lista montre ren que {{PLURAL:$1|la premiére pâge qu’utilise|les $1 premiéres pâges qu’utilisont}} ceti fichiér.
-Una [[Special:WhatLinksHere/$2|lista complèta]] est disponibla.',
-'nolinkstoimage' => 'Niona pâge utilise ceti fichiér.',
+'linkstoimage' => '{{PLURAL:$1|Cela pâge-que emplèye|Celes $1 pâges-que emplèyont}} ceti fichiér :',
+'linkstoimage-more' => 'Més {{PLURAL:$1|d’una pâge emplèye|de $1 pâges emplèyont}} ceti fichiér.
+Ceta lista montre ren que {{PLURAL:$1|la premiére pâge qu’emplèye|les $1 premiéres pâges qu’emplèyont}} ceti fichiér.
+Na [[Special:WhatLinksHere/$2|lista complèta]] est disponibla.',
+'nolinkstoimage' => 'Niona pâge emplèye ceti fichiér.',
'morelinkstoimage' => 'Vêde [[Special:WhatLinksHere/$1|més de lims]] de vers ceti fichiér.',
'linkstoimage-redirect' => '$1 (redirèccion de fichiér) $2',
-'duplicatesoffile' => '{{PLURAL:$1|Ceti fichiér est un doblo|Cetos fichiérs sont des doblos}} de ceti ([[Special:FileDuplicateSearch/$2|més de dètalys]]) :',
-'sharedupload' => 'Ceti fichiér vint de $1 et pôt étre utilisâ per d’ôtros projèts.',
-'sharedupload-desc-there' => 'Ceti fichiér vint de $1 et pôt étre utilisâ per d’ôtros projèts.
-Vêde sa [$2 pâge de dèscripcion] por més d’enformacions.',
-'sharedupload-desc-here' => 'Ceti fichiér vint de $1 et pôt étre utilisâ per d’ôtros projèts.
-La dèscripcion de sa [$2 pâge de dèscripcion] est montrâ ce-desot.',
+'duplicatesoffile' => '{{PLURAL:$1|Cél fichiér-que est un doblo|Celos $1 fichiérs-que sont des doblos}} de ceti ([[Special:FileDuplicateSearch/$2|més de dètalys]]) :',
+'sharedupload' => 'Ceti fichiér vint de $1 et pôt étre empleyê per d’ôtros projèts.',
+'sharedupload-desc-there' => 'Ceti fichiér vint de $1 et pôt étre empleyê per d’ôtros projèts.
+Se vos plét, vêde la sina [$2 pâge de dèscripcion] por més d’enformacions.',
+'sharedupload-desc-here' => 'Ceti fichiér vint de $1 et pôt étre empleyê per d’ôtros projèts.
+La dèscripcion de la sina [$2 pâge de dèscripcion] est montrâye ce-desot.',
+'sharedupload-desc-edit' => 'Ceti fichiér vint de $1 et pôt étre empleyê per d’ôtros projèts.
+Pôt-étre vos voléd changiér la dèscripcion sur la sina [$2 pâge de dèscripcion].',
+'sharedupload-desc-create' => 'Ceti fichiér vint de $1 et pôt étre empleyê per d’ôtros projèts.
+Pôt-étre vos voléd changiér la dèscripcion sur la sina [$2 pâge de dèscripcion].',
'filepage-nofile' => 'Nion fichiér de cél nom ègziste.',
'filepage-nofile-link' => 'Nion fichiér de cél nom ègziste, mas vos en pouede [$1 tèlèchargiér yon].',
-'uploadnewversion-linktext' => 'Tèlèchargiér una novèla vèrsion de ceti fichiér',
-'shared-repo-from' => 'de $1',
+'uploadnewversion-linktext' => 'Tèlèchargiér na novèla vèrsion de ceti fichiér',
+'shared-repo-from' => 'de : $1',
'shared-repo' => 'un dèpôt partagiê',
'filepage.css' => '/* Lo code CSS betâ ique est encllu dens la pâge de dèscripcion du fichiér, et pués dens los vouiquis cliants ètrangiérs. */',
+'upload-disallowed-here' => 'Vos pouede pas ècllafar ceti fichiér.',
# File reversion
-'filerevert' => 'Rètablir $1',
-'filerevert-legend' => 'Rètablir lo fichiér',
-'filerevert-intro' => "Vos éte prèst a rètablir lo fichiér '''[[Media:$1|$1]]''' a la [$4 vèrsion du $2 a $3].",
+'filerevert' => 'Rèvocar $1',
+'filerevert-legend' => 'Rèvocar lo fichiér',
+'filerevert-intro' => "Vos éte prèst a rèvocar lo fichiér '''[[Media:$1|$1]]''' a la [$4 vèrsion du $2 a $3].",
'filerevert-comment' => 'Rêson :',
-'filerevert-defaultcomment' => 'Vèrsion du $1 a $2 rètablia',
-'filerevert-submit' => 'Rètablir',
-'filerevert-success' => "'''[[Media:$1|$1]]''' at étâ rètabli a la [$4 vèrsion du $2 a $3].",
-'filerevert-badversion' => 'Y at gins de vèrsion ples vielye du fichiér avouéc la dâta balyê.',
+'filerevert-defaultcomment' => 'Rèvocâ a la vèrsion du $1 a $2',
+'filerevert-submit' => 'Rèvocar',
+'filerevert-success' => "'''[[Media:$1|$1]]''' est étâ rèvocâ a la [$4 vèrsion du $2 a $3].",
+'filerevert-badversion' => 'Y at gins de vèrsion locala devant de cél fichiér avouéc l’horodatâjo balyê.',
# File deletion
'filedelete' => 'Suprimar $1',
'filedelete-legend' => 'Suprimar lo fichiér',
-'filedelete-intro' => "Vos éte prèst a suprimar '''[[Media:$1|$1]]''' et pués tot son historico.",
+'filedelete-intro' => "Vos éte prèst a suprimar lo fichiér '''[[Media:$1|$1]]''' et pués tot lo sin historico.",
'filedelete-intro-old' => "Vos éte aprés suprimar la vèrsion de '''[[Media:$1|$1]]''' du [$4 $2 a $3].",
'filedelete-comment' => 'Rêson :',
'filedelete-submit' => 'Suprimar',
-'filedelete-success' => "'''$1''' at étâ suprimâ.",
-'filedelete-success-old' => "La vèrsion de '''[[Media:$1|$1]]''' du $2 a $3 at étâ suprimâ.",
+'filedelete-success' => "'''$1''' est étâ suprimâ.",
+'filedelete-success-old' => "La vèrsion de '''[[Media:$1|$1]]''' du $2 a $3 est étâye suprimâye.",
'filedelete-nofile' => "'''$1''' ègziste pas.",
-'filedelete-nofile-old' => "Ègziste gins de vèrsion arch·ivâ de '''$1''' avouéc los atributs spècefiâs.",
+'filedelete-nofile-old' => "Ègziste gins de vèrsion arch·ivâye de '''$1''' avouéc los atributs spècifiâs.",
'filedelete-otherreason' => 'Ôtra rêson / rêson de ples :',
'filedelete-reason-otherlist' => 'Ôtra rêson',
'filedelete-reason-dropdown' => '*Rêsons corentes de suprèssion
** Violacion du drêt d’ôtor
** Fichiér en doblo',
'filedelete-edit-reasonlist' => 'Changiér les rêsons de suprèssion',
-'filedelete-maintenance' => 'La suprèssion et la rèstoracion de fichiérs est dèsactivâ temporèrament pendent la mantegnence.',
-'filedelete-maintenance-title' => 'Empossiblo de suprimar lo fichiér',
+'filedelete-maintenance' => 'La suprèssion et la rèstoracion de fichiérs est dèsactivâye temporèrament pendent la mantegnence.',
+'filedelete-maintenance-title' => 'Y at pas moyen de suprimar lo fichiér',
# MIME search
-'mimesearch' => 'Rechèrche per tipo de contegnu MIME',
-'mimesearch-summary' => "Ceta pâge vos pèrmèt de listar los fichiérs accèssiblos per ceti vouiqui d’aprés lor tipo de contegnu MIME.
-Entrâ : ''tipo de contegnu''/''sot-tipo'', per ègzemplo <code>image/jpeg</code>.",
+'mimesearch' => 'Rechèrche per tipo MIME',
+'mimesearch-summary' => "Ceta pâge pèrmèt de filtrar los fichiérs per lor tipo MIME.
+Entrâ : ''tipodecontegnu''/''sot-tipo'', per ègzemplo <code>image/jpeg</code>.",
'mimetype' => 'Tipo MIME :',
-'download' => 'Tèlèchargiér',
+'download' => 'tèlèchargiér',
# Unwatched pages
'unwatchedpages' => 'Pâges pas siuvues',
'listredirects' => 'Lista de les redirèccions',
# Unused templates
-'unusedtemplates' => 'Modèlos inutilisâs',
+'unusedtemplates' => 'Modèlos pas empleyês',
'unusedtemplatestext' => 'Ceta pâge liste totes les pâges de l’èspâço de noms « {{ns:template}} » que sont pas entrebetâyes dedens niona ôtra pâge.
Oubliâd pas de controlar s’y at gins d’ôtro lim de vers los modèlos devant que los suprimar.',
'unusedtemplateswlh' => 'ôtros lims',
# Move page
'move-page' => 'Mover "$1"',
'move-page-legend' => 'Mover páxina',
-'movepagetext' => "Ao usar o formulario de embaixo vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.
+'movepagetext' => "Ao usar o formulario inferior vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.
O título vello vaise converter nunha páxina de redirección ao novo título.
Pode actualizar automaticamente as redireccións que van dar ao título orixinal.
Se escolle non facelo, asegúrese de verificar que non hai redireccións [[Special:DoubleRedirects|dobres]] ou [[Special:BrokenRedirects|crebadas]].
Vostede é responsábel de asegurarse de que as ligazóns continúan a apuntar cara a onde se supón que deberían.
-Teña en conta que a páxina '''non''' será movida se xa existe unha páxina co novo título, a menos que sexa unha redirección e non teña historial de edicións.
+Teña en conta que a páxina '''non''' será trasladada se xa existe unha páxina co novo título, a menos que esta última sexa unha redirección e non teña historial de edicións.
Isto significa que pode volver renomear unha páxina ao seu nome antigo se comete un erro, e que non pode sobrescribir unha páxina que xa existe.
'''Atención!'''
Este cambio nunha páxina popular pode ser drástico e inesperado;
por favor, asegúrese de que entende as consecuencias disto antes de proseguir.",
-'movepagetext-noredirectfixer' => "Ao usar o formulario de embaixo vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.
+'movepagetext-noredirectfixer' => "Ao usar o formulario inferior vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.
O título vello vaise converter nunha páxina de redirección ao novo título.
Asegúrese de verificar que non hai redireccións [[Special:DoubleRedirects|dobres]] ou [[Special:BrokenRedirects|crebadas]].
Vostede é responsábel de asegurarse de que as ligazóns continúan a apuntar cara a onde se supón que deberían.
-Teña en conta que a páxina '''non''' será movida se xa existe unha páxina co novo título, a menos que sexa unha redirección e non teña historial de edicións.
+Teña en conta que a páxina '''non''' será trasladada se xa existe unha páxina co novo título, a menos que esta última sexa unha redirección e non teña historial de edicións.
Isto significa que pode volver renomear unha páxina ao seu nome antigo se comete un erro, e que non pode sobrescribir unha páxina que xa existe.
'''Atención!'''
'nohistory' => 'Njeje žanych staršich wersijow strony.',
'currentrev' => 'Aktualna wersija',
'currentrev-asof' => 'Aktualna wersija wot $1',
-'revisionasof' => 'Wersija z $1',
+'revisionasof' => 'Wersija wot $1',
'revision-info' => 'Wersija wot $1 wužiwarja $2',
'previousrevision' => '← Starša wersija',
'nextrevision' => 'Nowša wersija →',
# Diffs
'history-title' => '$1: Wersijowe stawizny',
-'difference-title' => '$1: Rozdźěl mjez wersijemi',
+'difference-title' => '$1: Rozdźěl mjez wersijomaj',
'difference-title-multipage' => '$1 a $2: Rozdźěl mjez stronami',
'difference-multipage' => '(Rozdźěl mjez stronami)',
'lineno' => 'Rjadka $1:',
'move-page-legend' => 'Stronu přesunyć',
'movepagetext' => "Wužiwanje formulara deleka budźe stronu přemjenować, suwajo jeje cyłe stawizny pod nowe mjeno. Stary titl budźe daleposrědkowanje na nowy titl. Móžeš dalesposrědkowanja, kotrež na prěnjotny titl pokazać, awtomatisce aktualizować. Pruwuj za [[Special:DoubleRedirects|dwójnymi]] abo [[Special:BrokenRedirects|skóncowanymi daleposrědkowanjemi]]. Dyrbiš zaručić, zo wotkazy na stronu pokazuja, na kotruž dyrbja dowjesć.
-Wobkedźbuj, zo strona so '''nje''' přesunje, jeli strona z nowym titlom hizo eksistuje, chibazo wona je prózdna abo dalesposrědkowanje a nima zašłe stawizny. To woznamjenja, zo móžeš stronu tam wróćo přemjenować, hdźež bu runje přemjenowana, jeli zmylk činiš a njemóžeš wobstejacu stronu přepisować.
+Wobkedźbuj, zo strona so '''nje'''přesunje, jeli strona z nowym titlom hizo eksistuje, chibazo poslednja je dalesposrědkowanje a nima zašłe stawizny. To woznamjenja, zo móžeš stronu tam wróćo přemjenować, hdźež bu runje přemjenowana, jeli zmylk činiš a njemóžeš wobstejacu stronu přepisować.
'''Kedźbu!''' Móže to drastiska a njewočakowana změna za woblubowanu stronu być; prošu budź sej wěsty, zo sćěwki rozumiš, prjedy hač pokročuješ.",
'movepagetext-noredirectfixer' => "Wužiwajo slědowacy formular, móžeš stronu přemjenować a wšě jich daty do stawiznow noweho titula přesunyć.
'dec' => 'Des',
# Categories related messages
-'pagecategories' => '{{PLURAL:$1|Kategori}}',
+'pagecategories' => '{{PLURAL:$1|Kategori|Kategori}}',
'category_header' => 'Halaman dalam kategori "$1"',
'subcategories' => 'Subkategori',
'category-media-header' => 'Media dalam kategori "$1"',
'category-empty' => "''Saat ini, tidak terdapat halaman ataupun media dalam kategori ini.''",
-'hidden-categories' => '{{PLURAL:$1|Kategori tersembunyi}}',
+'hidden-categories' => '{{PLURAL:$1|Kategori tersembunyi|Kategori tersembunyi}}',
'hidden-category-category' => 'Kategori tersembunyi',
'category-subcat-count' => '{{PLURAL:$2|Kategori ini hanya memiliki satu subkategori berikut.|Kategori ini memiliki {{PLURAL:$1|subkategori|$1 subkategori}} berikut, dari total $2.}}',
'category-subcat-count-limited' => 'Kategori ini memiliki {{PLURAL:$1|subkategori|$1 subkategori}} berikut.',
'undo-success' => 'Suntingan ini dapat dibatalkan. Tolong cek perbandingan di bawah untuk meyakinkan bahwa benar itu yang Anda ingin lakukan, lalu simpan perubahan tersebut untuk menyelesaikan pembatalan suntingan.',
'undo-failure' => 'Suntingan ini tidak dapat dibatalkan karena konflik penyuntingan antara.',
'undo-norev' => 'Suntingan ini tidak dapat dibatalkan karena halaman tidak ditemukan atau telah dihapuskan.',
-'undo-summary' => 'Membatalkan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])',
+'undo-summary' => 'Membatalkan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|bicara]])',
# Account creation failure
'cantcreateaccounttitle' => 'Akun tak dapat dibuat',
'notextmatches' => 'Tidak ada teks halaman yang cocok',
'prevn' => '{{PLURAL:$1|$1}} sebelumnya',
'nextn' => '{{PLURAL:$1|$1}} selanjutnya',
-'prevn-title' => '$1 {{PLURAL:$1|hasil}} sebelumnya',
-'nextn-title' => '$1 {{PLURAL:$1|hasil}} selanjutnya',
+'prevn-title' => '$1 {{PLURAL:$1|hasil|hasil}} sebelumnya',
+'nextn-title' => '$1 {{PLURAL:$1|hasil|hasil}} selanjutnya',
'shown-title' => 'Tampilkan $1 {{PLURAL:$1|hasil|hasil}} per halaman',
'viewprevnext' => 'Lihat ($1 {{int:pipe-separator}} $2) ($3)',
'searchmenu-legend' => 'Opsi pencarian',
'searchprofile-images-tooltip' => 'Pencarian berkas',
'searchprofile-everything-tooltip' => 'Pencarian di seluruh situs (termasuk halaman pembicaraan)',
'searchprofile-advanced-tooltip' => 'Pencarian di ruang nama tertentu',
-'search-result-size' => '$1 ({{PLURAL:$2|$2 kata}})',
+'search-result-size' => '$1 ({{PLURAL:$2|1 kata|$2 kata}})',
'search-result-category-size' => '{{PLURAL:$1|1 anggota|$1 anggota}} ({{PLURAL:$2|1 subkategori|$2 subkategori}}, {{PLURAL:$3|1 berkas|$3 berkas}})',
'search-result-score' => 'Relevansi: $1%',
'search-redirect' => '(pengalihan $1)',
# Preferences page
'preferences' => 'Preferensi',
-'mypreferences' => 'Pengaturan',
+'mypreferences' => 'Preferensi',
'prefs-edits' => 'Jumlah suntingan:',
'prefsnologin' => 'Belum masuk log',
'prefsnologintext' => 'Anda harus <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} masuk log]</span> untuk mengeset preferensi Anda.',
'fewestrevisions' => 'Halaman dengan perubahan tersedikit',
# Miscellaneous special pages
-'nbytes' => '$1 {{PLURAL:$1|bita}}',
+'nbytes' => '$1 {{PLURAL:$1|bita|bita}}',
'ncategories' => '$1 {{PLURAL:$1|kategori|kategori}}',
'ninterwikis' => '$1 {{PLURAL:$1|interwiki|interwiki}}',
'nlinks' => '$1 {{PLURAL:$1|pranala|pranala}}',
'nopagetitle' => 'Halaman tujuan tidak ditemukan',
'nopagetext' => 'Halaman yang Anda tuju tidak ditemukan.',
'pager-newer-n' => '{{PLURAL:$1|1 lebih baru|$1 lebih baru}}',
-'pager-older-n' => '{{PLURAL:$1|$1 lebih lama}}',
+'pager-older-n' => '{{PLURAL:$1|1 lebih lama|$1 lebih lama}}',
'suppress' => 'Pengawas',
'querypage-disabled' => 'Halaman istimewa ini dinonaktifkan demi alasan kinerja.',
# Special:ActiveUsers
'activeusers' => 'Daftar pengguna aktif',
'activeusers-intro' => 'Berikut adalah daftar pengguna yang memiliki suatu bentuk aktivitas selama paling tidak $1 {{PLURAL:$1|hari|hari}} terakhir.',
-'activeusers-count' => '$1 {{PLURAL:$1|aktivitas|aktivitas}} dalam {{PLURAL:$3|hari|$3 hari}} terakhir',
+'activeusers-count' => '$1 {{PLURAL:$1|aktivitas|aktivitas}} dalam {{PLURAL:$3|1 hari|$3 hari}} terakhir',
'activeusers-from' => 'Tampilkan pengguna mulai dari:',
'activeusers-hidebots' => 'Sembunyikan bot',
'activeusers-hidesysops' => 'Sembunyikan pengurus',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
'video-dims' => '$1, $2 × $3',
'seconds-abbrev' => '$1 d',
-'minutes-abbrev' => '$1 m',
+'minutes-abbrev' => '$1 mnt',
'hours-abbrev' => '$1 j',
'days-abbrev' => '$1 h',
'seconds' => '{{PLURAL:$1|$1 detik|$1 detik}}',
'move-page-legend' => 'Spostamento di pagina',
'movepagetext' => "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Puoi aggiornare automaticamente i redirect che puntano al titolo originale. Puoi decidere di non farlo, ma ricordati di verificare che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.
-Si noti che la pagina '''non''' sarà spostata se ne esiste già una con il nuovo nome, a meno che non sia costituita solo da un redirect alla vecchia e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.
+Si noti che la pagina '''non''' sarà spostata se ne esiste già una con il nuovo nome, a meno che quest'ultima non sia costituita solo da un redirect alla vecchia e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.
'''ATTENZIONE:'''
Un cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。
リンクを正しく維持するのは移動した人の責任です。
-移動先が既に存在する場合は、そのページが転送ページであり、かつ過去の版を持たない場合を除いて移動'''できません'''。つまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。
+移動先のページが既に存在する場合は、その移動先が転送ページであり、かつ過去の版を持たない場合以外は移動'''できません'''。
+つまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。
-'''注意!'''
-よく閲覧されるページや、他の多くのページからリンクされているページを移動すると予期しない結果が起こるかもしれません。ページの移動に伴う影響をよく考えてから踏み切るようにしてください。",
+'''注意!'''
+よく閲覧されるページや、他の多くのページからリンクされているページを移動すると予期しない結果が起こるかもしれません。
+ページの移動に伴う影響をよく考えてから踏み切るようにしてください。",
'movepagetext-noredirectfixer' => "下のフォームを使用すると、ページ名を変更でき、そのページの履歴も変更先に移動できます。
移動元のページは移動先への転送ページになります。
自動的な修正を選択しない場合は、[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。
'emailusername' => 'მომხმარებლის სახელი:',
'emailusernamesubmit' => 'შენახვა',
'email-legend' => 'წერილის გაგზავნა სხვა მომხმარებლისადმი {{grammar:genitive|{{SITENAME}}}}',
-'emailfrom' => 'á\83\92á\83\90á\83\9bá\83\9dá\83\9bá\83\92á\83\96á\83\90á\83\95á\83\9cá\83\98:',
+'emailfrom' => 'გამგზავნი:',
'emailto' => 'მიმღები:',
'emailsubject' => 'თემა:',
'emailmessage' => 'შეტყობინება:',
'unblocked' => "D'Spär fir de [[User:$1|Benotzer $1]] gouf opgehuewen",
'unblocked-range' => "D'Spär vum $1 gouf opgehuewen",
'unblocked-id' => "D'Spär $1 gouf opgehuewen",
-'blocklist' => 'Gespaarte Benotzer',
-'ipblocklist' => 'Gespaarte Benotzer',
+'blocklist' => 'Gespaart Benotzer',
+'ipblocklist' => 'Gespaart Benotzer',
'ipblocklist-legend' => 'No engem gespaarte Benotzer sichen',
'blocklist-userblocks' => 'Benotzerspäre verstoppen',
'blocklist-tempblocks' => 'Temporär Späre verstoppen',
$messages = array(
# User preference toggles
-'tog-underline' => 'Garih bawahi link:',
+'tog-underline' => 'Garih bawahi tautan:',
'tog-justify' => 'Ratokan paragraf',
'tog-hideminor' => 'Suruakkan suntingan ketek di parubahan tabaru',
'tog-hidepatrolled' => 'Suruakkan suntingan nan lah dijago di parubahan tabaru',
-'tog-newpageshidepatrolled' => 'Suruakkan halaman nan lah dijago dari senarai halaman baru',
-'tog-extendwatchlist' => 'Kambangkan senarai pantauan untuak malihek sado parubahan, indak nan baru se',
-'tog-usenewrc' => 'Kalompok parubahan dek laman dalam parubahan tabaru jo daftar pantauan (paralu JavaScript)',
+'tog-newpageshidepatrolled' => 'Suruakkan laman nan lah dijago dari dafta laman baru',
+'tog-extendwatchlist' => 'Kambangkan dafta pantauan untuak malihek sado parubahan, indak nan baru se',
+'tog-usenewrc' => 'Gunokan tampilan parubahan tingkek lanjuik (paralu JavaScript)',
'tog-numberheadings' => 'Agiah nomor judua sacaro otomatis',
'tog-showtoolbar' => 'Tampilkan bilah suntiang (paralu JavaScript)',
-'tog-editondblclick' => 'Suntiang laman jo klik ganda (JavaScript)',
-'tog-editsection' => 'Fungsikan penyuntingan subbagian malalui [sunting] pranala',
-'tog-editsectiononrightclick' => 'Hiduikan bagian panyuntiangan jo mangklik kanan pado judul bagian (JavaScript)',
-'tog-showtoc' => 'Caliakkan dafta isi (untuak laman nan mampunyoi labiah dari 3 subbagian)',
-'tog-rememberpassword' => 'Kana log masuak denai di peramban ko (salamo $1 {{PLURAL:$1|hari|hari}})',
-'tog-watchcreations' => 'Tambahkan laman nan den buek jo gambar nan den unggah ka daftar pantauan',
-'tog-watchdefault' => 'Tambahkan laman jo gambar nan den suntiang ka daftar pantauan',
-'tog-watchmoves' => 'Tambahkan laman jo gambar nan den pindah ka daftar pantauan',
-'tog-watchdeletion' => 'Tambahkan laman jo gambar nan den hapuih ka daftar pantauan',
+'tog-editondblclick' => 'Suntiang laman jo klik duo kali (paralu JavaScript)',
+'tog-editsection' => 'Fungsikan penyuntiangan subbagian malalui [sunting] tautan',
+'tog-editsectiononrightclick' => 'Hiduikkan bagian panyuntiangan jo mangklik kanan pado judul bagian (paralu JavaScript)',
+'tog-showtoc' => 'Tunjuakkan dafta isi (untuak laman nan labiah dari 3 subbagian)',
+'tog-rememberpassword' => 'Ingek log masuak denai di paramban ko (salamo $1 {{PLURAL:$1|hari}})',
+'tog-watchcreations' => 'Tambahkan laman nan den buek jo gambar nan den unggah ka dafta pantauan',
+'tog-watchdefault' => 'Tambahkan laman jo gamba nan den suntiang ka dafta pantauan',
+'tog-watchmoves' => 'Tambahkan laman jo gamba nan den pindah ka dafta pantauan',
+'tog-watchdeletion' => 'Tambahkan laman jo gamba nan den hapuih ka dafta pantauan',
'tog-minordefault' => 'Tandoi sadoalah suntiangan sabagai suntiangan ketek sacaro baku',
'tog-previewontop' => 'Tampilkan pratonton sabalun kotak suntiang',
-'tog-previewonfirst' => 'Caliakkan pratayang pado suntiangan patamo',
-'tog-nocache' => 'Matikan panyinggahan laman peramban',
-'tog-enotifwatchlistpages' => 'Kirimkan surel kalau laman atau gambar pado daftar pantauan lah barubah',
-'tog-enotifusertalkpages' => 'E-mail ambo jiko laman barundiang denai lah barubah',
+'tog-previewonfirst' => 'Tunjuakkan pratonton pado suntiangan patamo',
+'tog-nocache' => 'Matikan panyinggahan laman paramban',
+'tog-enotifwatchlistpages' => 'Kirimkan surel, kalau laman atau gambar pado daftar pantauan den lah barubah',
+'tog-enotifusertalkpages' => "Kirimkan denai surel ko' laman diskusi den lah barubah",
'tog-enotifminoredits' => 'Kirimkan surel juo untuk saketek suntingan pado laman jo gambar',
-'tog-enotifrevealaddr' => 'Cogokan alamaik e-mail den pado e-mail notifikasi',
+'tog-enotifrevealaddr' => 'Tunjuakkan alamaik surel ambo pado pambaritauan surel',
'tog-shownumberswatching' => 'Tunjuakkan jumlah pamantau',
'tog-oldsig' => 'Tando tangan kini:',
-'tog-fancysig' => 'Palakuan tando tangan sabagai teks wiki (tanpa suatu tautan otomatis)',
-'tog-externaleditor' => 'Gunokan editor eksternal sacaro bawaan (untuak nan ahli sajo, kabutuahan pangaturan khusus pado komputer Sanak [//www.mediawiki.org/wiki/Manual:External_editors Informasi labiah lanjuik.].)',
+'tog-fancysig' => 'Jadikan tando tangan manjadi teks wiki (indak jo tautan otomatis)',
+'tog-externaleditor' => 'Gunokan editor dari lua sacaro bawaan (untuak nan ahli sajo, butuah pangaturan khusus di komputer Sanak [//www.mediawiki.org/wiki/Manual:External_editors Informasi labiah lanjuik.])',
'tog-externaldiff' => 'Gunokan diff eksternal sacaro bawaan (untuak nan ahli sajo, kabutuahan pangaturan khusus pado komputer Sanak [//www.mediawiki.org/wiki/Manual:External_editors Informasi labiah lanjuik.].)',
'tog-showjumplinks' => 'Aktifkan tautan pambantu "langsuang ka"',
'tog-uselivepreview' => 'Gunokan pratayang langsuang (JavaScript) (eksperimental)',
'tog-forceeditsummary' => 'Ingekkan awak bilo kotak ringkasan suntiangan masih kosoang',
-'tog-watchlisthideown' => 'Sambunyikan suntiangan awak di dafta pantauan',
-'tog-watchlisthidebots' => 'Sambunyikan suntiangan bot di dafta pantauan',
-'tog-watchlisthideminor' => 'Sambunyikan suntiangan ketek di dafta pantauan',
-'tog-watchlisthideliu' => 'Sambunyikan suntiangan pangguno masuak log di dafta pantauan',
-'tog-watchlisthideanons' => 'Sambunyikan suntiangan pangguno indak di kana di dafta pantauan',
-'tog-watchlisthidepatrolled' => 'Sambunyikan suntiangan tapatroli di dafta pantauan',
-'tog-ccmeonemails' => 'Kiriman awak salinan surel nan awak kiriman ka urang lain',
+'tog-watchlisthideown' => 'Suruakkan suntiangan surang di dafta pantauan',
+'tog-watchlisthidebots' => 'Suruakkan suntiangan bot di dafta pantauan',
+'tog-watchlisthideminor' => 'Suruakkan suntiangan ketek di dafta pantauan',
+'tog-watchlisthideliu' => 'Suruakkan suntiangan pangguno masuak log di dafta pantauan',
+'tog-watchlisthideanons' => 'Suruakkan suntiangan pangguno indak di kana di dafta pantauan',
+'tog-watchlisthidepatrolled' => 'Suruakkan suntiangan tapatroli di dafta pantauan',
+'tog-ccmeonemails' => 'Kiriman awak salinan surel nan dikiriman ka urang lain',
'tog-diffonly' => 'Jan tampilan isi laman di bawah pabedoan suntiangan',
'tog-showhiddencats' => 'Tampilan kategori tasambunyi',
'tog-norollbackdiff' => 'Jan tampilan pabedoan sasudah malakukan pangambalian',
'dec' => 'Des',
# Categories related messages
-'pagecategories' => '{{PLURAL:$1|Kategori|Kategori}}',
+'pagecategories' => '{{PLURAL:$1|Kategori}}',
'category_header' => 'Laman dalam kategori "$1"',
'subcategories' => 'Subkategori',
'category-media-header' => 'Laman/Media dalam kategori "$1"',
'category-empty' => "''Kini ko, indak ado laman ataupun media dalam kategori ko.''",
-'hidden-categories' => '{{PLURAL:$1|Kategori tapandam|Kategori tapandam}}',
+'hidden-categories' => '{{PLURAL:$1|Kategori tapandam}}',
'hidden-category-category' => 'Kategori tasambunyi',
'category-subcat-count' => '{{PLURAL:$2|Kategori ko punyo {{PLURAL:$1|$1 subkategori}}, dari total $2.}}',
-'category-subcat-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|subkategori|$1 subkategori}} barikuik.',
+'category-subcat-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|$1 subkategori}} barikuik.',
'category-article-count' => '{{PLURAL:$2|Kategori ko punyo {{PLURAL:$1|$1 laman}}, dari total $2.}}',
-'category-article-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|ciek laman|$1 laman}} barikuik.',
+'category-article-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|$1 laman}} barikuik.',
'category-file-count' => '{{PLURAL:$2|Kategori ko ado {{PLURAL:$1|$1 laman}}, dari $2 laman.}}',
-'category-file-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|laman|$1 laman}} barikuik.',
-'listingcontinuesabbrev' => 'lanjuik',
+'category-file-count-limited' => 'Kategori iko mamiliki {{PLURAL:$1|$1 laman}} barikuik.',
+'listingcontinuesabbrev' => 'samb.',
'index-category' => 'Laman nan diindeks',
'noindex-category' => 'Laman nan indak diindeks',
'broken-file-category' => 'Laman jo gamba rusak',
'morenotlisted' => 'Salabiahnyo...',
'mypage' => 'Laman',
'mytalk' => 'Maota',
-'anontalk' => 'Ota IP iko',
+'anontalk' => 'Diskusi IP ko',
'navigation' => 'Pinteh',
'and' => ' jo',
'vector-simplesearch-preference' => 'Aktifkan kotak pancarian sadarano (hanyo kulik Vector)',
'vector-view-create' => 'Buek',
'vector-view-edit' => 'Suntiang',
-'vector-view-history' => 'Caliak riwayaik nan lalu',
+'vector-view-history' => 'Riwayaik lalu',
'vector-view-view' => 'Baco',
'vector-view-viewsource' => 'Caliak sumber',
'actions' => 'Tindakan',
'unprotectthispage' => 'Tuka palindungan laman ko',
'newpage' => 'Laman baru',
'talkpage' => 'Musyawarahkan laman ko',
-'talkpagelinktext' => 'Maota',
+'talkpagelinktext' => 'maota',
'specialpage' => 'Laman istimewa',
'personaltools' => 'Pakakeh pribadi',
'postcomment' => 'Bagian baru',
'templatepage' => 'Caliak laman templat',
'viewhelppage' => 'Caliak laman bantuan',
'categorypage' => 'Caliak laman kategori',
-'viewtalkpage' => 'Caliak laman ota',
+'viewtalkpage' => 'Caliak laman diskusi',
'otherlanguages' => 'Dalam bahaso lain',
'redirectedfrom' => '(Dialiahkan dari $1)',
'redirectpagesub' => 'Laman pengalihan',
'badaccess' => 'Kasalahan hak akses',
'badaccess-group0' => 'Sanak indak diizinkan untuak malakukan tindakan nan Sanak nio.',
-'badaccess-groups' => 'Tindakan nan Sanak nio dibatasi untuak pangguno dalam {{PLURAL:$2|kalompok|ciek dari kelompok}}: $1.',
+'badaccess-groups' => 'Tindakan nan Sanak nio dibatasi untuak pangguno dalam {{PLURAL:$2|kalompok}}: $1.',
'versionrequired' => 'Dibutuahkan MediaWiki versi $1',
'versionrequiredtext' => 'MediaWiki versi $1 dibutuahkan untuak manggunokan laman ko. Caliak [[Special:Version|versi laman]]',
'youhavenewmessages' => 'Awak punyo $1 ($2).',
'newmessageslink' => 'pasan baru',
'newmessagesdifflink' => 'parubahan tarakhia',
-'youhavenewmessagesfromusers' => 'Sanak mandapek $1 dari {{PLURAL:$3|another user|$3 users}} ($2)',
+'youhavenewmessagesfromusers' => 'Sanak mandapek $1 dari {{PLURAL:$3|$3 pangguno}} ($2)',
'youhavenewmessagesmanyusers' => 'Sanak mandapek $1 dari banyak pangguno ($2)',
-'newmessageslinkplural' => '{{PLURAL:$1|sabuah pasan baru|pasan baru}}',
+'newmessageslinkplural' => '{{PLURAL:$1|pasan baru}}',
'newmessagesdifflinkplural' => '{{PLURAL:$1|parubahan}} taakhia',
'youhavenewmessagesmulti' => 'Sanak mandapek pasan baru pado $1',
'editsection' => 'suntiang',
'collapsible-expand' => 'Kambangan',
'thisisdeleted' => 'Caliak atau kambalian $1?',
'viewdeleted' => 'Caliak $1?',
-'restorelink' => 'Caliak {{PLURAL:$1|ciek suntiangan|$1 suntiangan}} nan dihapuih',
+'restorelink' => '{{PLURAL:$1|$1 suntiangan}} lah dihapuih',
'feedlinks' => 'Umpan:',
'feed-invalid' => 'Tipe pamintaan umpan indak tapek.',
'feed-unavailable' => 'Sindikasi umpan indak tasadio',
'badarticleerror' => 'Tindakan iko indak dapek dilaksanakan di laman iko.',
'cannotdelete' => 'Laman atau berkas "$1" indak dapek dihapuih.
Mungkin alah dihapuih jo urang lain.',
-'cannotdelete-title' => 'Indak bisa mangapuih halaman "$1"',
+'cannotdelete-title' => 'Indak dapek mangapuih laman "$1"',
'delete-hook-aborted' => 'Pengapusan batal jo hook.
Indak ado keterangan.',
'badtitle' => 'Judul indak sah',
'actionthrottledtext' => 'Anda dibatasi untuak malakuan tindakan iko talalu banyak dalam waktu singkek. Sila mancubo laik satalah bara menit.',
'protectedpagetext' => 'Laman ko alah dikunci untuak manghindari panyuntiangan.',
'viewsourcetext' => 'Sanak dapek malihek atau manyalin sumber laman iko:',
-'viewyourtext' => 'Sanak bisa mancaliak dan mangopi sumber untuak "editan sanak" ka halaman iko',
+'viewyourtext' => 'Sanak dapek mancaliak jo mangkopi sumber untuak "suntiangan sanak" ka laman ko',
'protectedinterface' => 'Laman iko baisi teks antarmuko untuak digunoan dek parangkaik lunak di wiki iko sajo, dan alah dikunci untuak maindaan kasalahan.
Untuak manambah atau maubah tajamahan di sadonyo wiki, harap gunoan [//translatewiki.net/ translatewiki.net], yaitu proyek palokalan MediaWiki.',
'editinginterface' => "'''Paringatan:''' Sanak manyuntiang laman nan digunoan untuak manyadiokan teks antarmuko untuak parangkaik lunak.
'invalidtitle-knownnamespace' => '↓Judul nan indak sah jo ruangnamo "$2" dan teks "$3"',
'invalidtitle-unknownnamespace' => 'Judul nan tak sah jo nomor ruang namo indak diketahui $1 dan teks "$2"',
'exception-nologin' => 'Indak log masuak',
-'exception-nologin-text' => 'Halaman ko hanyo bisa disuntiang dek pangguno badaftar.',
+'exception-nologin-text' => 'Laman ko hanyo dapek disuntiang dek pangguno nan mandaftar.',
# Virus scanner
'virus-badscanner' => "Kasalahan konfigurasi: pamindai virus indak dikenal: ''$1''",
'log-fulllog' => 'Liek saluruah log',
'edit-hook-aborted' => 'Suntiangan dibatalan samo kait parser
tanpa ado katarangan.',
-'edit-gone-missing' => 'Indak bisa mamperbarui halaman.
+'edit-gone-missing' => 'Indak dapek mampabarui laman.
Mungkin alah dihapuih.',
'edit-conflict' => 'Konflik suntingan.',
'edit-no-change' => 'Suntiangan sanak ditulak, karano indak ado parubahan nan tajadi ka teks.',
-'edit-already-exists' => 'Indak bisa mambuek halaman baru.
-Alah ado.',
-'defaultmessagetext' => 'Teks pasan default.',
+'edit-already-exists' => 'Indak dapek mambuek aman baru.
+Nyo alah ado.',
+'defaultmessagetext' => 'Teks baku.',
'content-failed-to-parse' => 'Gagal manjabarkan konten $2 untuak model $1: $3',
'invalid-content-data' => 'Data kanduangan indak valid.',
'content-not-allowed-here' => 'Konten "$1" indak diizinan di laman [[$2]]',
# Parser/template warnings
'expensive-parserfunction-warning' => "'''Paringatan:''' Laman ko manganduang talalu banyak panggilan fungsi parser.
-Seharusnyo kurang dari $2 {{PLURAL:$2|panggilan|$2 panggilan}}, tapi {{PLURAL:$1|kini ado $1 panggilan|kini ko ado $1 panggilan}}.",
+Seharusnyo kurang dari $2 {{PLURAL:$2|panggilan}}, tapi {{PLURAL:$1|kini ado $1 panggilan}}.",
'expensive-parserfunction-category' => 'Laman nan talalu banyak panggilan fungsi parser',
'post-expand-template-inclusion-warning' => "'''Peringatan:''' Ukuran templat talalu gadang.
Babarapo templat akan diabaikan.",
'converter-manual-rule-error' => 'Kasalahan tadeteksi di aturan manual konversi bahaso',
# "Undo" feature
-'undo-success' => 'Suntiangan iko dapek dibatalan.
-Tolong cek pabandiangan di bawah untuak mayakinkan bahwa bana itu nan Sanak ingin buek, lalu simpan parubahan tasabuik untuak manyalasaikan pambatalan suntiangan.',
+'undo-success' => 'Suntiangan ko dapek dibatalan.
+Tolong cek pabedoan di bawah untuak mayakinkan bahwa bana nan tu Sanak nio buek, lalu simpan parubahan tasabuik untuak manyalasaikan pambatalan suntiangan.',
'undo-failure' => 'Suntiangan ko indak dapek dibatalan dek konflik panyuntiangan antaro.',
'undo-norev' => 'Suntiangan ko indak dapek dibatalan dek laman indak ditamukan atau lah dihapuih.',
-'undo-summary' => 'Mambatalan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])',
+'undo-summary' => 'Mambatalan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|maota]])',
# Account creation failure
'cantcreateaccounttitle' => 'Indak dapek mambuek akun',
'history-show-deleted' => 'Hanyo nan dihapuih',
'histfirst' => 'Nan lamo',
'histlast' => 'Nan baru',
-'historysize' => '({{PLURAL:$1|bita|$1 bita}})',
+'historysize' => '({{PLURAL:$1|$1 bita}})',
'historyempty' => '(kosong)',
# Revision feed
'searchprofile-images-tooltip' => 'Cari untuak berkas',
'searchprofile-everything-tooltip' => 'Cari sadoalahnyo (tamasuak laman maota)',
'searchprofile-advanced-tooltip' => 'Pacarian di ruang namo tatantu',
-'search-result-size' => '$1 ({{PLURAL:$2|1 kato|$2 kato}})',
+'search-result-size' => '$1 ({{PLURAL:$2|$2 kato}})',
'search-result-category-size' => '{{PLURAL:$1|$1 anggota}} ({{PLURAL:$2|$2 subkategori}}, {{PLURAL:$3|$3 berkas}})',
'search-result-score' => 'Relevansi: $1%',
'search-redirect' => '(pangaliahan $1)',
'search-interwiki-more' => '(selanjutnyo)',
'searchrelated' => 'bakaitan',
'searchall' => 'sado',
-'showingresults' => "Di bawah iko dikaluaan inggo {{PLURAL:$1|'''1''' asia|'''$1''' asia}}, dimulai dari #'''$2'''.",
-'showingresultsnum' => "Di bawah iko dikaluaan {{PLURAL:$3|'''1'''|'''$3'''}} asia, dimulai dari #'''$2'''.",
-'showingresultsheader' => "{{PLURAL:$5|Hasil '''$1''' dari '''$3'''|Hasil '''$1 - $2''' dari '''$3'''}} untuak '''$4'''",
+'showingresults' => "Di bawah ko dikaluaan sampai {{PLURAL:$1|'''$1''' hasil}}, dimulai dari #'''$2'''.",
+'showingresultsnum' => "Di bawah ko dikaluaan {{PLURAL:$3|'''$3'''}} hasil mulai dari #'''$2'''.",
+'showingresultsheader' => "{{PLURAL:$5|Hasil '''$1 - $2''' dari '''$3'''}} untuak '''$4'''",
'nonefound' => "'''Catatan''': hanyo babarapo ruangnamo yang dicari sacaro default.
Cubo awali permintaan awak tu jo ''all:'' untuak mancari sado kandungan (tamasuak laman ota, templat, dll), atau gunoan ruangnamo yang diinginkan sabagai awalan.",
'search-nonefound' => 'Indak ado hasil nan cocok sasuai jo parmintaan',
'powersearch-togglenone' => 'Dak ado',
# Preferences page
-'preferences' => 'Preferensi',
+'preferences' => 'Pangaturan',
'mypreferences' => 'Pangaturan',
-'prefs-beta' => 'Fitur Beta',
-'prefs-datetime' => 'Tanggal dan waktu',
-'prefs-labs' => 'Fitur uji',
+'prefs-skin' => 'Kulik',
+'skin-preview' => 'Caliak',
+'datedefault' => 'Indak usah diatua',
+'prefs-beta' => 'Baru dicubo (Beta)',
+'prefs-datetime' => 'Tangga jo wakatu',
+'prefs-labs' => 'Alaik uji',
'prefs-user-pages' => 'Laman pangguno',
'prefs-personal' => 'Profil pangguno',
'prefs-rc' => 'Parubahan tabaru',
'prefs-watchlist' => 'Dafta pantauan',
'prefs-watchlist-days' => 'Lamonyo dalam daftar pantauan:',
+'prefs-watchlist-days-max' => 'Maksimum $1 {{PLURAL:$1|hari}}',
+'prefs-watchlist-edits' => 'Jumlah suntiangan maksimum nan ditampilkan di dafta pantauan nan labiah langkok:',
+'prefs-watchlist-edits-max' => 'Nilai maksimum: 1000',
+'prefs-watchlist-token' => 'Token pantauan:',
+'prefs-misc' => 'Lain-lain',
+'prefs-resetpass' => 'Tuka kato sandi',
+'prefs-changeemail' => 'Tuka alamaik surel',
+'prefs-setemail' => 'Atua alamaik surel',
+'prefs-email' => 'Opsi surel',
+'prefs-rendering' => 'Tampilan',
+'saveprefs' => 'Simpan',
+'resetprefs' => 'Batalan parubahan',
+'restoreprefs' => 'Baliakkan ka setelan bawaan',
+'prefs-editing' => 'Panyuntiangan',
+'prefs-edit-boxsize' => 'Ukuran kotak panyuntiangan.',
+'rows' => 'Barih:',
+'columns' => 'Kolom',
+'searchresultshead' => 'Cari',
+'resultsperpage' => 'Hasil per laman:',
+'stub-threshold' => 'Ambang bateh untuak format <a href="#" class="stub">tautan rintisan</a>:',
+'stub-threshold-disabled' => 'Nonaktifkan',
+'recentchangesdays' => 'Jumlah ari nan ditampilkan di parubahan tabaru:',
+'recentchangesdays-max' => 'Maksimum $1 {{PLURAL:$1|hari}}',
+'recentchangescount' => 'Standar jumlah suntiangan nan ditampilkan:',
+'prefs-help-recentchangescount' => 'Iko untuak parubahan tabaru, riwayaik laman nan lalu, sarato log.',
+'prefs-help-watchlist-token' => 'Mangisi kotak ko jo kunci rasio (PIN) akan manghasilkan sindikasi RSS untuak dafta pantauan Angku. Sia juo nan tau jo kunci ko dapek mambaco dafta pantauan Angku, jadi hati-hatilah mamiliah nilainyo
+Barikuik ko nilai acak nan dapek Angku gunoan: $1',
+'savedprefs' => 'Pangaturan Angku alah tasimpan',
+'timezonelegend' => 'Zona wakatu:',
+'localtime' => 'Wakatu satampaik:',
+'timezoneuseserverdefault' => 'Gunokan nan dari wiki ($1)',
+'timezoneuseoffset' => 'Lainnyo (tantuan pabedoannyo)',
+'timezoneoffset' => 'Pabedoan¹:',
+'servertime' => 'Wakatu server:',
+'guesstimezone' => 'Isikan dari panjalajah web',
'timezoneregion-africa' => 'Afrika',
'timezoneregion-america' => 'Amerika',
'timezoneregion-antarctica' => 'Antarktika',
'allowemail' => 'Izinkan pangguno lain mangirim surel',
'prefs-searchoptions' => 'Cari',
'prefs-namespaces' => 'Ruang namo',
-'defaultns' => 'Ataupun cari dalam ruang-ruang namo ko:',
+'defaultns' => 'Ataupun cari dalam ruang namo lain:',
'default' => 'baku',
'prefs-files' => 'Berkas',
'prefs-custom-css' => 'CSS pribadi',
'prefs-custom-js' => 'JS pribadi',
-'prefs-common-css-js' => 'CSS/JS babagi untuak sado kulit:',
+'prefs-common-css-js' => 'CSS/JS babagi untuak sado kulik:',
+'prefs-reset-intro' => 'Angku dapek manggunokan laman ko untuak mangambalikan pangaturan ka setelan baku situs ko.
+Pangambalian pangaturan indak dapek dibatalan.',
'prefs-emailconfirm-label' => 'Surel konfirmasi:',
'prefs-textboxsize' => 'Ukuran kotak suntiang',
'youremail' => 'Surel:',
'username' => '{{GENDER:$1|Namo pangguno}}:',
+'prefs-registration' => 'Wakatu pandaftaran:',
'yourrealname' => 'Namo sabananyo:',
+'yourlanguage' => 'Bahaso',
+'yourvariant' => 'Varian bahaso isi:',
+'prefs-help-variant' => 'Varian atau ortografi pilihan Angku untuak manampilkan isi laman wiki ko.',
+'yournick' => 'Tando tangan:',
+'prefs-help-signature' => 'Komen pado laman maota paralu ditandotangani jo "<nowiki>~~~~</nowiki>" nan kan diubah manjadi tando tangan Angku jo wakatu saat kini ko.',
+'badsig' => 'Tando tangan mantah indak sah; pariso tag HTML.',
+'badsiglength' => 'Tando tangan Angku panjang bana.
+Jan labiah dari $1 {{PLURAL:$1|karakter}}.',
+'yourgender' => 'Jenis kelamin:',
+'gender-unknown' => 'Indak ditanyo',
+'gender-male' => 'Laki-laki',
+'gender-female' => 'Padusi',
+'prefs-help-gender' => 'Lainnyo: digunoan untuak manyabuik gender jo parangkaik lunak. Informasi ko akan tabukak untuak umum.',
+'email' => 'Surel',
+'prefs-help-realname' => "Namo asli sifaiknyo opsional.
+Jiko' Angku manambahkannyo, namo asli Angku akan digunoan untuak mengenal hasil karaja Angku.",
'prefs-help-email' => 'Alamaik surel ko hanyo tambahan se, namun paralu untuak maulang kato kunci, jikok Sanak lupo kato kunci.',
'prefs-help-email-others' => 'Sanak dapek mamiliah untuak mangizinkan urang lain manghubungi jo surel malalui laman pangguno atau laman diskusi.
Alamaik surel tu indakkan tau dek urang nan manghubungi sanak tu.',
+'prefs-help-email-required' => 'Alamaik surel wajib diisi.',
+'prefs-info' => 'Informasi dasar',
+'prefs-i18n' => 'Internasionalisasi',
'prefs-signature' => 'Tando tangan',
+'prefs-dateformat' => 'Format tangga',
+'prefs-timeoffset' => 'Format wakatu',
+'prefs-advancedediting' => 'Opsi lanjuik',
+'prefs-advancedrc' => 'Opsi lanjuik',
+'prefs-advancedrendering' => 'Opsi lanjuik',
+'prefs-advancedsearchoptions' => 'Opsi lanjuik',
+'prefs-advancedwatchlist' => 'Opsi lanjuik',
+'prefs-displayrc' => 'Pilihan tampilan',
+'prefs-displaysearchoptions' => 'Pilihan tampilan',
+'prefs-displaywatchlist' => 'Pilihan tampilan',
+'prefs-diffs' => 'Pabedoan',
+
+# User preference: e-mail validation using jQuery
+'email-address-validity-valid' => 'Alamaik surel nampaknyo sah',
+'email-address-validity-invalid' => 'Masuakkan alamaik surel nan sah',
+
+# User rights
+'userrights' => 'Manajemen hak pangguno',
+'userrights-lookup-user' => 'Mangatua kalompok pangguno',
+'userrights-user-editname' => 'Masuakkan namo pangguno:',
+'editusergroup' => 'Suntiang kalompok pangguno',
# Groups
-'group-sysop' => 'Pengurus',
-
+'group' => 'Kalompok:',
+'group-user' => 'Pangguno',
+'group-autoconfirmed' => 'Pangguno takonfirmasi otomatis',
+'group-bot' => 'Bot',
+'group-sysop' => 'Panguruih',
+'group-bureaucrat' => 'Birokrat',
+'group-suppress' => 'Pangawas',
+'group-all' => '(sadonyo)',
+
+'group-user-member' => '{{GENDER:$1|pangguno}}',
+
+'grouppage-user' => '{{ns:project}}:Pangguno',
'grouppage-sysop' => '{{ns:project}}:Pengurus',
+# Rights
+'right-createpage' => 'Mambuek laman baru (nan bukan laman diskusi)',
+'right-createtalk' => 'Mambuek laman diskusi',
+'right-createaccount' => 'Mambuek akun baru',
+
# Special:Log/newusers
'newuserlogpage' => 'Log pangguno baru',
'rightslog' => 'Log parubahan hak akses',
# Associated actions - in the sentence "You do not have permission to X"
+'action-read' => 'baco laman ko',
'action-edit' => 'suntiang laman ko',
+'action-createpage' => 'buek laman',
+'action-createtalk' => 'buek laman diskusi',
+'action-createaccount' => 'buek akun pangguno ko',
+'action-minoredit' => 'tandoi sabagai suntiangan ketek',
# Recent changes
'nchanges' => '$1 {{PLURAL:$1|parubahan}}',
'license' => 'Lisensi:',
'license-header' => 'Lisensi',
+# Special:ListFiles
+'listfiles_user' => 'Pangguno',
+'listfiles_size' => 'Ukuran',
+'listfiles_description' => 'Katarangan',
+'listfiles_count' => 'Versi',
+
# File description page
'file-anchor-link' => 'Berkas',
'filehist' => 'Riwayaik berkas',
'filehist-datetime' => 'Tanggal/Waktu',
'filehist-thumb' => 'Miniatur',
'filehist-thumbtext' => 'Miniatur untuak versi per $1',
+'filehist-nothumb' => 'Miniatur indak ado',
'filehist-user' => 'Pangguno',
'filehist-dimensions' => 'Dimensi',
+'filehist-filesize' => 'Ukuran berkas',
'filehist-comment' => 'Komen',
+'filehist-missing' => 'Berkas indak ado',
'imagelinks' => 'Panggunoan berkas',
'linkstoimage' => 'Barikuik ko {{PLURAL:$1|$1 laman nan takaik}} jo berkas:',
'nolinkstoimage' => 'Indak ado laman nan batauik ka berkas ko.',
# Contributions
'contributions' => 'Jariah {{GENDER:$1|pangguno}}',
'contributions-title' => 'Jariah pangguno untuak $1',
-'mycontris' => 'Jariah denai',
+'mycontris' => 'Jariah',
'contribsub2' => 'Untuak $1 ($2)',
'uctop' => '(ateh)',
'month' => 'Dari bulan (dan sabalunnyo):',
'sp-contributions-deleted' => 'kontribusi pangguno nan lah batiadoan',
'sp-contributions-uploads' => 'muek',
'sp-contributions-logs' => 'log',
-'sp-contributions-talk' => 'diskusi',
+'sp-contributions-talk' => 'maota',
'sp-contributions-search' => 'Cari jariah',
'sp-contributions-username' => 'Alamat IP atau namo pangguno:',
'sp-contributions-toponly' => 'Hanyo manampilan suntiangan nan tarakhia',
Perhatikan bahwa laman '''indak''' akan dipindah apobilo lah ado laman yang manggunokan judul yang baru, kecuali bilo laman tu kosong atau marupokan laman peralihan dan indak punyo riwayat suntingan. Maksudnyo awak dapek maubah namo laman seperti samulo apobilo ado kesalahan, dan awak indak dapek manimpo laman yang lah ado.
'''Peringatan:''' Iko dapek maakibatkan parubahan yang tak diduga pado laman yang populer. Jadi pastikan awak paham akibat tindakan ko sabalun melanjutkannyo.",
-'movepagetalktext' => "Laman ota yang bakaitan akan dipindahkan sacaro otomatis '''kecuali apobilo:'''
+'movepagetalktext' => "Laman diskusi nan bakaitan akan dipindahkan sacaro otomatis '''kacuali apobilo:'''
-*Sebuah laman ota yang indak kosong lah ado pado judul baru, atau
-*Awak indak memberi tando pado kotak di bawahnyo
+*Sabuah laman diskusi nan indak kosong lah ado pado judul baru, atau
+*Angku indak mangagiah tando pado kotak di bawah.
-Dalam kasus tu, apobilo diinginkan, awak dapek mamindahkan atau manggabuangkan laman sacaro manual.",
+Dalam kasus tu, kok amuah Angku dapek mamindahkan ataupun manggabuangkan laman sacaro manual.",
'movearticle' => 'Pindahkan laman',
'newtitle' => 'Ka judul baru:',
'move-watch' => 'Pantau laman ko',
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Laman pangguno sanak',
-'tooltip-pt-mytalk' => 'Laman ota sanak',
-'tooltip-pt-preferences' => 'Preferensi denai',
+'tooltip-pt-anonuserpage' => 'Laman pangguno IP Sanak',
+'tooltip-pt-mytalk' => 'Laman diskusi sanak',
+'tooltip-pt-anontalk' => 'Diskusi tantang suntiangan dari alamat IP ko',
+'tooltip-pt-preferences' => 'Pangaturan denai',
'tooltip-pt-watchlist' => 'Dafta laman nan dipantau.',
'tooltip-pt-mycontris' => 'Daftar jariah Sanak',
'tooltip-pt-login' => 'Sanak disaranan untuak masuak log; walaupun indak wajib',
'tooltip-t-permalink' => 'Pranala permanen untuak revisi laman ko',
'tooltip-ca-nstab-main' => 'Caliak isi laman',
'tooltip-ca-nstab-user' => 'Caliak laman pangguno',
+'tooltip-ca-nstab-media' => 'Caliak laman media',
'tooltip-ca-nstab-special' => 'Laman istimewa, indak dapek disuntiang',
'tooltip-ca-nstab-project' => 'Caliak laman proyek',
'tooltip-ca-nstab-image' => 'Caliak laman berkas',
# Metadata
'notacceptable' => 'Layanan wiki indak manyadioan data dalam format yang dapek dibaco dek pelanggan awak.',
+# Attribution
+'anonymous' => '{{PLURAL:$1|Pangguno}} anonim {{SITENAME}}',
+'siteuser' => 'pangguno {{SITENAME}} $1',
+'anonuser' => 'pangguno anonim {{SITENAME}} $1',
+'siteusers' => '{{PLURAL:$2|pangguno}} {{SITENAME}} $1',
+'anonusers' => '{{PLURAL:$2|pangguno}} anonim {{SITENAME}} $1',
+'creditspage' => 'Panghargaan laman',
+
+# Info page
+'pageinfo-title' => 'Informasi untuak "$1"',
+'pageinfo-header-basic' => 'Informasi dasar',
+'pageinfo-header-edits' => 'Riwayaik suntiangan',
+'pageinfo-header-restrictions' => 'Palinduangan laman',
+'pageinfo-header-properties' => 'Properti laman',
+'pageinfo-display-title' => 'Judua tampilan',
+'pageinfo-length' => 'Panjang laman (dalam bita)',
+'pageinfo-article-id' => 'ID Laman',
+'pageinfo-firstuser' => 'Pambuek laman',
+
# Skin names
'skinname-standard' => 'Klasik',
'skinname-nostalgia' => 'Nostalgia',
'nextdiff' => 'Revisi salanjuiknyo →',
# Media information
+'thumbsize' => 'Ukuran miniatur:',
'widthheight' => '$1 × $2',
+'widthheightpage' => '$1 × $2, $3 {{PLURAL:$3|laman}}',
+'file-info' => 'ukuran berkas: $1, tipe MIME: $2',
'file-info-size' => '$1 × $2 piksel, ukuran berkas: $3, tipe MIME: $4',
+'file-info-size-pages' => '$1 × $2 piksel, ukuran berkas: $3, tipe MIME: $4, $5 {{PLURAL:$5|laman}}',
'file-nohires' => 'Indak tasadio resolusi nan labiah gadang.',
'svg-long-desc' => 'Berkas SVG, $1 × $2 piksel, ukuran berkas: $3',
+'svg-long-desc-animated' => 'Berkas anmasi SVG, $1 × $2 piksel, ukuran berkas: $3',
'show-big-image' => 'Resolusi panuah',
+'show-big-image-preview' => 'Ukuran pratonton ko: $1',
+'show-big-image-other' => '{{PLURAL:$2|Resolusi}} lainnyo: $1.',
+'show-big-image-size' => '$1 × $2 piksel',
+'file-info-gif-looped' => 'ulang',
+'file-info-gif-frames' => '$1 {{PLURAL:$1|bingkai}}',
+'file-info-png-looped' => 'ulang',
+'file-info-png-repeat' => 'dimainkan $1 {{PLURAL:$1|kali}}',
+'file-info-png-frames' => '$1 {{PLURAL:$1|bingkai}}',
+
+# Special:NewFiles
+'newimages-legend' => 'Panyaring',
+'newimages-label' => 'Namo berkas (atau sabagian darinyo):',
+'showhidebots' => '($1 bot)',
+'noimages' => 'Indak ado nan dicaliak.',
+'ilsubmit' => 'Cari',
+'bydate' => 'jo tanggal',
+'sp-newimages-showfrom' => 'Tampilkan berkas baru mulai dari $2, $1',
# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
'video-dims' => '$1, $2 × $3',
'minutes-abbrev' => '$1 min',
'hours-abbrev' => '$1 j',
'days-abbrev' => '$1 h',
+'seconds' => '{{PLURAL:$1|$1 detik}}',
+'minutes' => '{{PLURAL:$1|$1 minik}}',
+'hours' => '{{PLURAL:$1|$1 jam}}',
+'days' => '{{PLURAL:$1|$1 hari}}',
+'ago' => '$1 nan lalu',
# Bad image list
'bad_image_list' => 'Ukurannyo adolah sabagai barikuik:
'metadata-help' => 'Berkas ko ado informasi tambahan nan mungkin ditambahkan dek kamera digital atau pemindai yang digunokan untuak mambuek atau mendigitalisasi berkas. Jikok berkas ko lah mangalami modifikasi, rincian nan ado mungkin indak sacaro panuah merefleksi modifikasi dari berkas tu.',
'metadata-expand' => 'Tampilkan rincian tambahan',
'metadata-collapse' => 'Suruakkan rincian tambahan',
-'metadata-fields' => 'Tapak metadata gambar nan disenaraikan dalam pasan ko akan di masuakan pado tampilan laman gambar katiko tabel metadata disuruakkan.
-Nan lainnyo akan tasuruak sacaro default.
+'metadata-fields' => 'Tapak metadata gamba nan didata dalam pasan ko akan di masuakan pado tampilan laman gambar katiko tabel metadata disuruakkan.
+Nan lainnyo akan tasuruak sacaro baku.
* make
* model
* datetimeoriginal
* gpslongitude
* gpsaltitude',
+# EXIF tags
+'exif-imagewidth' => 'Leba',
+'exif-imagelength' => 'Tinggi',
+'exif-bitspersample' => 'Bita per komponen',
+'exif-compression' => 'Skema kompresi',
+'exif-photometricinterpretation' => 'Komposisi piksel',
+'exif-orientation' => 'Orientasi',
+'exif-samplesperpixel' => 'Jumlah komponen',
+'exif-planarconfiguration' => 'Pangaturan data',
+'exif-imagedescription' => 'Judua gamba',
+'exif-make' => 'Produsen kamera',
+'exif-model' => 'Model kamera',
+'exif-software' => 'Parangkaik lunak',
+'exif-artist' => 'Pambuek',
+'exif-copyright' => 'Nan punyo hak cipta',
+'exif-exifversion' => 'Versi Exif',
+'exif-flashpixversion' => 'Dukuangan versi Flashpix',
+'exif-colorspace' => 'Ruang warna',
+'exif-componentsconfiguration' => 'Arti tiok komponen',
+'exif-compressedbitsperpixel' => 'Mode kompresi gamba',
+'exif-pixelydimension' => 'Leba gamba',
+'exif-pixelxdimension' => 'Tinggi gamba',
+'exif-usercomment' => 'Komen pangguno',
+'exif-relatedsoundfile' => 'Berkas audio nan bahubuangan',
+
# External editor support
'edit-externally' => 'Suntiang berkas ko dengan aplikasi lua',
'edit-externally-help' => '(Caliak [//www.mediawiki.org/wiki/Manual:External_editors instruksi pangaturan] untuak informasi lanjuiknyo)',
'watchlisttools-edit' => 'Tampilkan sarato suntiang daftapantau',
'watchlisttools-raw' => 'Suntiang pantauan mantah',
+# Signatures
+'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|maota]])',
+
# Core parser functions
'duplicate-defaultsort' => '\'\'\'Peringatan:\'\'\' Kunci panguruitan default "$2" sabalunnyo mangabaikan kunci panguruitan default "$1".',
# Special:Version
+'version-skins' => 'Kulik',
+'version-other' => 'Lain-lain',
+'version-license' => 'Lisensi',
'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Artikel path]',
'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Skrip path]',
+# Special:FilePath
+'filepath-page' => 'Berkas:',
+
# Special:FileDuplicateSearch
'fileduplicatesearch-result-n' => 'Berkas "$1" punyo {{PLURAL:$2|1 duplikat identik|$2 duplikat identik}}.',
+'fileduplicatesearch-noresults' => 'Indak basobok berkas banamo "$1".',
# Special:SpecialPages
'specialpages' => 'Laman istimewa',
#Latakan sado fragmen regex di bawah barih ko. Bia se barih apo adonyo</pre>',
# Special:Tags
-'tag-filter' => '[[Special:Tags|Tag]] bateh:',
+'tag-filter' => '[[Special:Tags|Tag]] sariang:',
+'tag-filter-submit' => 'Sariang',
+'tags-title' => 'Tag',
+'tags-tag' => 'Namo tag',
+'tags-edit' => 'suntiang',
+'tags-hitcount' => '$1 {{PLURAL:$1|parubahan}}',
+
+# New logging system
+'logentry-newusers-newusers' => 'Akun pangguno $1 lah dibuek',
+'logentry-newusers-create' => '$1 mambuek akun pangguno',
+'logentry-newusers-create2' => 'Akun pangguno $3 dibuek jo $1',
+'logentry-newusers-autocreate' => 'Akun $1 dibuek sacaro otomatis',
# Search suggestions
'searchsuggest-search' => 'Cari',
* Paġni speċjali normali.
* <span class="mw-specialpagerestricted">Paġni speċjali riservati.</span>
* <span class="mw-specialpagecached">Paġni speċjali disponibbli f\'verżjoni cache (jistgħu jkunu skaduti).</span>',
-'specialpages-group-maintenance' => 'Rapporti tal-manteniment',
+'specialpages-group-maintenance' => "Rapporti ta' manutenzjoni",
'specialpages-group-other' => 'Paġni speċjali oħrajn',
'specialpages-group-login' => 'Idħol / oħloq kont',
'specialpages-group-changes' => 'L-Aħħar modifiki u reġistri',
<?php
-/** Norwegian Bokmål (norsk (bokmål))
+/** Norwegian Bokmål (norsk bokmål)
*
* See MessagesQqq.php for message documentation incl. usage of parameters
* To improve a translation please visit http://translatewiki.net
<?php
-/** Norwegian Nynorsk (norsk (nynorsk))
+/** Norwegian Nynorsk (norsk nynorsk)
*
* See MessagesQqq.php for message documentation incl. usage of parameters
* To improve a translation please visit http://translatewiki.net
'category-subcat-count' => 'Kategorien har {{PLURAL:$2|berre denne underkategorien|{{PLURAL:$1|denne underkategorien|desse $1 underkategoriane}}, av totalt $2}}.',
'category-subcat-count-limited' => 'Kategorien har {{PLURAL:$1|denne underkategorien|desse $1 underkategoriane}}.',
'category-article-count' => 'Kategorien inneheld {{PLURAL:$2|berre denne sida|{{PLURAL:$1|denne sida|desse $1 sidene}}, av totalt $2}}.',
-'category-article-count-limited' => 'Følgjande {{PLURAL:$1|side|$1 sider}} er i denne kategorien.',
+'category-article-count-limited' => '{{PLURAL:$1|Denne sida|Desse $1 sidene}} er i kategorien.',
'category-file-count' => 'Kategorien inneheld {{PLURAL:$2|berre den følgjande fila|dei følgjande {{PLURAL:$1|fil|$1 filene}}, av totalt $2}}.',
-'category-file-count-limited' => 'Følgjande {{PLURAL:$1|fil|$1 filer}} er i denne kategorien.',
+'category-file-count-limited' => '{{PLURAL:$1|Denne fila|Desse $1 filene}} er i kategorien.',
'listingcontinuesabbrev' => 'vidare',
'index-category' => 'Indekserte sider',
'noindex-category' => 'Ikkje-indekserte sider',
'double-redirect-fixer' => 'Omdirigeringsfiksar',
'brokenredirects' => 'Blindvegsomdirigeringar',
-'brokenredirectstext' => 'Dei følgjande omdirigeringane viser til ei side som ikkje finst:',
+'brokenredirectstext' => 'Desse omdirigeringane viser til sider som ikkje finst:',
'brokenredirects-edit' => 'endre',
'brokenredirects-delete' => 'slett',
# Special:ListGroupRights
'listgrouprights' => 'Rettar for brukargrupper',
-'listgrouprights-summary' => 'Følgjande liste viser brukargruppene som er definert på denne wikien, og kvar rettar dei har. Meir informasjon om dei ulike rettane ein kan ha finn ein [[{{MediaWiki:Listgrouprights-helppage}}|her]].',
+'listgrouprights-summary' => 'Detter ei liste som viser brukargruppene som er definerte på wikien, og kva rettar dei har. Det kan finnast [[{{MediaWiki:Listgrouprights-helppage}}|meir informasjon]] om dei ulike rettane.',
'listgrouprights-key' => '* <span class="listgrouprights-granted">Innvilga rettar</span>
* <span class="listgrouprights-granted">Tilbaketrukne rettar</span>',
'listgrouprights-group' => 'Gruppe',
# Undelete
'undelete' => 'Sletta sider',
'undeletepage' => 'Sletta sider',
-'undeletepagetitle' => "'''Følgjande innhald er sletta versjonar av [[:$1]]'''.",
+'undeletepagetitle' => "'''Dette innhaldet er sletta versjonar av [[:$1]]'''.",
'viewdeletedpage' => 'Sjå sletta sider',
'undeletepagetext' => '{{PLURAL:$1|Den følgjande sida er sletta, men ho|Dei følgjande $1 sidene er sletta, men dei}} finst enno i arkivet og kan attopprettast. Arkivet blir periodevis sletta.',
'undelete-fieldset-title' => 'Attenderull endringar',
Du kan oppdatera omdirigeringar som peikar til den opphavlege tittelen automatisk.
Vel du å ikkje gjera dette, pass på å sjå etter [[Special:DoubleRedirects|doble]] eller [[Special:BrokenRedirects|øydelagde omdirigeringar]].
-Merk at sida '''ikkje''' vert flytt dersom det alt finst ei side med den nye tittelen, minder ho er ei omdirigering og ikkje har nokon endringshistorikk. Detter tyder at du kan omdøypa ei side attende til der ho vart omdøypt frå om du gjorde eit mistak, og du kan ikkje skriva over sider som finst.
+Merk at sida '''ikkje''' vert flytt dersom det alt finst ei side med den nye tittelen, minder målsida er ei omdirigering og ikkje har nokon endringshistorikk. Detter tyder at du kan omdøypa ei side attende til der ho vart omdøypt frå om du gjorde eit mistak, og du kan ikkje skriva over sider som finst.
'''ÅTVARING!'''
Dette kan vera ei drastisk og uventa endring for ei populær side; ver viss på at du skjøner konsekvensane av dette før du held fram.",
# Delete
'deletepage' => "Défacer l'pache",
'confirmdeletetext' => "Vos alez défacer eune pache ou un fichié aveuc toutes chés antieusses vérchons.<br /> Confreumer éq ch'est cho éq vos voulez foaire, éq vos conprindez chés consécanches et pi éq ch'est bin s'lon el [[{{MediaWiki:Policy-url}}|politique éd MédiaWiki]].",
-'actioncomplete' => 'Plònne acchon',
+'actioncomplete' => 'Accion toute piéte',
'actionfailed' => "L’action n'a poin réussi",
'deletedtext' => "« $1 » o té défacé.
Vir $2 pou eune lisse d'chés darinnes défachons.",
'blocklogpage' => 'Historia blokad',
'blocklog-showlog' => '{{GENDER:$1|Ten użytkownik był|Ta użytkowniczka była}} już wcześniej {{GENDER:$1|blokowany|blokowana}}. Poniżej znajduje się rejestr blokad:',
'blocklog-showsuppresslog' => '{{GENDER:$1|Ten użytkownik był|Ta użytkowniczka była}} już wcześniej {{GENDER:$1|blokowany oraz ukrywany|blokowana oraz ukrywana}}. Poniżej znajduje się rejestr ukrywania:',
-'blocklogentry' => 'blokuje [[$1]], czas blokady: $2 $3',
+'blocklogentry' => '{{GENDER:$2|zablokował|zablokowała}} [[$1]], czas blokady: $2 $3',
'reblock-logentry' => 'zmienia ustawienia blokady dla [[$1]], czas blokady: $2 $3',
'blocklogtext' => 'Poniżej znajduje się lista blokad założonych i zdjętych z poszczególnych adresów IP.
Na liście nie znajdą się adresy IP, które zablokowano w sposób automatyczny.
'whatlinkshere-title' => 'Páginas que têm links para "$1"',
'whatlinkshere-page' => 'Página:',
'linkshere' => "As seguintes páginas têm links para '''[[:$1]]''':",
-'nolinkshere' => "Não existem links para '''[[:$1]]'''.",
+'nolinkshere' => "Não existem afluentes para '''[[:$1]]''' com as condições especificadas.",
'nolinkshere-ns' => "Não existem links para '''[[:$1]]''' no espaço nominal seleccionado.",
'isredirect' => 'página de redireccionamento',
'istemplate' => 'inclusão',
'whatlinkshere-title' => 'Páginas que têm links para "$1"',
'whatlinkshere-page' => 'Página:',
'linkshere' => "As seguintes páginas possuem links para '''[[:$1]]''':",
-'nolinkshere' => "Não há links para '''[[:$1]]'''.",
+'nolinkshere' => "Não há afluentes para '''[[:$1]]''' com as condições especificadas.",
'nolinkshere-ns' => "Não há links para '''[[:$1]]''' no espaço nominal selecionado.",
'isredirect' => 'página de redirecionamento',
'istemplate' => 'transclusão',
'pageinfo-watchers' => 'Número de vigilantes da página',
'pageinfo-redirects-name' => 'Redirecionamentos para esta página',
'pageinfo-subpages-name' => 'Subpáginas desta página',
-'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|redirecionamento|redirecionamentos}}; $3 {{PLURAL:$3|não-redirecionamento|não-redirecionamentos}})',
+'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|redirecionamento|redirecionamentos}}; $3 {{PLURAL:$3|não redirecionamento|não redirecionamentos}})',
'pageinfo-firstuser' => 'Criador da página',
'pageinfo-firsttime' => 'Data de criação da página',
'pageinfo-lastuser' => 'Último editor',
'pageinfo-magic-words' => '{{PLURAL:$1|Palavra mágica|Palavras mágicas}} ($1)',
'pageinfo-hidden-categories' => '{{PLURAL:$1|Categoria oculta|Categorias ocultas}} ($1)',
'pageinfo-templates' => '{{PLURAL:$1|Predefinição transcluída|Predefinições transcluídas ($1)}}',
-'pageinfo-transclusions' => '{{PLURAL:$1|Página incluída |Páginas incluídas}} ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Página em que é transcluída|Páginas em que é transcluída ($1)}}',
'pageinfo-toolboxlink' => 'Informações da página',
'pageinfo-redirectsto' => 'Redireciona para',
'pageinfo-redirectsto-info' => 'informações',
'pageinfo-robot-noindex' => 'An indication that the page is not indexable (that is, is not listed on the results page of a search engine).',
'pageinfo-views' => 'The number of times the page has been viewed.',
'pageinfo-watchers' => 'The number of users watching the page.',
+'pageinfo-few-watchers' => 'Message displayed when there are fewer than $wgUnwatchedPageThreshold watchers. $1 is the value of $wgUnwatchedPageThreshold.',
'pageinfo-redirects-name' => "The number of redirects to the page.
Used as link text, linked to '{{int:Whatlinkshere-title}}' page ([[Special:WhatLinksHere]]).",
Ce tu no ste scacchie, sta secure de condrollà [[Special:DoubleRedirects|doppie ridirezionaminde]] o [[Special:BrokenRedirects|ridirezionaminde scuasciate]].
Tu si 'u responsabbile de quidde ca cumbine, assicurate ca 'u collegamende condinue a appondà addò avessa scè.
-Vide Bbuene ca 'a pàgene '''non''' g'avene spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.
+Vide Bbuene ca 'a pàgene '''non''' g'avène spustate ce esiste n'otra pàgene cu 'u titole nuéve, a mene ca jè vacande o jè 'na pàgene de ridirezionamende senza storie.
Quieste significhe ca tu puè fà turnà 'u vecchie nome 'a pàgene ce jedde ha state renomenate e t'è rese conde ca è fatte 'na studecarije sovrascrevènne 'na pàgene esistende.
'''ATTENZIONE!'''
'gotaccount' => "Çoktan kayıt oldunuz mu? '''$1'''.",
'gotaccountlink' => 'Oturum açın',
'userlogin-resetlink' => 'Giriş bilgilerinizi mi unuttunuz?',
-'createaccountmail' => 'e-posta ile',
+'createaccountmail' => 'Geçici bir rastgele şifre kullan ve şifreyi aşağıda belirtilen e-posta adresine gönder',
'createaccountreason' => 'Sebep:',
'badretype' => 'Girdiğiniz şifreler birbirleriyle uyuşmuyor.',
'userexists' => 'Girdiğiniz kullanıcı adı zaten kullanımda.
Ayrıca bu ekleyeceğiniz yazıyı sizin yazdığınızı ya da serbest kopyalama izni veren bir kaynaktan kopyaladığınızı bize taahhüt etmektesiniz (ayrıntılar için referans: $1).',
'longpageerror' => "'''Hata: Girdiğiniz metnin uzunluğu kabul edilebilir en fazla uzunluk olan {{PLURAL:$2|bir kilobayt|$2 kilobayt}}tan fazladır ve {{PLURAL:$1|bir kilobayt|$1 kilobayt}} büyüklüğündedir.'''
Değişikliğiniz kaydedilemez.",
-'readonlywarning' => "'''DİKKAT: Bakım nedeni ile veritabanı şu anda kilitlidir. Bu sebeple değişiklikleriniz şu anda kaydedilememektedir. Yazdıklarınızı başka bir editöre alıp saklayabilir ve daha sonra tekrar buraya getirip kaydedebilirsiniz'''
+'readonlywarning' => "'''Uyarı: Bakım nedeniyle veritabanı şu anda kilitlenmiştir. Bu yüzden şu anda düzenlemelerinizi kaydetmek mümkün değildir.'''
+Yaptığınız düzenlemeleri daha sonra kaydetmek isterseniz, yaptığınız düzenlemeleri bir metin dosyasına ya da herhangi bir şeye kopyala yapıştır yaparak saklayınız.
-Kilitleyen hizmetli şu açıklamayı eklemiştir: $1",
+Kilitlemeyi yapan yetkili şu açıklamayı eklemiştir: $1",
'protectedpagewarning' => "'''Uyarı: Bu sayfa koruma altına alınmıştır ve yalnızca hizmetli olanlar tarafından değiştirilebilir.'''
Son günlük girdisi referans amaçlı aşağıda verilmiştir:",
'semiprotectedpagewarning' => "'''Not:''' Bu sayfa sadece kayıtlı kullanıcı olanlar tarafından değiştirilebilir.
'linksearch-ok' => 'Ara',
'linksearch-text' => '"*.wikipedia.org" gibi jokerler kullanılabilir.
En az bir üst-seviye alan gerekir, örneğin "*.org".<br />
-Desteklenen iletişim kuralları: <code>$1</code> (bunların hiçbirini aramanıza eklemeyin).',
+Desteklenen {{PLURAL:$2|iletişim kuralı|iletişim kuralları}}: <code>$1</code> (herhangi bir iletişim kuralı belirtmezseniz http:// otomatik olarak eklenir).',
'linksearch-line' => "$1'e $2'den bağlantı verilmiş",
'linksearch-error' => 'Jokerler sadece ana makine adının başında görünebilir.',
# Special:ActiveUsers
'activeusers' => 'Aktif kullanıcı listesi',
'activeusers-intro' => 'Bu, son $1 {{PLURAL:$1|günde|günde}} bir çeşit etkinlik göstermiş kullanıcıların listesidir.',
-'activeusers-count' => 'Son {{PLURAL:$3|günde|$3 günde}} $1 {{PLURAL:$1|değişiklik|değişiklik}}',
+'activeusers-count' => 'Son {{PLURAL:$3|günde|$3 günde}} $1 {{PLURAL:$1|eylem|eylem}}',
'activeusers-from' => 'Şununla başlayan kullanıcıları görüntüle:',
'activeusers-hidebots' => 'Botları gizle',
'activeusers-hidesysops' => 'Yöneticileri gizle',
'emailuser-title-target' => 'Bu {{GENDER:$1|kullanıcıya}} e-posta gönder',
'emailuser-title-notarget' => 'Kullanıcı e-posta',
'emailpage' => 'Kullanıcıya e-posta gönder',
-'emailpagetext' => 'Bu kullanıcıya e-posta mesajı göndermek için aşağıdaki formu kullanabilirsiniz.
-[[Special:Preferences|Kullanıcı tercihlerinizde]] girdiğiniz e-posta adresiniz, e-postanın "From (Kimden)" adresinde görünecektir, bu yüzden alıcı size direk cevap verebilecektir.',
+'emailpagetext' => 'Bu {{GENDER:$1|kullanıcıya}} e-posta iletisi göndermek için aşağıdaki formu kullanabilirsiniz.
+[[Special:Preferences|Kullanıcı tercihlerinizde]] girdiğiniz e-posta adresiniz, e-postanın "From (Kimden)" adresinde görünecektir, bu yüzden alıcı size doğrudan yanıt verebilecektir.',
'usermailererror' => 'E-posta hizmeti hata verdi:',
'defemailsubject' => '"$1" kullanıcısından {{SITENAME}} e-postası',
'usermaildisabled' => 'Kullanıcı e-postası devre dışı',
'watchnologin' => 'Oturum açık değil.',
'watchnologintext' => 'İzleme listenizi değiştirebilmek için [[Special:UserLogin|oturum açmalısınız]].',
'addwatch' => 'İzleme listesine ekle',
-'addedwatchtext' => '"<nowiki>$1</nowiki>" adlı sayfa [[Special:Watchlist|izleme listenize]] kaydedildi.
-
-Gelecekte, bu sayfaya ve ilgili tartışma sayfasına yapılacak değişiklikler burada listelenecektir.
-
-Kolayca seçilebilmeleri için de [[Special:RecentChanges|son değişiklikler listesi]] başlığı altında koyu harflerle listeleneceklerdir.
-
-Sayfayı izleme listenizden çıkarmak istediğinizde "sayfayı izlemeyi durdur" bağlantısına tıklayabilirsiniz.',
+'addedwatchtext' => '"[[:$1]]" sayfası [[Special:Watchlist|izleme listenize]] eklenmiştir.
+Bundan sonra, bu sayfaya ve ilgili tartışma sayfasına yapılacak değişiklikler burada listelenecek.',
'removewatch' => 'İzleme listesinden kaldır',
'removedwatchtext' => '"[[:$1]]" sayfası [[Special:Watchlist|izleme listenizden]] silinmiştir.',
'watch' => 'İzle',
Bu sayfanın koruma seviyesini değiştirebilirsiniz; ancak bu kademeli korumaya etki etmeyecektir.',
'protect-default' => 'Tüm kullanıcılara izin ver',
'protect-fallback' => '"$1" izni gerektir',
-'protect-level-autoconfirmed' => 'Yeni ve kayıtlı olmayan kullanıcıları engelle',
-'protect-level-sysop' => 'sadece hizmetliler',
+'protect-level-autoconfirmed' => 'Yalnızca otomatik onaylanmış kullanıcılara izin verilir',
+'protect-level-sysop' => 'Yalnızca hizmetlilere izin verilir',
'protect-summary-cascade' => 'kademeli',
'protect-expiring' => 'bitiş tarihi $1 (UTC)',
'protect-expiring-local' => '$1 tarihinde bitiyor',
# JavaScriptTest
'javascripttest' => 'JavaScript denemesi',
'javascripttest-title' => '$1 testleri çalışıyor',
+'javascripttest-qunit-intro' => 'mediawiki.org üzerinden [$1 deneme belgelerine] bakınız.',
'javascripttest-qunit-heading' => 'MediaWiki JavaScript QUnit deneme paketi',
# Tooltip help for the actions
'pageinfo-protect-cascading-from' => 'Korumalar üzerinden geçiş',
'pageinfo-category-info' => 'Kategori bilgileri',
'pageinfo-category-pages' => 'Sayfa sayısı',
+'pageinfo-category-subcats' => 'Alt kategori sayısı',
+'pageinfo-category-files' => 'Dosya sayısı',
# Skin names
'skinname-standard' => 'Klasik',
'file-info-size-pages' => '$1 × $2 piksel, dosya boyutu: $3, MIME tipi: $4, $5 {{PLURAL:$5|sayfa|sayfa}}',
'file-nohires' => 'Daha yüksek çözünürlük yok.',
'svg-long-desc' => 'SVG dosyası, sözde $1 × $2 piksel, dosya boyutu: $3',
+'svg-long-desc-animated' => 'Hareketli SVG dosyası, sözde $1 × $2 piksel, dosya boyutu: $3',
'svg-long-error' => 'Geçersiz SVG dosyası: $1',
'show-big-image' => 'Tam çözünürlük',
'show-big-image-preview' => 'Ön izleme boyutu: $1.',
'minutes' => '{{PLURAL:$1|$1 dakika|$1 dakika}}',
'hours' => '{{PLURAL:$1|$1 saat|$1 saat}}',
'days' => '{{PLURAL:$1|$1 gün|$1 gün}}',
+'months' => '{{PLURAL:$1|$1 ay|$1 ay}}',
+'years' => '{{PLURAL:$1|$1 yıl|$1 yıl}}',
'ago' => '$1 önce',
'just-now' => 'Hemen şimdi',
'exif-worldregiondest' => 'Gösterilen bölge',
'exif-countrydest' => 'Gösterilen ülke',
'exif-countrycodedest' => 'Gösterilen ülke kodu',
+'exif-provinceorstatedest' => 'Gösterilen il ya da devlet/eyalet',
'exif-citydest' => 'Gösterilen Şehir',
'exif-objectname' => 'Kısa başlık',
'exif-specialinstructions' => 'Özel talimatlar',
'exif-giffilecomment' => 'GIF dosyası yorumu',
'exif-intellectualgenre' => 'Öğe türü',
'exif-subjectnewscode' => 'Konu kodu',
+'exif-scenecode' => 'IPTC sahne kodu',
'exif-event' => 'Adı geçen olay',
'exif-organisationinimage' => 'Organizasyon gösterilmiştir',
'exif-personinimage' => 'Adı geçen kişi',
'specialpages-group-highuse' => 'Çok kullanılan sayfalar',
'specialpages-group-pages' => 'Sayfaların listeleri',
'specialpages-group-pagetools' => 'Sayfa araçları',
-'specialpages-group-wiki' => 'Viki bilgiler ve araçlar',
+'specialpages-group-wiki' => 'Veri ve araçlar',
'specialpages-group-redirects' => 'Yönlendirmeli özel sayfalar',
'specialpages-group-spam' => 'Spam araçları',
The password for this new account can be changed on the ''[[Special:ChangePassword|change password]]'' page upon logging in.",
'newarticle' => '(Чаа)',
+'newarticletext' => 'Амдыызында чаяатынмаан арынче шөлүглеп шилчий бердиңер.
+Ону чаяарда адакы көзенекке сөзүглелден таналап киириңер ([[{{MediaWiki:Helppage}}|тайылбыр арынын]] тода көрүңер)..
+Маңаа алдаг аайы-биле шилчий берген болзуңарза, браузериңерниң "дедир" деп таназын базыптыңар.',
+'noarticletext' => "Амдыызында ук арында сөзүглел чок.
+Ол дилеп турар [[Special:Search/{{PAGENAME}}|арыныңар дугайында өске чүүлдерге бижээнин тып аап]] болур силер,
+<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} журналдар аразынга айытканын көрүп болур силер] азы '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} шак ындыг аттыг арын чаяап болур силер]'''</span>.,",
+'noarticletext-nopermission' => 'Амдыызында ук арында сөзүглел чок.
+Ол дилеп турар [[Special:Search/{{PAGENAME}}|арыныңар дугайында өске чүүлдерге бижээнин тып аап]] болур силер, азы
+<span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} журналдар аразынга айытканын көрүп болур силер]. Шак ындыг ук арын чаяар чөшпээрелиңер чок.',
'userpage-userdoesnotexist' => '«<nowiki>$1</nowiki>» деп ажыглакчы is not registered.
Please check if you want to create/edit this page.',
'userpage-userdoesnotexist-view' => '«$1» деп ажыглакчы not registered.',
'template-semiprotected' => '(четпес камгалаан)',
'hiddencategories' => 'Бо арын {{PLURAL:$1|$1 чажыт бөлүкке}} хамааржыр:',
'permissionserrorstext-withaction' => "Мында «'''$2'''» силерниң эргеңер чок, {{PLURAL:$1|чылдагааны|чылдагааннары}}:",
+'recreate-moveddeleted-warn' => "'''Кичээңейлиг. Ооң мурнунда казыттынган арынны катап тургузар деп тур Силер.'''
+
+Ол арынны катап тургузары шынап-ла чугула бе, боданыңар.
+Бо адаанда ол арынның казыышкыннар болгаш өскээр адалгалар журналдарын көргүскен.",
'moveddeleted-notice' => 'Бо арын ап каавыткан.
Адаанда ап каавыткан биле өскээр адаан бижиктер шынзылгазын көргүскен.',
'last' => 'эрткен',
'page_first' => 'бирги',
'page_last' => 'сөөлгү',
+'histlegend' => "Версиялар шилиири: деңнээр дээн арыныңар версияларын имнеңеш, бээр базыптыңар '''{{int:compare-submit}}'''.<br />
+Тайылбыр: '''({{int:cur}})''' — амгы версиядан ылгавыр; '''({{int:last}})''' — эрткен версиядан ылгавыр; '''{{int:minoreditletter}}''' — биче өскерилгелер.",
'history-fieldset-title' => 'Каралаары төөгүзү',
'history-show-deleted' => 'Чүгле казыттынган',
'histfirst' => 'Эң эрте',
'emailsend' => 'Чорудары',
# Watchlist
-'watchlist' => 'Ð\9cÑ\8dÑ\8dÒ£ Ñ\85айгааÑ\80ал даңзÑ\8bм',
+'watchlist' => 'ХайгааÑ\80ал даңзÑ\8bзÑ\8b',
'mywatchlist' => 'Хайгаарал даңзы',
'watchlistfor2' => '$1, силерге $2',
'nowatchlist' => 'Силерниң хайгаарал даңзыңар куруг.',
# Special:BlankPage
'blankpage' => 'Куруг арын',
+# External image whitelist
+'external_image_whitelist' => ' #Бо одуругну ол-ла хевээр арттырыңар<pre>
+#Турум илередиглер (регулярные выражения) фрагментилерин маңаа салыңар (// аразынга турар кезээн)
+#Даштыкы чурумалдар URL-биле олар холбаашкан болур.
+#Дужа бергеннери чурумалдар кылдыр көстүп келир, артканнары чурумалдарже шөлүг кылдыр көстүр.
+# "#" деп демдектен эгелээн одуругларны саналдар кылдыр билдинер.
+#Одуруглар регистрге кунук эвес (билинмес)
+
+#Турум илередиглер фрагментилерин бо одуругнуң кырынга салыңар. А бо одуругну олчаан хевээр арттырыңар</pre>',
+
# Special:Tags
'tag-filter' => '[[Special:Tags|демдек]] шүүрү:',
'tag-filter-submit' => 'Шүүрү',
'prefs-emailconfirm-label' => 'Підтвердження електронної пошти:',
'prefs-textboxsize' => 'Розмір вікна редагування',
'youremail' => 'Адреса електронної пошти:',
-'username' => '{{GENDER:$1|Ім’я користувача}}:',
+'username' => "{{GENDER:$1|Ім'я користувача|Ім'я користувачки}}:",
'uid' => 'Ідентифікатор {{GENDER:$1|користувача}}:',
'prefs-memberingroups' => '{{GENDER:$2|Член}} {{PLURAL:$1|групи|груп}}:',
'prefs-memberingroups-type' => '$1',
Якщо ви цього не зробите, будь ласка, перевірте наявність [[Special:DoubleRedirects|подвійних]] чи [[Special:BrokenRedirects|розірваних]] перенаправлень.
Ви відповідаєте за те, щоб посилання і надалі вказували туди, куди припускалося.
-Ð\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка '''не''' бÑ\83де пеÑ\80ейменована, Ñ\8fкÑ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка з новоÑ\8e назвоÑ\8e вже Ñ\96Ñ\81нÑ\83Ñ\94, окÑ\80Ñ\96м випадкÑ\96в, коли вона порожня або є перенаправленням, а журнал її редагувань порожній.
+Ð\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка '''не''' бÑ\83де пеÑ\80ейменована, Ñ\8fкÑ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка з новоÑ\8e назвоÑ\8e вже Ñ\96Ñ\81нÑ\83Ñ\94, окÑ\80Ñ\96м випадкÑ\96в, коли оÑ\81Ñ\82аннÑ\8f порожня або є перенаправленням, а журнал її редагувань порожній.
Це означає, що ви можете повернути сторінці стару назву, якщо ви перейменували її помилково, але ви не можете затерти існуючу сторінку.
'''ПОПЕРЕДЖЕННЯ!'''
如果您選擇不去做的話,請檢查[[Special:DoubleRedirects|雙重]]或[[Special:BrokenRedirects|損壞重定向]]連結。
您應當負責確定所有連結依然會連到指定的頁面。
-注意如果新頁面已經有內容的話,頁面將'''不會'''被移動,
-除非新頁面是重定向頁,而且沒有修訂歷史。
-這意味著您再必要時可以在移動到新頁面後再移回老的頁面,
-同時您也無法覆蓋現有頁面。
+注意如果新頁面已經有內容的話,頁面將'''不會'''被移動,除非新頁面是重定向頁,而且沒有修訂歷史。
+這意味著您再必要時可以在移動到新頁面後再移回老的頁面,同時您也無法覆蓋現有頁面。
'''警告!'''
對一個經常被訪問的頁面而言這可能是一個重大與唐突的更改;
if ( !is_readable( $settingsFile ) ) {
$this->error( "A copy of your installation's LocalSettings.php\n" .
"must exist and be readable in the source directory.\n" .
- "Use --conf to specify it." , true );
+ "Use --conf to specify it.", true );
}
$wgCommandLineMode = true;
return $settingsFile;
var $infiles = null;
function BaseDump( $infile ) {
- $this->infiles = explode(';',$infile);
+ $this->infiles = explode( ';', $infile );
$this->reader = new XMLReader();
- $infile = array_shift($this->infiles);
+ $infile = array_shift( $this->infiles );
if (defined( 'LIBXML_PARSEHUGE' ) ) {
$this->reader->open( $infile, null, LIBXML_PARSEHUGE );
}
} elseif ( $this->hasOption( "userid" ) ) {
$user = User::newFromId( $this->getOption( 'userid' ) );
} else {
- $this->error( "A \"user\" or \"userid\" must be set to change the password for" , true );
+ $this->error( "A \"user\" or \"userid\" must be set to change the password for", true );
}
if ( !$user || !$user->getId() ) {
$this->error( "No such user: " . $this->getOption( 'user' ), true );
unset( $lags[0] );
echo gmdate( 'H:i:s' ) . ' ';
foreach ( $lags as $lag ) {
- printf( "%-12s " , $lag === false ? 'false' : $lag );
+ printf( "%-12s ", $lag === false ? 'false' : $lag );
}
echo "\n";
sleep( 5 );
$lags = $lb->getLagTimes();
foreach ( $lags as $i => $lag ) {
$name = $lb->getServerName( $i );
- $this->output( sprintf( "%-20s %s\n" , $name, $lag === false ? 'false' : $lag ) );
+ $this->output( sprintf( "%-20s %s\n", $name, $lag === false ? 'false' : $lag ) );
}
}
}
if ( isset( $messages[$key] ) ) {
- $messages[$key] = implode( $messages[$key],", " );
+ $messages[$key] = implode( $messages[$key], ", " );
}
}
return $messages;
'pageinfo-robot-noindex',
'pageinfo-views',
'pageinfo-watchers',
+ 'pageinfo-few-watchers',
'pageinfo-redirects-name',
'pageinfo-redirects-value',
'pageinfo-subpages-name',
);
$tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
$tmpFileName = tempnam( wfTempDir(), 'mwdocgen-' );
- file_put_contents( $tmpFileName , $tmpCfg ) or die( "Could not write doxygen configuration to file $tmpFileName\n" );
+ file_put_contents( $tmpFileName, $tmpCfg ) or die( "Could not write doxygen configuration to file $tmpFileName\n" );
return $tmpFileName;
}
$types = JobQueueGroup::singleton()->getDefaultQueueTypes();
}
+ // Handle any required periodic queue maintenance
+ $this->executeReadyPeriodicTasks();
+
$memcKey = 'jobqueue:dbs:v3';
$pendingDbInfo = $wgMemc->get( $memcKey );
$pendingDBs = array(); // (job type => (db list))
foreach ( $wgLocalDatabases as $db ) {
- $types = JobQueueGroup::singleton( $db )->getQueuesWithJobs();
- foreach ( $types as $type ) {
+ foreach ( JobQueueGroup::singleton( $db )->getQueuesWithJobs() as $type ) {
$pendingDBs[$type][] = $db;
}
}
return $pendingDBs;
}
+
+ /**
+ * Do all ready periodic jobs for all databases every 5 minutes (and .1% of the time)
+ * @return integer
+ */
+ private function executeReadyPeriodicTasks() {
+ global $wgLocalDatabases, $wgMemc;
+
+ $count = 0;
+ $memcKey = 'jobqueue:periodic:lasttime';
+ $timestamp = (int)$wgMemc->get( $memcKey ); // UNIX timestamp or 0
+ if ( ( time() - $timestamp ) > 300 || mt_rand( 0, 999 ) == 0 ) { // 5 minutes
+ if ( $wgMemc->add( "$memcKey:rebuild", 1, 1800 ) ) { // lock
+ foreach ( $wgLocalDatabases as $db ) {
+ $count += JobQueueGroup::singleton( $db )->executeReadyPeriodicTasks();
+ }
+ $wgMemc->set( $memcKey, time() );
+ $wgMemc->delete( "$memcKey:rebuild" ); // unlock
+ }
+ }
+
+ return $count;
+ }
}
$maintClass = "nextJobDb";
public function __construct() {
parent::__construct();
$this->mDescription = "Send purge requests for listed pages to squid";
- $this->addOption( 'purge', 'Whether to update page_touched.' , false, false );
+ $this->addOption( 'purge', 'Whether to update page_touched.', false, false );
$this->addOption( 'namespace', 'Namespace number', false, true );
$this->setBatchSize( 100 );
}
$this->addOption( 'start', 'Name of file to start with', false, true );
$this->addOption( 'end', 'Name of file to end with', false, true );
- $this->addOption( 'mime', '(Inefficient!) Only refresh files with this mime type. Can accept wild-card image/*' , false, true );
+ $this->addOption( 'mime', '(Inefficient!) Only refresh files with this mime type. Can accept wild-card image/*', false, true );
$this->addOption( 'metadata-contains', '(Inefficient!) Only refresh files where the img_metadata field contains this string. Can be used if its known a specific property was being extracted incorrectly.', false, true );
}
$n = 0;
$group = JobQueueGroup::singleton();
+ // Handle any required periodic queue maintenance
+ $count = $group->executeReadyPeriodicTasks();
+ if ( $count > 0 ) {
+ $this->runJobsLog( "Executed $count periodic queue task(s)." );
+ }
+
do {
$job = ( $type === false )
? $group->pop( JobQueueGroup::TYPE_DEFAULT, JobQueueGroup::USE_CACHE )
$max_length_value = $max_length_desc = 0;
foreach ( $fields as $field => $desc ) {
$max_length_value = max( $max_length_value, strlen( $stats->$field ) );
- $max_length_desc = max( $max_length_desc , strlen( $desc ) ) ;
+ $max_length_desc = max( $max_length_desc, strlen( $desc ) ) ;
}
// Show them
'height' => 16,
'width' => 16,
'type' => 'image/x-icon' ),
- wfExpandUrl( $wgFavicon , PROTO_CURRENT ) );
+ wfExpandUrl( $wgFavicon, PROTO_CURRENT ) );
$urls = array();
<td class="mw-profileinfo-count"><?php echo $this->count(); ?></td>
<td class="mw-profileinfo-cpr"><?php echo round( sprintf( '%.2f', $this->callsPerRequest() ), 2 ); ?></td>
<td class="mw-profileinfo-tpc"><?php echo round( sprintf( '%.2f', $this->timePerCall() ), 2 ); ?></td>
- <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f' ,$this->memoryPerCall() / 1024 ), 2 ); ?></td>
+ <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f', $this->memoryPerCall() / 1024 ), 2 ); ?></td>
<td class="mw-profileinfo-tpr"><?php echo @round( sprintf( '%.2f', $this->time() / self::$totalcount ), 2 ); ?></td>
- <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f' ,$this->memory() / self::$totalcount / 1024 ), 2 ); ?></td>
+ <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f', $this->memory() / self::$totalcount / 1024 ), 2 ); ?></td>
</tr>
<?php
if ( $ex ) {
),
'jquery.json' => array(
'scripts' => 'resources/jquery/jquery.json.js',
+ 'targets' => array( 'mobile', 'desktop' ),
),
'jquery.localize' => array(
'scripts' => 'resources/jquery/jquery.localize.js',
font-size: 1em;
}
body {
- background-color: #f3f3f3;
- /* @embed */
- background-image: url(images/page-base.png);
+ background-color: #f6f6f6;
}
/* Content */
div#content {
margin-left: 10em;
padding: 1em;
- /* @embed */
- background-image: url(images/border.png);
- background-position: top left;
- background-repeat: repeat-y;
+ /* Border on top, left, and bottom side */
+ border: 1px solid #a7d7f9;
+ border-right-width: 0;
+ /* Merge the border with tabs' one (in their background image) */
+ margin-top: -1px;
background-color: white;
color: black;
direction: ltr;
margin-top: -5em;
margin-left: 10em;
height: 5em;
- /* @embed */
- background-image: url(images/border.png);
- background-position: bottom left;
- background-repeat: repeat-x;
}
div#mw-head {
position: absolute;
margin-left: 10em;
margin-top: 0;
padding: 0.75em;
- /* @embed */
- background-image: url(images/border.png);
- background-position: top left;
- background-repeat: repeat-x;
direction: ltr;
}
div#footer ul {
margin-top: -2px;
clear: both;
border: solid 1px #ccc;
- background-color: #f9f9f9;
- /* @embed */
- background-image: url(images/preferences-base.png);
+ background-color: #fafafa;
}
#preferences fieldset {
border: none;
</p>
!! end
-!! test
-Formatted date
-!! config
-wgUseDynamicDates=1
-!! input
-[[2009-03-24]]
-!! result
-<p><span class="mw-formatted-date" title="2009-03-24"><a href="/index.php?title=2009&action=edit&redlink=1" class="new" title="2009 (page does not exist)">2009</a>-<a href="/index.php?title=March_24&action=edit&redlink=1" class="new" title="March 24 (page does not exist)">03-24</a></span>
-</p>
-!!end
-
!!test
formatdate parser function
!!input
</p>
!! end
-!! test
-Linked date with autoformatting disabled
-!! config
-wgUseDynamicDates=false
-!! input
-[[2009-03-24]]
-!! result
-<p><a href="/index.php?title=2009-03-24&action=edit&redlink=1" class="new" title="2009-03-24 (page does not exist)">2009-03-24</a>
-</p>
-!! end
-
!! test
Spacing of numbers in formatted dates
!! input
</p>
!! end
-!! test
-Spacing of numbers in formatted dates (linked)
-!! config
-wgUseDynamicDates=true
-!! input
-[[January 15]]
-!! result
-<p><span class="mw-formatted-date" title="01-15"><a href="/index.php?title=January_15&action=edit&redlink=1" class="new" title="January 15 (page does not exist)">January 15</a></span>
-</p>
-!! end
-
!! test
formatdate parser function, with default format and on a page of which the content language is always English and different from the wiki content language
!! options
--- /dev/null
+<root><template><title>vorlage</title></template>
+
+<tplarg lineStart="1"><title>argument</title></tplarg>
+
+Nach [[:meta:Help:Expansion#XML parse tree]]
+{<tplarg><title>vorlagenname</title></tplarg>}
+<template lineStart="1"><title> <template><title>vorlagenname</title></template></title></template>
+<template lineStart="1"><title><template><title>vorlagenname</title></template> </title></template>
+<template lineStart="1"><title><template><title>vorlagenname</title></template>erweiterung</title></template>
+
+<template lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg></title></template>
+<tplarg lineStart="1"><title> <template><title>vorlagenname</title></template></title></tplarg>
+<template lineStart="1"><title> <tplarg><title>vorlagenname</title></tplarg></title></template>
+<tplarg lineStart="1"><title><template><title>vorlagenname</title></template> </title></tplarg>
+<template lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg> </title></template>
+
+nur etwas erweitert
+<tplarg lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg></title></tplarg>
+<tplarg lineStart="1"><title> <tplarg><title>vorlagenname</title></tplarg></title></tplarg>
+<tplarg lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg> </title></tplarg>
+<template lineStart="1"><title> {<tplarg><title>vorlagenname</title></tplarg></title></template>}
+{<tplarg><title> <template><title>vorlagenname</title></template></title></tplarg>}
+<template lineStart="1"><title> <template><title> <template><title>vorlagenname</title></template></title></template></title></template>
+{<tplarg><title> <template><title>vorlagenname</title></template>} </title></tplarg>
+{<template><title><tplarg><title>vorlagenname</title></tplarg>} </title></template>
+{<tplarg><title><template><title>vorlagenname</title></template> </title></tplarg>}
+<template lineStart="1"><title> <template><title><template><title>vorlagenname</title></template> </title></template></title></template>
+<tplarg lineStart="1"><title> {<template><title>vorlagenname</title></template> </title></tplarg>}
+
+{<tplarg><title><tplarg><title> </title></tplarg></title></tplarg>}
+
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg></title></tplarg></title></template>
+<tplarg lineStart="1"><title><tplarg><title><template><title> </title></template> </title></tplarg></title></tplarg>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg> </title></tplarg></title></template>
+{{<tplarg><title><tplarg><title> </title></tplarg>} </title></tplarg>}
+<tplarg lineStart="1"><title><template><title><tplarg><title> </title></tplarg></title></template> </title></tplarg>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg></title></tplarg> </title></template>
+{<tplarg><title><template><title><template><title> </title></template> </title></template> </title></tplarg>}
+{<template><title><tplarg><title><template><title> </title></template> </title></tplarg>} </title></template>
+{<template><title><template><title><tplarg><title> </title></tplarg>} </title></template> </title></template>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg> </title></tplarg> </title></template>
+<tplarg lineStart="1"><title><template><title><tplarg><title> </title></tplarg> </title></template> </title></tplarg>
+<tplarg lineStart="1"><title><tplarg><title><template><title> </title></template> </title></tplarg> </title></tplarg>
+<template lineStart="1"><title><template><title><template><title><template><title> </title></template> </title></template> </title></template> </title></template>
+
+<template lineStart="1"><title>vorlage</title></template>
+
+<tplarg lineStart="1"><title>argument</title></tplarg>
+
+Nach [[:meta:Help:Expansion#XML parse tree]]
+{<tplarg><title>vorlagenname</title></tplarg>}
+<template lineStart="1"><title> <template><title>vorlagenname</title></template></title></template>
+<template lineStart="1"><title><template><title>vorlagenname</title></template> </title></template>
+<template lineStart="1"><title><template><title>vorlagenname</title></template>erweiterung</title></template>
+
+<template lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg></title></template>
+<tplarg lineStart="1"><title> <template><title>vorlagenname</title></template></title></tplarg>
+<template lineStart="1"><title> <tplarg><title>vorlagenname</title></tplarg></title></template>
+<tplarg lineStart="1"><title><template><title>vorlagenname</title></template> </title></tplarg>
+<template lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg> </title></template>
+
+nur etwas erweitert
+<tplarg lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg></title></tplarg>
+<tplarg lineStart="1"><title> <tplarg><title>vorlagenname</title></tplarg></title></tplarg>
+<tplarg lineStart="1"><title><tplarg><title>vorlagenname</title></tplarg> </title></tplarg>
+<template lineStart="1"><title> {<tplarg><title>vorlagenname</title></tplarg></title></template>}
+{<tplarg><title> <template><title>vorlagenname</title></template></title></tplarg>}
+<template lineStart="1"><title> <template><title> <template><title>vorlagenname</title></template></title></template></title></template>
+{<tplarg><title> <template><title>vorlagenname</title></template>} </title></tplarg>
+{<template><title><tplarg><title>vorlagenname</title></tplarg>} </title></template>
+{<tplarg><title><template><title>vorlagenname</title></template> </title></tplarg>}
+<template lineStart="1"><title> <template><title><template><title>vorlagenname</title></template> </title></template></title></template>
+<tplarg lineStart="1"><title> {<template><title>vorlagenname</title></template> </title></tplarg>}
+
+{<tplarg><title><tplarg><title> </title></tplarg></title></tplarg>}
+
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg></title></tplarg></title></template>
+<tplarg lineStart="1"><title><tplarg><title><template><title> </title></template> </title></tplarg></title></tplarg>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg> </title></tplarg></title></template>
+{{<tplarg><title><tplarg><title> </title></tplarg>} </title></tplarg>}
+<tplarg lineStart="1"><title><template><title><tplarg><title> </title></tplarg></title></template> </title></tplarg>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg></title></tplarg> </title></template>
+{<tplarg><title><template><title><template><title> </title></template> </title></template> </title></tplarg>}
+{<template><title><tplarg><title><template><title> </title></template> </title></tplarg>} </title></template>
+{<template><title><template><title><tplarg><title> </title></tplarg>} </title></template> </title></template>
+<template lineStart="1"><title><tplarg><title><tplarg><title> </title></tplarg> </title></tplarg> </title></template>
+<tplarg lineStart="1"><title><template><title><tplarg><title> </title></tplarg> </title></template> </title></tplarg>
+<tplarg lineStart="1"><title><tplarg><title><template><title> </title></template> </title></tplarg> </title></tplarg>
+<template lineStart="1"><title><template><title><template><title><template><title> </title></template> </title></template> </title></template> </title></template>
+</root>
\ No newline at end of file
--- /dev/null
+{{vorlage}}
+
+{{{argument}}}
+
+Nach [[:meta:Help:Expansion#XML parse tree]]
+{{{{vorlagenname}}}}
+{{ {{vorlagenname}}}}
+{{{{vorlagenname}} }}
+{{{{vorlagenname}}erweiterung}}
+
+{{{{{vorlagenname}}}}}
+{{{ {{vorlagenname}}}}}
+{{ {{{vorlagenname}}}}}
+{{{{{vorlagenname}} }}}
+{{{{{vorlagenname}}} }}
+
+nur etwas erweitert
+{{{{{{vorlagenname}}}}}}
+{{{ {{{vorlagenname}}}}}}
+{{{{{{vorlagenname}}} }}}
+{{ {{{{vorlagenname}}}}}}
+{{{{ {{vorlagenname}}}}}}
+{{ {{ {{vorlagenname}}}}}}
+{{{{ {{vorlagenname}}} }}}
+{{{{{{vorlagenname}}}} }}
+{{{{{{vorlagenname}} }}}}
+{{ {{{{vorlagenname}} }}}}
+{{{ {{{vorlagenname}} }}}}
+
+{{{{{{{ }}}}}}}
+
+{{{{{{{{ }}}}}}}}
+{{{{{{{{ }} }}}}}}
+{{{{{{{{ }}} }}}}}
+{{{{{{{{ }}}} }}}}
+{{{{{{{{ }}}}} }}}
+{{{{{{{{ }}}}}} }}
+{{{{{{{{ }} }} }}}}
+{{{{{{{{ }} }}}} }}
+{{{{{{{{ }}}} }} }}
+{{{{{{{{ }}} }}} }}
+{{{{{{{{ }}} }} }}}
+{{{{{{{{ }} }}} }}}
+{{{{{{{{ }} }} }} }}
+
+{{vorlage}}
+
+{{{argument}}}
+
+Nach [[:meta:Help:Expansion#XML parse tree]]
+{{{{vorlagenname}}}}
+{{ {{vorlagenname}}}}
+{{{{vorlagenname}} }}
+{{{{vorlagenname}}erweiterung}}
+
+{{{{{vorlagenname}}}}}
+{{{ {{vorlagenname}}}}}
+{{ {{{vorlagenname}}}}}
+{{{{{vorlagenname}} }}}
+{{{{{vorlagenname}}} }}
+
+nur etwas erweitert
+{{{{{{vorlagenname}}}}}}
+{{{ {{{vorlagenname}}}}}}
+{{{{{{vorlagenname}}} }}}
+{{ {{{{vorlagenname}}}}}}
+{{{{ {{vorlagenname}}}}}}
+{{ {{ {{vorlagenname}}}}}}
+{{{{ {{vorlagenname}}} }}}
+{{{{{{vorlagenname}}}} }}
+{{{{{{vorlagenname}} }}}}
+{{ {{{{vorlagenname}} }}}}
+{{{ {{{vorlagenname}} }}}}
+
+{{{{{{{ }}}}}}}
+
+{{{{{{{{ }}}}}}}}
+{{{{{{{{ }} }}}}}}
+{{{{{{{{ }}} }}}}}
+{{{{{{{{ }}}} }}}}
+{{{{{{{{ }}}}} }}}
+{{{{{{{{ }}}}}} }}
+{{{{{{{{ }} }} }}}}
+{{{{{{{{ }} }}}} }}
+{{{{{{{{ }}}} }} }}
+{{{{{{{{ }}} }}} }}
+{{{{{{{{ }}} }} }}}
+{{{{{{{{ }} }}} }}}
+{{{{{{{{ }} }} }} }}
}
-
- function testInStringTest() {
-
- $this->assertTrue( in_string( 'foo', 'foobar' ), 'foo is in foobar' );
- $this->assertFalse( in_string( 'Bar', 'foobar' ), 'Case-sensitive by default' );
- $this->assertTrue( in_string( 'Foo', 'foobar', true ), 'Case-insensitive when asked' );
-
- }
-
/**
* test @see wfShorthandToInteger()
* @dataProvider provideShorthand
+++ /dev/null
-<?php
-
-class ParserOptionsTest extends MediaWikiTestCase {
-
- private $popts;
- private $pcache;
-
- protected function setUp() {
- global $wgLanguageCode, $wgUser;
- parent::setUp();
-
- $langObj = Language::factory( $wgLanguageCode );
-
- $this->setMwGlobals( array(
- 'wgContLang' => $langObj,
- 'wgUseDynamicDates' => true,
- ) );
-
- $this->popts = ParserOptions::newFromUserAndLang( $wgUser, $langObj );
- $this->pcache = ParserCache::singleton();
- }
-
- /**
- * ParserOptions::optionsHash was not giving consistent results when $wgUseDynamicDates was set
- * @group Database
- */
- function testGetParserCacheKeyWithDynamicDates() {
- $title = Title::newFromText( "Some test article" );
- $page = WikiPage::factory( $title );
-
- $pcacheKeyBefore = $this->pcache->getKey( $page, $this->popts );
- $this->assertNotNull( $this->popts->getDateFormat() );
-
- $pcacheKeyAfter = $this->pcache->getKey( $page, $this->popts );
- $this->assertEquals( $pcacheKeyBefore, $pcacheKeyAfter );
- }
-}
--- /dev/null
+<?php
+
+class UIDGeneratorTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider provider_testTimestampedUID
+ */
+ public function testTimestampedUID( $method, $digitlen, $bits, $tbits, $hostbits ) {
+ $id = call_user_func( array( 'UIDGenerator', $method ) );
+ $this->assertEquals( true, ctype_digit( $id ), "UID made of digit characters" );
+ $this->assertLessThanOrEqual( $digitlen, strlen( $id ),
+ "UID has the right number of digits" );
+ $this->assertLessThanOrEqual( $bits, strlen( wfBaseConvert( $id, 10, 2 ) ),
+ "UID has the right number of bits" );
+
+ $ids = array();
+ for ( $i = 0; $i < 300; $i++ ) {
+ $ids[] = call_user_func( array( 'UIDGenerator', $method ) );
+ }
+
+ $lastId = array_shift( $ids );
+ if ( $hostbits ) {
+ $lastHost = substr( wfBaseConvert( $lastId, 10, 2, $bits ), -$hostbits );
+ }
+
+ $this->assertArrayEquals( array_unique( $ids ), $ids, "All generated IDs are unique." );
+
+ foreach ( $ids as $id ) {
+ $id_bin = wfBaseConvert( $id, 10, 2 );
+ $lastId_bin = wfBaseConvert( $lastId, 10, 2 );
+
+ $this->assertGreaterThanOrEqual(
+ substr( $id_bin, 0, $tbits ),
+ substr( $lastId_bin, 0, $tbits ),
+ "New ID timestamp ($id_bin) >= prior one ($lastId_bin)." );
+
+ if ( $hostbits ) {
+ $this->assertEquals(
+ substr( $id_bin, 0, -$hostbits ),
+ substr( $lastId_bin, 0, -$hostbits ),
+ "Host ID of ($id_bin) is same as prior one ($lastId_bin)." );
+ }
+
+ $lastId = $id;
+ }
+ }
+
+ /**
+ * array( method, length, bits, hostbits )
+ */
+ public static function provider_testTimestampedUID() {
+ return array(
+ array( 'newTimestampedUID128', 39, 128, 46, 48 ),
+ array( 'newTimestampedUID128', 39, 128, 46, 48 ),
+ array( 'newTimestampedUID88', 27, 88, 46, 32 ),
+ );
+ }
+
+ public function testUUIDv4() {
+ for ( $i = 0; $i < 100; $i++ ) {
+ $id = UIDGenerator::newUUIDv4();
+ $this->assertEquals( true,
+ preg_match( '!^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$!', $id ),
+ "UID $id has the right format" );
+
+ $id = UIDGenerator::newRawUUIDv4();
+ $this->assertEquals( true,
+ preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
+ "UID $id has the right format" );
+
+ $id = UIDGenerator::newRawUUIDv4( UIDGenerator::QUICK_RAND );
+ $this->assertEquals( true,
+ preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
+ "UID $id has the right format" );
+ }
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * @group JobQueue
+ * @group medium
+ * @group Database
+ */
+class JobQueueTest extends MediaWikiTestCase {
+ protected $old = array();
+
+ function __construct( $name = null, array $data = array(), $dataName = '' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->tablesUsed[] = 'job';
+ }
+
+ protected function setUp() {
+ global $wgMemc;
+ parent::setUp();
+ $this->old['wgMemc'] = $wgMemc;
+ $wgMemc = new HashBagOStuff();
+ $this->queueRand = JobQueue::factory( array( 'class' => 'JobQueueDB',
+ 'wiki' => wfWikiID(), 'type' => 'null', 'order' => 'random' ) );
+ $this->queueRandTTL = JobQueue::factory( array( 'class' => 'JobQueueDB',
+ 'wiki' => wfWikiID(), 'type' => 'null', 'order' => 'random', 'claimTTL' => 10 ) );
+ $this->queueFifo = JobQueue::factory( array( 'class' => 'JobQueueDB',
+ 'wiki' => wfWikiID(), 'type' => 'null', 'order' => 'fifo' ) );
+ $this->queueFifoTTL = JobQueue::factory( array( 'class' => 'JobQueueDB',
+ 'wiki' => wfWikiID(), 'type' => 'null', 'order' => 'fifo', 'claimTTL' => 10 ) );
+ }
+
+ protected function tearDown() {
+ global $wgMemc;
+ parent::tearDown();
+ foreach ( array( 'queueRand', 'queueRandTTL', 'queueFifo', 'queueFifoTTL' ) as $q ) {
+ do {
+ $job = $this->$q->pop();
+ if ( $job ) $this->$q->ack( $job );
+ } while ( $job );
+ }
+ $this->queueRand = null;
+ $this->queueRandTTL = null;
+ $this->queueFifo = null;
+ $this->queueFifoTTL = null;
+ $wgMemc = $this->old['wgMemc'];
+ }
+
+ /**
+ * @dataProvider provider_queueLists
+ */
+ function testProperties( $queue, $order, $recycles, $desc ) {
+ $queue = $this->$queue;
+
+ $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
+ $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
+ }
+
+ /**
+ * @dataProvider provider_queueLists
+ */
+ function testBasicOperations( $queue, $order, $recycles, $desc ) {
+ $queue = $this->$queue;
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
+
+ $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
+ $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
+
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
+
+ $job1 = $queue->pop();
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
+
+ $queue->flushCaches();
+ if ( $recycles ) {
+ $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ } else {
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ $job2 = $queue->pop();
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ if ( $recycles ) {
+ $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ } else {
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ $queue->ack( $job1 );
+
+ $queue->flushCaches();
+ if ( $recycles ) {
+ $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ } else {
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ $queue->ack( $job2 );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ /**
+ * @dataProvider provider_queueLists
+ */
+ function testBasicDeduplication( $queue, $order, $recycles, $desc ) {
+ $queue = $this->$queue;
+
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
+
+ $this->assertTrue( $queue->batchPush(
+ array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
+ "Push worked ($desc)" );
+
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
+
+ $this->assertTrue( $queue->batchPush(
+ array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
+ "Push worked ($desc)" );
+
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
+
+ $job1 = $queue->pop();
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ if ( $recycles ) {
+ $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ } else {
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ $queue->ack( $job1 );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
+ }
+
+ /**
+ * @dataProvider provider_queueLists
+ */
+ function testRootDeduplication( $queue, $order, $recycles, $desc ) {
+ $queue = $this->$queue;
+
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
+
+ $id = wfRandomString( 32 );
+ $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
+ for ( $i=0; $i<5; ++$i ) {
+ $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
+ }
+ $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
+ sleep( 1 ); // roo job timestamp will increase
+ $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
+ $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
+ "Root job signatures have different timestamps." );
+ for ( $i=0; $i<5; ++$i ) {
+ $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
+ }
+ $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
+
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
+
+ $dupcount = 0;
+ $jobs = array();
+ do {
+ $job = $queue->pop();
+ if ( $job ) {
+ $jobs[] = $job;
+ $queue->ack( $job );
+ }
+ if ( $job instanceof DuplicateJob ) ++$dupcount;
+ } while ( $job );
+
+ $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
+ $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
+ }
+
+ /**
+ * @dataProvider provider_fifoQueueLists
+ */
+ function testJobOrder( $queue, $recycles, $desc ) {
+ $queue = $this->$queue;
+
+ $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
+
+ for ( $i=0; $i<10; ++$i ) {
+ $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
+ }
+
+ for ( $i=0; $i<10; ++$i ) {
+ $job = $queue->pop();
+ $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
+ $params = $job->getParams();
+ $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
+ $queue->ack( $job );
+ }
+
+ $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
+
+ $queue->flushCaches();
+ $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
+ $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
+ }
+
+ function provider_queueLists() {
+ return array(
+ array( 'queueRand', 'rand', false, 'Random queue without ack()' ),
+ array( 'queueRandTTL', 'rand', true, 'Random queue with ack()' ),
+ array( 'queueFifo', 'fifo', false, 'Ordered queue without ack()' ),
+ array( 'queueFifoTTL', 'fifo', true, 'Ordered queue with ack()' )
+ );
+ }
+
+ function provider_fifoQueueLists() {
+ return array(
+ array( 'queueFifo', false, 'Ordered queue without ack()' ),
+ array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
+ );
+ }
+
+ function newJob( $i = 0, $rootJob = array() ) {
+ return new NullJob( Title::newMainPage(),
+ array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
+ }
+
+ function newDedupedJob( $i = 0, $rootJob = array() ) {
+ return new NullJob( Title::newMainPage(),
+ array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
+ }
+}
array( "Factorial" ), # http://en.wikipedia.org/w/index.php?title=Template:Factorial&oldid=98548758 GFDL + CC-BY-SA by Polonium
array( "All_system_messages" ), # http://tl.wiktionary.org/w/index.php?title=Suleras:All_system_messages&oldid=2765 GPL text generated by MediaWiki
array( "Fundraising" ), # http://tl.wiktionary.org/w/index.php?title=MediaWiki:Sitenotice&oldid=5716 GFDL + CC-BY-SA, copied there by Sky Harbor.
+ array( "NestedTemplates" ), # bug 27936
);
}