Merge "API edit: allow ConfirmEdit to use the merged parse"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 10 Dec 2014 20:22:05 +0000 (20:22 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 10 Dec 2014 20:22:05 +0000 (20:22 +0000)
172 files changed:
HISTORY
RELEASE-NOTES-1.25
autoload.php
composer.json
docs/hooks.txt
images/.htaccess
includes/Block.php
includes/CdbCompat.php
includes/EditPage.php
includes/OutputPage.php
includes/PrefixSearch.php
includes/Status.php
includes/Title.php
includes/User.php
includes/Xml.php
includes/actions/RevertAction.php
includes/api/ApiBase.php
includes/api/ApiEditPage.php
includes/api/ApiOpenSearch.php
includes/api/ApiPageSet.php
includes/api/ApiParse.php
includes/api/ApiPatrol.php
includes/api/ApiQueryRevisionsBase.php
includes/api/ApiQuerySearch.php
includes/api/ApiStashEdit.php
includes/api/i18n/ar.json
includes/api/i18n/be-tarask.json
includes/api/i18n/es.json
includes/api/i18n/fa.json
includes/api/i18n/fr.json
includes/api/i18n/ms.json
includes/api/i18n/te.json [new file with mode: 0644]
includes/api/i18n/zh-hans.json
includes/cache/LocalisationCache.php
includes/cache/ResourceFileCache.php
includes/changes/OldChangesList.php
includes/changes/RecentChange.php
includes/db/Database.php
includes/db/LBFactory.php
includes/deferred/SearchUpdate.php
includes/exception/MWExceptionHandler.php
includes/filebackend/FSFileBackend.php
includes/filerepo/file/File.php
includes/installer/DatabaseUpdater.php
includes/installer/WebInstallerOutput.php
includes/installer/i18n/bg.json
includes/installer/i18n/ca.json
includes/installer/i18n/fi.json
includes/installer/i18n/te.json
includes/installer/i18n/vi.json
includes/libs/GenericArrayObject.php
includes/libs/IPSet.php
includes/libs/ScopedCallback.php
includes/libs/Xhprof.php
includes/libs/XmlTypeCheck.php
includes/media/Bitmap.php
includes/page/WikiPage.php
includes/parser/MWTidy.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/profiler/Profiler.php
includes/profiler/ProfilerStandard.php
includes/profiler/ProfilerStub.php
includes/profiler/ProfilerXhprof.php
includes/profiler/SectionProfiler.php
includes/profiler/output/ProfilerOutputDb.php
includes/profiler/output/ProfilerOutputText.php
includes/profiler/output/ProfilerOutputUdp.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderImage.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderImageModule.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderSkinModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php
includes/resourceloader/ResourceLoaderUserDefaultsModule.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelArchiveList.php
includes/revisiondelete/RevDelArchivedFileList.php
includes/revisiondelete/RevDelFileList.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionList.php
includes/revisiondelete/RevisionDeleteUser.php
includes/site/SiteListFileCache.php
includes/site/SiteListFileCacheBuilder.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/ImageQueryPage.php
includes/specialpage/PageQueryPage.php
includes/specialpage/QueryPage.php
includes/specialpage/WantedQueryPage.php
includes/specials/SpecialContributions.php
includes/specials/SpecialWantedfiles.php
includes/utils/AutoloadGenerator.php
languages/i18n/ar.json
languages/i18n/be-tarask.json
languages/i18n/bn.json
languages/i18n/ca.json
languages/i18n/cdo.json
languages/i18n/ce.json
languages/i18n/de.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gd.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/ilo.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/lb.json
languages/i18n/mk.json
languages/i18n/nap.json
languages/i18n/nl.json
languages/i18n/pl.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/sco.json
languages/i18n/sl.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/uz.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/backupTextPass.inc
maintenance/checkLess.php
maintenance/findHooks.php
maintenance/storage/recompressTracked.php
maintenance/updateArticleCount.php
resources/Resources.php
resources/lib/oojs-ui/images/grab.cur [new file with mode: 0644]
resources/lib/oojs-ui/images/grabbing.cur [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-apex.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-apex.svg.css
resources/lib/oojs-ui/oojs-ui-mediawiki.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css
resources/lib/oojs-ui/oojs-ui.js
resources/src/jquery/jquery.client.js
resources/src/jquery/jquery.textSelection.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.legacy/images/magnify-clip-ltr.png [new file with mode: 0644]
resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg [new file with mode: 0644]
resources/src/mediawiki.legacy/images/magnify-clip-rtl.png [new file with mode: 0644]
resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg [new file with mode: 0644]
resources/src/mediawiki.legacy/oldshared.css
resources/src/mediawiki/mediawiki.Uri.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.js
tests/TestsAutoLoader.php
tests/parser/parserTests.txt
tests/phpunit/includes/PrefixSearchTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/api/query/ApiQueryTest.php
tests/phpunit/includes/cache/LocalisationCacheTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/media/FormatMetadataTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/qunit/suites/resources/jquery/jquery.client.test.js
tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js

diff --git a/HISTORY b/HISTORY
index 78ece33..64d409f 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -462,9 +462,9 @@ changes to languages because of Bugzilla reports.
   meaning that JavaScript is no longer executed in these browser versions.
 * Browser support for Opera 11 lowered from Grade A to Grade C.
 * Removed IEFixes module which existed purely to provide support for MSIE versions
+  below 7 (conditionally loaded only for those browsers).
 * Deprecated SpecialPageFactory::getList() in favor of
   SpecialPageFactory::getNames()
-  below 7 (conditionally loaded only for those browsers).
 * Action::checkCanExecute() no longer has a return value.
 * Removed cleanupForIRC(), loadFromCurRow(), newFromCurRow(), notifyRC2UDP()
   and sendToUDP() from RecentChange.php. (deprecated since 1.22)
index 36d698f..06d2d66 100644 (file)
@@ -29,7 +29,7 @@ production.
 * $wgOpenSearchTemplate is deprecated in favor of $wgOpenSearchTemplates.
 * Edits are now prepared via AJAX as users type edit summaries. This behavior
   can be disabled via $wgAjaxEditStash.
-* (bug 44740) The temporary option $wgIncludejQueryMigrate was removed, along
+* (T46740) The temporary option $wgIncludejQueryMigrate was removed, along
   with the jQuery Migrate library, as indicated when this option was provided in
   MediaWiki 1.24.
 
index dbc28ed..175ab42 100644 (file)
@@ -951,6 +951,8 @@ $wgAutoloadLocalClasses = array(
        'ResourceLoaderFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderFileModule.php',
        'ResourceLoaderFilePageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderFilePageModule.php',
        'ResourceLoaderFilePath' => __DIR__ . '/includes/resourceloader/ResourceLoaderFilePath.php',
+       'ResourceLoaderImage' => __DIR__ . '/includes/resourceloader/ResourceLoaderImage.php',
+       'ResourceLoaderImageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderImageModule.php',
        'ResourceLoaderLanguageDataModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageDataModule.php',
        'ResourceLoaderLanguageNamesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageNamesModule.php',
        'ResourceLoaderModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderModule.php',
@@ -959,6 +961,7 @@ $wgAutoloadLocalClasses = array(
        'ResourceLoaderSkinModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSkinModule.php',
        'ResourceLoaderStartUpModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderStartUpModule.php',
        'ResourceLoaderUserCSSPrefsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php',
+       'ResourceLoaderUserDefaultsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserDefaultsModule.php',
        'ResourceLoaderUserGroupsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserGroupsModule.php',
        'ResourceLoaderUserModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserModule.php',
        'ResourceLoaderUserOptionsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserOptionsModule.php',
index bcd196f..0634c2d 100644 (file)
@@ -20,7 +20,7 @@
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
                "cssjanus/cssjanus": "1.1.1",
-               "cdb/cdb": "1.0.0"
+               "wikimedia/cdb": "1.0.1"
        },
        "require-dev": {
                "phpunit/phpunit": "*"
index 1c6e5c5..4a9e0b8 100644 (file)
@@ -776,12 +776,10 @@ $out: OutputPage object
 'BeforeParserFetchFileAndTitle': Before an image is rendered by Parser.
 $parser: Parser object
 $nt: the image title
-&$options: array of options to RepoGroup::findFile
+&$options: array of options to RepoGroup::findFile. If it contains 'broken'
+  as a key then the file will appear as a broken thumbnail.
 &$descQuery: query string to add to thumbnail URL
 
-FIXME: Where does the below sentence fit in?
-If 'broken' is a key in $options then the file will appear as a broken thumbnail.
-
 'BeforeParserFetchTemplateAndtitle': Before a template is fetched by Parser.
 $parser: Parser object
 $title: title of the template
@@ -2425,7 +2423,7 @@ after variants have been added.
 'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts
 page output.
 &$sktemplate: SkinTemplate object
-&$tpl: Template engine object
+&$tpl: QuickTemplate engine object
 
 'SkinTemplatePreventOtherActiveTabs': Use this to prevent showing active tabs.
 $sktemplate: SkinTemplate object
index 1cc74f4..8c46bbb 100644 (file)
@@ -3,4 +3,6 @@
        RewriteEngine On
        RewriteCond %{QUERY_STRING} \.[^\\/:*?\x22<>|%]+(#|\?|$) [nocase]
        RewriteRule . - [forbidden]
+       # Fix for bug T64289
+       Options +FollowSymLinks
 </IfModule>
index 3c80035..f2ab6d5 100644 (file)
@@ -113,7 +113,7 @@ class Block {
                        $this->forcedTargetID = $user; // needed for foreign users
                }
                if ( $by ) { // local user
-                       $this->setBlocker( User::newFromID( $by ) );
+                       $this->setBlocker( User::newFromId( $by ) );
                } else { // foreign user
                        $this->setBlocker( $byText );
                }
@@ -366,7 +366,7 @@ class Block {
        protected function initFromRow( $row ) {
                $this->setTarget( $row->ipb_address );
                if ( $row->ipb_by ) { // local user
-                       $this->setBlocker( User::newFromID( $row->ipb_by ) );
+                       $this->setBlocker( User::newFromId( $row->ipb_by ) );
                } else { // foreign user
                        $this->setBlocker( $row->ipb_by_text );
                }
index 0c00b39..0074cc9 100644 (file)
 /**
  * @deprecated since 1.25
  */
-abstract class CdbReader extends \Cdb\Reader {}
+abstract class CdbReader extends \Cdb\Reader {
+}
 
 /**
  * @deprecated since 1.25
  */
-abstract class CdbWriter extends \Cdb\Writer {}
+abstract class CdbWriter extends \Cdb\Writer {
+}
 
 /**
  * @deprecated since 1.25
  */
-class CdbException extends \Cdb\Exception {}
+class CdbException extends \Cdb\Exception {
+}
index 7b010ea..071ddeb 100644 (file)
@@ -150,6 +150,12 @@ class EditPage {
         */
        const AS_NO_CHANGE_CONTENT_MODEL = 235;
 
+       /**
+        * Status: user tried to create self-redirect (redirect to the same article) and
+        * wpIgnoreSelfRedirect == false
+        */
+       const AS_SELF_REDIRECT = 236;
+
        /**
         * Status: can't parse content
         */
@@ -256,6 +262,12 @@ class EditPage {
        /** @var bool */
        protected $allowBlankArticle = false;
 
+       /** @var bool */
+       protected $selfRedirect = false;
+
+       /** @var bool */
+       protected $allowSelfRedirect = false;
+
        /** @var string */
        public $autoSumm = '';
 
@@ -848,6 +860,7 @@ class EditPage {
                        $this->autoSumm = $request->getText( 'wpAutoSummary' );
 
                        $this->allowBlankArticle = $request->getBool( 'wpIgnoreBlankArticle' );
+                       $this->allowSelfRedirect = $request->getBool( 'wpIgnoreSelfRedirect' );
                } else {
                        # Not a posted form? Start with nothing.
                        wfDebug( __METHOD__ . ": Not a posted form.\n" );
@@ -1334,6 +1347,7 @@ class EditPage {
                        case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
                        case self::AS_END:
                        case self::AS_BLANK_ARTICLE:
+                       case self::AS_SELF_REDIRECT:
                                return true;
 
                        case self::AS_HOOK_ERROR:
@@ -1910,6 +1924,17 @@ class EditPage {
                        $status->value = self::AS_SUCCESS_UPDATE;
                }
 
+               if ( !$this->allowSelfRedirect
+                       && $content->isRedirect()
+                       && $content->getRedirectTarget()->equals( $this->getTitle() )
+               ) {
+                       $this->selfRedirect = true;
+                       $status->fatal( 'selfredirect' );
+                       $status->value = self::AS_SELF_REDIRECT;
+                       wfProfileOut( __METHOD__ );
+                       return $status;
+               }
+
                // Check for length errors again now that the section is merged in
                $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
                if ( $this->kblength > $wgMaxArticleSize ) {
@@ -2458,6 +2483,10 @@ class EditPage {
                        $wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
                }
 
+               if ( $this->selfRedirect ) {
+                       $wgOut->addHTML( Html::hidden( 'wpIgnoreSelfRedirect', true ) );
+               }
+
                if ( $this->hasPresetSummary ) {
                        // If a summary has been preset using &summary= we don't want to prompt for
                        // a different summary. Only prompt for a summary if the summary is blanked.
@@ -2622,6 +2651,10 @@ class EditPage {
                                $wgOut->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
                        }
 
+                       if ( $this->selfRedirect ) {
+                               $wgOut->wrapWikiMsg( "<div id='mw-selfredirect'>\n$1\n</div>", 'selfredirect' );
+                       }
+
                        if ( $this->hookError !== '' ) {
                                $wgOut->addWikiText( $this->hookError );
                        }
@@ -3476,7 +3509,6 @@ HTML
                        }
 
                        $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
-                       $parserOptions->setEditSection( false );
                        $parserOptions->setIsPreview( true );
                        $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
 
@@ -3528,13 +3560,17 @@ HTML
                        # For CSS/JS pages, we should have called the ShowRawCssJs hook here.
                        # But it's now deprecated, so never mind
 
-                       $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
-                       $parserOutput = $content->getParserOutput(
-                               $this->getArticle()->getTitle(),
-                               null,
-                               $parserOptions
+                       $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
+                       $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
+
+                       # Try to stash the edit for the final submission step
+                       # @todo: different date format preferences cause cache misses
+                       ApiStashEdit::stashEditFromPreview(
+                               $this->getArticle(), $content, $pstContent,
+                               $parserOutput, $parserOptions, $parserOptions, wfTimestampNow()
                        );
 
+                       $parserOutput->setEditSectionTokens( false ); // no section edit links
                        $previewHTML = $parserOutput->getText();
                        $this->mParserOutput = $parserOutput;
                        $wgOut->addParserOutputMetadata( $parserOutput );
index fb2d6f4..3294884 100644 (file)
@@ -1033,17 +1033,29 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add a subtitle containing a backlink to a page
+        * Build message object for a subtitle containing a backlink to a page
         *
         * @param Title $title Title to link to
         * @param array $query Array of additional parameters to include in the link
+        * @return Message
+        * @since 1.25
         */
-       public function addBacklinkSubtitle( Title $title, $query = array() ) {
+       public static function buildBacklinkSubtitle( Title $title, $query = array() ) {
                if ( $title->isRedirect() ) {
                        $query['redirect'] = 'no';
                }
-               $this->addSubtitle( $this->msg( 'backlinksubtitle' )
-                       ->rawParams( Linker::link( $title, null, array(), $query ) ) );
+               return wfMessage( 'backlinksubtitle' )
+                       ->rawParams( Linker::link( $title, null, array(), $query ) );
+       }
+
+       /**
+        * Add a subtitle containing a backlink to a page
+        *
+        * @param Title $title Title to link to
+        * @param array $query Array of additional parameters to include in the link
+        */
+       public function addBacklinkSubtitle( Title $title, $query = array() ) {
+               $this->addSubtitle( self::buildBacklinkSubtitle( $title, $query ) );
        }
 
        /**
index a4e64ee..4df6a9e 100644 (file)
@@ -199,19 +199,19 @@ abstract class PrefixSearch {
                                return $this->pullFront( $key, $srchres );
                        }
                        $redirectTargetsToRedirect = $this->redirectTargetsToRedirect( $srchres );
-                       if ( isset( $redirectTargetsToRedirect[ $target ] ) ) {
+                       if ( isset( $redirectTargetsToRedirect[$target] ) ) {
                                // The exact match and something in the results list are both redirects
                                // to the same thing!  In this case we'll pull the returned match to the
                                // top following the same logic above.  Again, it might not be a perfect
                                // choice but it'll do.
-                               return $this->pullFront( $redirectTargetsToRedirect[ $target ], $srchres );
+                               return $this->pullFront( $redirectTargetsToRedirect[$target], $srchres );
                        }
                } else {
                        $redirectTargetsToRedirect = $this->redirectTargetsToRedirect( $srchres );
-                       if ( isset( $redirectTargetsToRedirect[ $string ] ) ) {
+                       if ( isset( $redirectTargetsToRedirect[$string] ) ) {
                                // The exact match is the target of a redirect already in the results list so remove
                                // the redirect from the results list and push the exact match to the front
-                               array_splice( $srchres, $redirectTargetsToRedirect[ $string ], 1 );
+                               array_splice( $srchres, $redirectTargetsToRedirect[$string], 1 );
                                array_unshift( $srchres, $string );
                                return $srchres;
                        }
@@ -242,7 +242,7 @@ abstract class PrefixSearch {
                        if ( !$target ) {
                                continue;
                        }
-                       $result[ $target ] = $key;
+                       $result[$target] = $key;
                }
                return $result;
        }
index 265eae1..fb267bd 100644 (file)
@@ -469,7 +469,7 @@ class Status {
        public function __toString() {
                $status = $this->isOK() ? "OK" : "Error";
                if ( count( $this->errors ) ) {
-                       $errorcount = "collected " . ( count($this->errors) ) . " error(s) on the way";
+                       $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way";
                } else {
                        $errorcount = "no errors detected";
                }
@@ -486,16 +486,16 @@ class Status {
                        $errorcount,
                        $valstr
                );
-               if ( count ($this->errors ) > 0 ) {
+               if ( count$this->errors ) > 0 ) {
                        $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" );
                        $i = 1;
                        $out .= "\n";
                        $out .= $hdr;
-                       foreach( $this->getStatusArray() as $stat ) {
+                       foreach ( $this->getStatusArray() as $stat ) {
                                $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n",
                                        $i,
                                        $stat[0],
-                                       implode(" ", array_slice( $stat, 1 ) )
+                                       implode( " ", array_slice( $stat, 1 ) )
                                );
                                $i += 1;
                        }
index 638da08..0a9b433 100644 (file)
@@ -747,7 +747,7 @@ class Title {
         * @param string $title The DB key form the title
         * @param string $fragment The link fragment (after the "#")
         * @param string $interwiki The interwiki prefix
-        * @param boolean $canoncialNamespace If true, use the canonical name for
+        * @param bool $canoncialNamespace If true, use the canonical name for
         *   $ns instead of the localized version.
         * @return string The prefixed form of the title
         */
index 5348020..3cbb052 100644 (file)
@@ -58,6 +58,12 @@ class User implements IDBAccessObject {
         */
        const MAX_WATCHED_ITEMS_CACHE = 100;
 
+       /**
+        * Exclude user options that are set to their default value.
+        * @since 1.25
+        */
+       const GETOPTIONS_EXCLUDE_DEFAULTS = 1;
+
        /**
         * @var PasswordFactory Lazily loaded factory object for passwords
         */
@@ -2547,9 +2553,12 @@ class User implements IDBAccessObject {
        /**
         * Get all user's options
         *
+        * @param int $flags Bitwise combination of:
+        *   User::GETOPTIONS_EXCLUDE_DEFAULTS  Exclude user options that are set
+        *                                      to the default value. (Since 1.25)
         * @return array
         */
-       public function getOptions() {
+       public function getOptions( $flags = 0 ) {
                global $wgHiddenPrefs;
                $this->loadOptions();
                $options = $this->mOptions;
@@ -2566,6 +2575,10 @@ class User implements IDBAccessObject {
                        }
                }
 
+               if ( $flags & self::GETOPTIONS_EXCLUDE_DEFAULTS ) {
+                       $options = array_diff_assoc( $options, self::getDefaultOptions() );
+               }
+
                return $options;
        }
 
index 159f711..c07ac73 100644 (file)
@@ -368,12 +368,10 @@ class Xml {
        public static function label( $label, $id, $attribs = array() ) {
                $a = array( 'for' => $id );
 
-               # FIXME avoid copy pasting below:
-               if ( isset( $attribs['class'] ) ) {
-                               $a['class'] = $attribs['class'];
-               }
-               if ( isset( $attribs['title'] ) ) {
-                               $a['title'] = $attribs['title'];
+               foreach ( array( 'class', 'title' ) as $attr ) {
+                       if ( isset( $attribs[$attr] ) ) {
+                               $a[$attr] = $attribs[$attr];
+                       }
                }
 
                return self::element( 'label', $a, $label );
index 6481630..d025878 100644 (file)
@@ -144,8 +144,6 @@ class RevertAction extends FormAction {
        }
 
        protected function getDescription() {
-               $this->getOutput()->addBacklinkSubtitle( $this->getTitle() );
-
-               return '';
+               return OutputPage::buildBacklinkSubtitle( $this->getTitle() );
        }
 }
index f9960d8..bbfe7cc 100644 (file)
@@ -284,7 +284,8 @@ abstract class ApiBase extends ContextSource {
        }
 
        /**
-        * Indicates whether this module is "internal" or unstable
+        * Indicates whether this module is "internal"
+        * Internal API modules are not (yet) intended for 3rd party use and may be unstable.
         * @since 1.25
         * @return bool
         */
index 7f4b222..92c951d 100644 (file)
@@ -195,9 +195,9 @@ class ApiEditPage extends ApiBase {
                                        list( $params['undo'], $params['undoafter'] ) =
                                                array( $params['undoafter'], $params['undo'] );
                                }
-                               $undoafterRev = Revision::newFromID( $params['undoafter'] );
+                               $undoafterRev = Revision::newFromId( $params['undoafter'] );
                        }
-                       $undoRev = Revision::newFromID( $params['undo'] );
+                       $undoRev = Revision::newFromId( $params['undo'] );
                        if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) {
                                $this->dieUsageMsg( array( 'nosuchrevid', $params['undo'] ) );
                        }
@@ -253,7 +253,8 @@ class ApiEditPage extends ApiBase {
                        'model' => $contentHandler->getModelID(),
                        'wpEditToken' => $params['token'],
                        'wpIgnoreBlankSummary' => '',
-                       'wpIgnoreBlankArticle' => true
+                       'wpIgnoreBlankArticle' => true,
+                       'wpIgnoreSelfRedirect' => true,
                );
 
                if ( !is_null( $params['summary'] ) ) {
index 4a9e216..3b56dec 100644 (file)
@@ -59,7 +59,7 @@ class ApiOpenSearch extends ApiBase {
        }
 
        public function getCustomPrinter() {
-               switch( $this->getFormat() ) {
+               switch ( $this->getFormat() ) {
                        case 'json':
                                return $this->getMain()->createPrinterByName( 'json' . $this->fm );
 
@@ -124,6 +124,9 @@ class ApiOpenSearch extends ApiBase {
                // Find matching titles as Title objects
                $searcher = new TitlePrefixSearch;
                $titles = $searcher->searchWithVariants( $search, $limit, $namespaces );
+               if ( !$titles ) {
+                       return;
+               }
 
                if ( $resolveRedir ) {
                        // Query for redirects
index 78c33ed..c70629a 100644 (file)
@@ -1213,7 +1213,7 @@ class ApiPageSet extends ApiBase {
         *
         * @param ApiResult|array &$result
         * @param array $path
-        * @return boolean Whether the data fit
+        * @return bool Whether the data fit
         */
        public function populateGeneratorData( &$result, array $path = array() ) {
                if ( $result instanceof ApiResult ) {
@@ -1334,8 +1334,7 @@ class ApiPageSet extends ApiBase {
                if ( !$this->mAllowGenerator ) {
                        unset( $result['generator'] );
                } elseif ( $flags & ApiBase::GET_VALUES_FOR_HELP ) {
-                       $result['generator'][ApiBase::PARAM_TYPE] = $this->getGenerators();
-                       foreach ( $result['generator'][ApiBase::PARAM_TYPE] as $g ) {
+                       foreach ( $this->getGenerators() as $g ) {
                                $result['generator'][ApiBase::PARAM_TYPE][] = $g;
                                $result['generator'][ApiBase::PARAM_VALUE_LINKS][$g] = "Special:ApiHelp/query+$g";
                        }
index 2bf1677..f260385 100644 (file)
@@ -87,7 +87,7 @@ class ApiParse extends ApiBase {
                if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
                        if ( !is_null( $oldid ) ) {
                                // Don't use the parser cache
-                               $rev = Revision::newFromID( $oldid );
+                               $rev = Revision::newFromId( $oldid );
                                if ( !$rev ) {
                                        $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
                                }
index 3684461..779c418 100644 (file)
@@ -38,7 +38,7 @@ class ApiPatrol extends ApiBase {
                $this->requireOnlyOneParameter( $params, 'rcid', 'revid' );
 
                if ( isset( $params['rcid'] ) ) {
-                       $rc = RecentChange::newFromID( $params['rcid'] );
+                       $rc = RecentChange::newFromId( $params['rcid'] );
                        if ( !$rc ) {
                                $this->dieUsageMsg( array( 'nosuchrcid', $params['rcid'] ) );
                        }
index 4d75a20..a658309 100644 (file)
@@ -78,7 +78,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        // DifferenceEngine returns a rather ambiguous empty
                        // string if that's not the case
                        if ( $params['diffto'] != 0 ) {
-                               $difftoRev = Revision::newFromID( $params['diffto'] );
+                               $difftoRev = Revision::newFromId( $params['diffto'] );
                                if ( !$difftoRev ) {
                                        $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
                                }
index c6999df..16a491e 100644 (file)
@@ -206,7 +206,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                $hasInterwikiResults = false;
                $totalhits = null;
                if ( $interwiki && $resultPageSet === null && $matches->hasInterwikiResults() ) {
-                       foreach( $matches->getInterwikiResults() as $matches ) {
+                       foreach ( $matches->getInterwikiResults() as $matches ) {
                                $matches = $matches->getInterwikiResults();
                                $hasInterwikiResults = true;
 
index 00f6814..2f9f37e 100644 (file)
@@ -48,7 +48,8 @@ class ApiStashEdit extends ApiBase {
                        $this->dieUsage( "Unsupported content model/format", 'badmodelformat' );
                }
 
-               $text = trim( $params['text'] ); // needed so the key SHA1's match
+               // Trim and fix newlines so the key SHA1's match (see RequestContext::getText())
+               $text = rtrim( str_replace( "\r\n", "\n", $params['text'] ) );
                $textContent = ContentHandler::makeContent(
                        $text, $title, $params['contentmodel'], $params['contentformat'] );
 
@@ -107,27 +108,23 @@ class ApiStashEdit extends ApiBase {
                        $editInfo = false;
                        $status = 'ratelimited';
                } elseif ( $wgMemc->lock( $key, 0, 30 ) ) {
-                       $contentFormat = $content->getDefaultFormat();
-                       $editInfo = $page->prepareContentForEdit( $content, null, $user, $contentFormat );
-                       $wgMemc->unlock( $key );
+                       $format = $content->getDefaultFormat();
+                       $editInfo = $page->prepareContentForEdit( $content, null, $user, $format, false );
                        $status = 'error'; // default
+                       $unlocker = new ScopedCallback( function() use ( $key ) {
+                               global $wgMemc;
+                               $wgMemc->unlock( $key );
+                       } );
                } else {
                        $editInfo = false;
                        $status = 'busy';
                }
 
                if ( $editInfo && $editInfo->output ) {
-                       $parserOutput = $editInfo->output;
-                       // If an item is renewed, mind the cache TTL determined by config and parser functions
-                       $since = time() - wfTimestamp( TS_UNIX, $parserOutput->getTimestamp() );
-                       $ttl = min( $parserOutput->getCacheExpiry() - $since, 5 * 60 );
-                       if ( $ttl > 0 && !$parserOutput->getFlag( 'vary-revision' ) ) {
-                               // Only store what is actually needed
-                               $stashInfo = (object)array(
-                                       'pstContent' => $editInfo->pstContent,
-                                       'output' => $editInfo->output,
-                                       'timestamp' => $editInfo->timestamp
-                               );
+                       list( $stashInfo, $ttl ) = self::buildStashValue(
+                               $editInfo->pstContent, $editInfo->output, $editInfo->timestamp
+                       );
+                       if ( $stashInfo ) {
                                $ok = $wgMemc->set( $key, $stashInfo, $ttl );
                                if ( $ok ) {
                                        $status = 'stashed';
@@ -146,24 +143,69 @@ class ApiStashEdit extends ApiBase {
        }
 
        /**
-        * Get the temporary prepared edit stash key for a user
+        * Attempt to cache PST content and corresponding parser output in passing
         *
-        * @param Title $title
-        * @param Content $content
-        * @param User $user User to get parser options from
-        * @return string
+        * This method can be called when the output was already generated for other
+        * reasons. Parsing should not be done just to call this method, however.
+        * $pstOpts must be that of the user doing the edit preview. If $pOpts does
+        * not match the options of WikiPage::makeParserOptions( 'canonical' ), this
+        * will do nothing. Provided the values are cacheable, they will be stored
+        * in memcached so that final edit submission might make use of them.
+        *
+        * @param Article|WikiPage $page Page title
+        * @param Content $content Proposed page content
+        * @param Content $pstContent The result of preSaveTransform() on $content
+        * @param ParserOutput $pOut The result of getParserOutput() on $pstContent
+        * @param ParserOptions $pstOpts Options for $pstContent (MUST be for prospective author)
+        * @param ParserOptions $pOpts Options for $pOut
+        * @param string $timestamp TS_MW timestamp of parser output generation
+        * @return bool Success
         */
-       protected static function getStashKey(
-               Title $title, Content $content, User $user
+       public static function stashEditFromPreview(
+               Page $page, Content $content, Content $pstContent, ParserOutput $pOut,
+               ParserOptions $pstOpts, ParserOptions $pOpts, $timestamp
        ) {
-               return wfMemcKey( 'prepared-edit',
-                       md5( $title->getPrefixedDBkey() ), // handle rename races
-                       $content->getModel(),
-                       $content->getDefaultFormat(),
-                       sha1( $content->serialize( $content->getDefaultFormat() ) ),
-                       $user->getId() ?: md5( $user->getName() ), // account for user parser options
-                       $user->getId() ? $user->getTouched() : '-' // handle preference change races
-               );
+               global $wgMemc;
+
+               // getIsPreview() controls parser function behavior that references things
+               // like user/revision that don't exists yet. The user/text should already
+               // be set correctly by callers, just double check the preview flag.
+               if ( !$pOpts->getIsPreview() ) {
+                       return false; // sanity
+               } elseif ( $pOpts->getIsSectionPreview() ) {
+                       return false; // short-circuit (need the full content)
+               }
+
+               // PST parser options are for the user (handles signatures, etc...)
+               $user = $pstOpts->getUser();
+               // Get a key based on the source text, format, and user preferences
+               $key = self::getStashKey( $page->getTitle(), $content, $user );
+
+               // Parser output options must match cannonical options.
+               // Treat some options as matching that are different but don't matter.
+               $canonicalPOpts = $page->makeParserOptions( 'canonical' );
+               $canonicalPOpts->setIsPreview( true ); // force match
+               $canonicalPOpts->setTimestamp( $pOpts->getTimestamp() ); // force match
+               if ( !$pOpts->matches( $canonicalPOpts ) ) {
+                       wfDebugLog( 'StashEdit', "Uncacheable preview output for key '$key' (options)." );
+                       return false;
+               }
+
+               // Build a value to cache with a proper TTL
+               list( $stashInfo, $ttl ) = self::buildStashValue( $pstContent, $pOut, $timestamp );
+               if ( !$stashInfo ) {
+                       wfDebugLog( 'StashEdit', "Uncacheable parser output for key '$key' (rev/TTL)." );
+                       return false;
+               }
+
+               $ok = $wgMemc->set( $key, $stashInfo, $ttl );
+               if ( !$ok ) {
+                       wfDebugLog( 'StashEdit', "Failed to cache preview parser output for key '$key'." );
+               } else {
+                       wfDebugLog( 'StashEdit', "Cached preview output for key '$key'." );
+               }
+
+               return $ok;
        }
 
        /**
@@ -195,12 +237,13 @@ class ApiStashEdit extends ApiBase {
                        if ( $wgMemc->lock( $key, 30, 30 ) ) {
                                $editInfo = $wgMemc->get( $key );
                                $wgMemc->unlock( $key );
-                               $sec = microtime( true ) - $start;
-                               wfDebugLog( 'StashEdit', "Waited $sec seconds on '$key'." );
                        }
+                       $sec = microtime( true ) - $start;
+                       wfDebugLog( 'StashEdit', "Waited $sec seconds on '$key'." );
                }
 
                if ( !is_object( $editInfo ) || !$editInfo->output ) {
+                       wfDebugLog( 'StashEdit', "No cache value for key '$key'." );
                        return false;
                }
 
@@ -248,6 +291,59 @@ class ApiStashEdit extends ApiBase {
                return $editInfo;
        }
 
+       /**
+        * Get the temporary prepared edit stash key for a user
+        *
+        * This key can be used for caching prepared edits provided:
+        *   - a) The $user was used for PST options
+        *   - b) The parser output was made from the PST using cannonical matching options
+        *
+        * @param Title $title
+        * @param Content $content
+        * @param User $user User to get parser options from
+        * @return string
+        */
+       protected static function getStashKey( Title $title, Content $content, User $user ) {
+               $hash = sha1( implode( ':', array(
+                       $content->getModel(),
+                       $content->getDefaultFormat(),
+                       sha1( $content->serialize( $content->getDefaultFormat() ) ),
+                       $user->getId() ?: md5( $user->getName() ), // account for user parser options
+                       $user->getId() ? $user->getTouched() : '-' // handle preference change races
+               ) ) );
+
+               return wfMemcKey( 'prepared-edit', md5( $title->getPrefixedDBkey() ), $hash );
+       }
+
+       /**
+        * Build a value to store in memcached based on the PST content and parser output
+        *
+        * This makes a simple version of WikiPage::prepareContentForEdit() as stash info
+        *
+        * @param Content $pstContent
+        * @param ParserOutput $parserOutput
+        * @param string $timestamp TS_MW
+        * @return array (stash info array, TTL in seconds) or (null, 0)
+        */
+       protected static function buildStashValue(
+               Content $pstContent, ParserOutput $parserOutput, $timestamp
+       ) {
+               // If an item is renewed, mind the cache TTL determined by config and parser functions
+               $since = time() - wfTimestamp( TS_UNIX, $parserOutput->getTimestamp() );
+               $ttl = min( $parserOutput->getCacheExpiry() - $since, 5 * 60 );
+               if ( $ttl > 0 && !$parserOutput->getFlag( 'vary-revision' ) ) {
+                       // Only store what is actually needed
+                       $stashInfo = (object)array(
+                               'pstContent' => $pstContent,
+                               'output'     => $parserOutput,
+                               'timestamp'  => $timestamp
+                       );
+                       return array( $stashInfo, $ttl );
+               }
+
+               return array( null, 0 );
+       }
+
        public function getAllowedParams() {
                return array(
                        'title' => array(
index 8096504..d82b0d8 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Meno25"
+                       "Meno25",
+                       "أحمد المحمودي"
                ]
        },
        "apihelp-main-param-format": "صيغة الخرج.",
@@ -17,5 +18,6 @@
        "apihelp-createaccount-param-name": "اسم المستخدم.",
        "apihelp-delete-description": "حذف صفحة.",
        "apihelp-delete-param-unwatch": "أزل الصفحة من قائمة مراقبتك.",
-       "apihelp-edit-description": "إنشاء وتعديل الصفحات."
+       "apihelp-edit-description": "إنشاء وتعديل الصفحات.",
+       "apihelp-query+prefixsearch-param-offset": "عدد النتائج المراد تخطيها."
 }
index 2d57aa2..4a501c2 100644 (file)
@@ -20,5 +20,8 @@
        "apihelp-block-param-user": "Імя ўдзельніка, IP-адрас або IP-дыяпазон, якія вы хочаце заблякаваць.",
        "apihelp-block-param-expiry": "Час заканчэньня. Можа быць адносным (напрыклад, «5 months» або «2 weeks») ці аблсалютным (напрыклад, «2014-09-18T12:34:56Z»). Калі выстаўлены на «infinite», «indefinite» ці «never», блякаваньне будзе бестэрміновым.",
        "apihelp-block-param-reason": "Прычына блякаваньня.",
-       "apihelp-block-param-anononly": "Заблякаваць толькі ананімных удзельнікаў (напрыклад, забараніць ананімныя праўкі з гэтага IP-адрасу)."
+       "apihelp-block-param-anononly": "Заблякаваць толькі ананімных удзельнікаў (напрыклад, забараніць ананімныя праўкі з гэтага IP-адрасу).",
+       "apihelp-block-param-nocreate": "Забарона стварэньня рахункаў.",
+       "apihelp-block-param-autoblock": "Аўтаматычна блякаваць апошні ўжыты IP-адрас, а таксама ўсе наступныя IP-адрасы, зь якіх будуць спробы ўваходу.",
+       "apihelp-block-param-noemail": "Забараняе ўдзельніку дасылаць лісты электроннай пошты празь вікі (трэба мець права «blockemail»)."
 }
index 157b27b..4550a75 100644 (file)
@@ -11,7 +11,9 @@
        "apihelp-main-param-format": "El formato de la salida.",
        "apihelp-main-param-curtimestamp": "Incluir la marca de tiempo actual en el resultado.",
        "apihelp-block-description": "Bloquear usuario",
+       "apihelp-block-param-user": "El nombre de usuario, dirección IP o intervalo de IP que quieres bloquear.",
        "apihelp-block-param-reason": "Razón para el bloqueo.",
+       "apihelp-block-param-anononly": "Bloquear solo usuarios anónimos (es decir, desactivar ediciones anónimas de esta IP).",
        "apihelp-block-param-nocreate": "Prevenir la creación de cuentas.",
        "apihelp-compare-param-fromtitle": "Primer título para comparar",
        "apihelp-createaccount-description": "Crear una nueva cuenta de usuario.",
        "apihelp-edit-param-minor": "Edición menor.",
        "apihelp-edit-param-notminor": "Edición no menor.",
        "apihelp-edit-param-bot": "Marcar esta edición como de bot.",
+       "apihelp-edit-param-createonly": "No editar la página si ya existe.",
+       "apihelp-edit-param-watch": "Añadir la página a tu lista de seguimiento.",
+       "apihelp-edit-param-unwatch": "Quitar la página de tu lista de seguimiento.",
        "apihelp-edit-example-edit": "Editar una página",
+       "apihelp-emailuser-description": "Enviar un mensaje de correo electrónico a un usuario.",
        "apihelp-expandtemplates-param-title": "Título de la página.",
        "apihelp-expandtemplates-param-text": "Sintaxis wiki que se convertirá.",
        "apihelp-feedcontributions-description": "Devuelve el canal de contribuciones de un usuario.",
        "apihelp-feedcontributions-param-feedformat": "El formato del canal.",
+       "apihelp-feedcontributions-param-year": "A partir del año (y anteriores).",
+       "apihelp-feedcontributions-param-month": "A partir del mes (y anteriores).",
+       "apihelp-feedcontributions-param-deletedonly": "Mostrar solo las contribuciones borradas.",
        "apihelp-feedrecentchanges-param-hideminor": "Ocultar cambios menores.",
+       "apihelp-import-param-summary": "Resumen de importación.",
        "apihelp-login-param-name": "Nombre de usuario.",
        "apihelp-login-param-password": "Contraseña.",
        "apihelp-login-param-domain": "Dominio (opcional).",
        "apihelp-move-description": "Mover una página.",
        "apihelp-opensearch-param-search": "Buscar cadena.",
+       "apihelp-options-example-reset": "Restablecer todas las preferencias",
        "apihelp-patrol-example-rcid": "Patrullar un cambio reciente",
        "apihelp-patrol-example-revid": "Patrullar una revisión",
        "apihelp-protect-example-protect": "Proteger una página",
index 265397f..91b4907 100644 (file)
        "apihelp-opensearch-param-format": "فرمت خروجی.",
        "apihelp-opensearch-example-te": "یافتن صفحه‌هایی که با «ته» آغاز می‌شوند",
        "apihelp-options-example-reset": "بازنشانی همه تنظیمات.",
+       "apihelp-paraminfo-param-helpformat": "ساختار راهنمای رشته‌ها",
        "apihelp-parse-example-page": "تجزیه یک صفحه.",
        "apihelp-parse-example-text": "تجزیه متن ویکی.",
        "apihelp-parse-example-summary": "تجزیه خلاصه.",
        "apihelp-query+allpages-param-filterredir": "صفحه‌هایی که باید فهرست شوند.",
        "apihelp-query+allpages-param-minsize": "محدودکردن به صفحه‌هایی که همراه دست کم این تعداد بایت است.",
        "apihelp-query+allredirects-param-limit": "تعداد آیتم‌ها برای بازگرداندن.",
+       "apihelp-query+blocks-example-simple": "فهرست بسته‌شده‌ها",
        "apihelp-query+categorymembers-description": "فهرست‌کردن همهٔ صفحه‌ها در یک ردهٔ مشخص‌شده.",
+       "apihelp-query+categorymembers-param-sort": "خصوصیت برای مرتب‌سازی",
+       "apihelp-query+categorymembers-param-dir": "جهت مرتب شدن",
        "apihelp-query+categorymembers-param-startsortkey": "جایش از $1starthexsortkey استفاده کنید.",
        "apihelp-query+imageinfo-param-urlheight": "مشابه $1urlwidth.",
        "apihelp-query+info-description": "دریافت اطلاعات سادهٔ صفحه.",
        "apihelp-query+iwbacklinks-param-limit": "تعداد صفحه‌ها برای بازگرداندن.",
        "apihelp-query+linkshere-param-limit": "تعداد برای بازگرداندن.",
        "apihelp-query+logevents-description": "دریافت رویدادها از سیاهه‌ها.",
+       "apihelp-query+prefixsearch-param-search": "جستجوی رشته",
+       "apihelp-query+prefixsearch-param-namespace": "فضاهای نامی برای جستجو",
+       "apihelp-query+prefixsearch-param-limit": "حداکثر تعداد نتایج برای بازگرداندن.",
+       "apihelp-query+prefixsearch-param-offset": "تعداد نتایج برای رها کردن.",
        "apihelp-query+protectedtitles-param-namespace": "فقط عنوان‌ها در این فضاهای نام را فهرست کنید.",
        "apihelp-query+protectedtitles-param-level": "فقط عنوان‌ها در این سطح‌های حفاظت را فهرست کنید.",
        "apihelp-query+protectedtitles-param-limit": "تعداد صفحه‌ها برای بازگرداندن.",
index 8676f43..2274371 100644 (file)
        "apihelp-rollback-example-summary": "Annuler les dernières modifications sur [[Main Page]] par l’utilisateur à l’adresse IP 192.0.2.5 avec le résumé « Annulation de vandalisme », et marquer ces modifications et l’annulation comme « robot »",
        "apihelp-rsd-description": "Exporter un schéma RSD (Découverte Très Simple).",
        "apihelp-rsd-example-simple": "Exporter le schéma RSD",
+       "apihelp-setnotificationtimestamp-description": "Mettre à jour l’horodatage de notification pour les pages suivies.\n\nCela affecte la mise en évidence des pages modifiées dans la liste de suivi et l’historique, et l’envoi de courriel quand la préférence « M’envoyer un courriel quand une page de ma liste de suivi est modifiée » est activée.",
+       "apihelp-setnotificationtimestamp-param-entirewatchlist": "Travailler sur toutes les pages suivies.",
+       "apihelp-setnotificationtimestamp-param-timestamp": "Horodatage auquel dater la notification.",
+       "apihelp-setnotificationtimestamp-param-torevid": "Révision pour laquelle fixer l’horodatage de notification (une page uniquement).",
+       "apihelp-setnotificationtimestamp-param-newerthanrevid": "Révision pour fixer l’horodatage de notification plus récent (une page uniquement).",
+       "apihelp-setnotificationtimestamp-example-all": "Réinitialiser l’état de notification pour toute la liste de suivi",
+       "apihelp-setnotificationtimestamp-example-page": "Réinitialiser l’état de notification pour la « Page principale »",
+       "apihelp-setnotificationtimestamp-example-pagetimestamp": "Fixer l’horodatage de notification pour « Page principale » afin que toutes les modifications depuis le 1 janvier 2012 soient non vues",
+       "apihelp-setnotificationtimestamp-example-allpages": "Réinitialiser l’état de notification sur les pages dans l’espace de noms Utilisateur",
+       "apihelp-tokens-description": "Obtenir les jetons pour les actions modifiant les données.\n\nCe module est obsolète, remplacé par [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
+       "apihelp-tokens-param-type": "Types de jeton à demander.",
+       "apihelp-tokens-example-edit": "Récupérer un jeton de modification (par défaut).",
+       "apihelp-tokens-example-emailmove": "Récupérer un jeton de courriel et un jeton de déplacement.",
+       "apihelp-unblock-description": "Débloquer un utilisateur.",
+       "apihelp-unblock-param-id": "ID du blocage à lever (obtenu via list=blocks). Impossible à utiliser avec $1user.",
+       "apihelp-unblock-param-user": "Nom d’utilisateur, adresse IP ou plage d’adresse IP à débloquer. Impossible à utiliser avec $1id.",
+       "apihelp-unblock-param-reason": "Motif de déblocage.",
+       "apihelp-unblock-example-id": "Lever le blocage d’ID #105",
+       "apihelp-unblock-example-user": "Débloquer l’utilisateur Bob avec le motif « Désolé Bob »",
+       "apihelp-undelete-description": "Restaurer les révisions d’une page supprimée.\n\nUne liste des révisions supprimées (avec les horodatages) peut être récupérée via [[Special:ApiHelp/query+deletedrevs|list=deletedrevs]], et une liste d’IDs de fichier supprimé peut être récupérée via [[Special:ApiHelp/query+filearchive|list=filearchive]].",
+       "apihelp-undelete-param-title": "Titre de la page à restaurer.",
+       "apihelp-undelete-param-reason": "Motif de restauration.",
+       "apihelp-undelete-param-timestamps": "Horodatages des révisions à restaurer. Si $1timestamps et $1fileids sont vides, toutes seront restaurées.",
+       "apihelp-undelete-param-fileids": "IDs des révisions de fichier à restaurer. Si $1timestamps et $1fileids sont vides, toutes seront restaurées.",
+       "apihelp-undelete-param-watchlist": "Ajouter ou supprimer la page de votre liste de suivi sans condition, utiliser les préférences ou ne pas modifier le suivi.",
+       "apihelp-undelete-example-page": "Annuler la suppression de [[Main Page]]",
+       "apihelp-undelete-example-revisions": "Annuler la suppression de deux révisions de [[Main Page]]",
+       "apihelp-upload-description": "Télécharger un fichier, ou obtenir l’état des téléchargements en cours.\n\nPlusieurs méthodes sont disponibles :\n* Télécharger directement le contenu du fichier, en utilisant le paramètre « $1file ».\n* Télécharger le fichier par morceaux, en utilsiant les paramètres « $1filesize », « $1chunk » et « $1offset ».* Pour que le serveur MédiaWiki cherche un fichier depuis une URL, utiliser le paramètre « $1url ».\n* Terminer un téléchargement précédent qui a échoué à cause d’avertissements, en utilisant le paramètre « $1filekey ».\nNoter que le POST HTTP doit être fait comme un téléchargement de fichier (par ex. en utilisant multipart/form-data) en envoyant le « $1file ».",
+       "apihelp-upload-param-filename": "Nom de fichier cible.",
+       "apihelp-upload-param-comment": "Télécharger le commentaire. Utilisé aussi comme texte de la page initiale pour les nouveaux fichiers si « $1text » n’est pas spécifié.",
+       "apihelp-upload-param-text": "Texte de page initiale pour les nouveaux fichiers.",
+       "apihelp-upload-param-watch": "Suivre la page.",
+       "apihelp-upload-param-watchlist": "Ajouter ou supprimer sans condition la page de votre liste de suivi, utiliser les préférences ou ne pas changer le suivi.",
+       "apihelp-upload-param-ignorewarnings": "Ignorer tous les avertissements.",
+       "apihelp-upload-param-file": "Contenu du fichier.",
+       "apihelp-upload-param-url": "URL où chercher le fichier.",
+       "apihelp-upload-param-filekey": "Clé identifiant un téléchargement précédent temporairement mis en attente.",
+       "apihelp-upload-param-sessionkey": "Comme $1filekey, conservé pour des raisons de compatibilité descendante.",
+       "apihelp-upload-param-stash": "Si positionné, le serveur conservera temporairement le fichier au lieu de l’ajouter au dépôt.",
+       "apihelp-upload-param-filesize": "Taille du fichier de tout le téléchargement.",
+       "apihelp-upload-param-offset": "Décalage du bloc en octets.",
+       "apihelp-upload-param-chunk": "Partie du contenu.",
+       "apihelp-upload-param-async": "Faire de façon asynchrone les grosses opérations sur les fichiers quand c’est possible.",
+       "apihelp-upload-param-asyncdownload": "Faire de façon asynchrone la recherche d’une URL.",
+       "apihelp-upload-param-leavemessage": "Si asyncdownload est utilisé, laisser un message sur la page de discussion de l’utilisateur quand c’est terminé.",
+       "apihelp-upload-param-statuskey": "Récupérer l’état de téléchargement pour cette clé de fichier (téléchargé par URL).",
+       "apihelp-upload-param-checkstatus": "Récupérer uniquement l’état de téléchargement pour la clé de fichier donnée.",
+       "apihelp-upload-example-url": "Télécharger depuis une URL",
+       "apihelp-upload-example-filekey": "Terminer un téléchargement qui a échoué à cause d’avertissements",
+       "apihelp-userrights-description": "Modifier l’appartenance d’un utilisateur à un groupe.",
+       "apihelp-userrights-param-user": "Nom d’utilisateur.",
+       "apihelp-userrights-param-userid": "ID de l’utilisateur.",
+       "apihelp-userrights-param-add": "Ajouter l’utilisateur à ces groupes.",
+       "apihelp-userrights-param-remove": "Supprimer l’utilisateur de ces groupes.",
+       "apihelp-userrights-param-reason": "Motif pour la modification.",
+       "apihelp-userrights-example-user": "Ajouter l’utilisateur FooBot au groupe « robot », et le supprimer des groupes « sysop » et « bureaucrate »",
+       "apihelp-userrights-example-userid": "Ajouter l’utilisateur d’ID 123 au groupe « robot », et le supprimer des groupes « sysop » et « bureaucrate »",
+       "apihelp-watch-description": "Ajouter ou supprimer des pages de la liste de suivi de l’utilisateur actuel.",
+       "apihelp-watch-param-title": "La page à (ne plus) suivre. Utiliser plutôt $1titles.",
+       "apihelp-watch-param-unwatch": "Si défini, la page ne sera plus suivie plutôt que suivie.",
+       "apihelp-watch-example-watch": "Suivre la page « Page principale »",
+       "apihelp-watch-example-unwatch": "Ne plus suivre la page « Page principale »",
+       "apihelp-watch-example-generator": "Suivre les quelques premières pages de l’espace de nom principal",
        "apihelp-format-example-generic": "Mettre en forme le résultat de la requête dans le format $1",
        "apihelp-dbg-description": "Extraire les données au format de var_export() de PHP.",
        "apihelp-dbgfm-description": "Extraire les données au format de var_export() de PHP (affiché proprement en HTML).",
        "apihelp-yamlfm-description": "Extraire les données YAML (affiché proprement en HTML).",
        "api-format-title": "Résultat de l’API de MédiaWiki",
        "api-format-prettyprint-header": "Vous regardez la représentation HTML du format $1. HTML est utile pour le débogage, mais inapproprié pour être utilisé dans une application.\n\nSpécifiez le paramètre format pour modifier le format de sortie. Pour voir la représentation non HTML du format $1, mettez format=$2.\n\nVoyez la [https://www.mediawiki.org/wiki/API documentation complète], ou l’ [[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
+       "api-orm-param-props": "Champs à rechercher.",
+       "api-orm-param-limit": "Nombre maximal de lignes à renvoyer.",
+       "api-pageset-param-titles": "Une liste des titres sur lesquels travailler.",
+       "api-pageset-param-pageids": "Une liste des IDs de page sur lesquelles travailler.",
+       "api-pageset-param-revids": "Une liste des IDs de révision sur lesquelles travailler.",
+       "api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de recherche spécifié.\n\n'''NOTE :''' les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
+       "api-pageset-param-redirects-generator": "Résoudre automatiquement les redirections dans $1titles, $1pageids et $1revids, et dans les pages renvoyées par $1generator.",
+       "api-pageset-param-redirects-nogenerator": "Résoudre automatiquement les redirections dans $1titles, $1pageids et $1revids.",
+       "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki supporte la conversion en variantes. Les langues qui supportent la conversion en variante incluent $1.",
        "api-help-title": "Aide de l’API de MediaWiki",
        "api-help-lead": "Ceci est une page d’aide de l’API de MédiaWiki générée automatiquement.\n\nDocumentation et exemples : https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "Module principal",
        "api-help-param-default": "Par défaut : $1",
        "api-help-param-default-empty": "Par défaut : <span class=\"apihelp-empty\">(vide)</span>",
        "api-help-param-token": "Un jeton « $1 » récupéré par [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
+       "api-help-param-token-webui": "Pour rester compatible, le jeton utilisé dans l’IHM web est aussi accepté.",
+       "api-help-param-disabled-in-miser-mode": "Désactivé à cause du [https://www.mediawiki.org/wiki/Manual:$wgMiserMode mode minimal].",
+       "api-help-param-limited-in-miser-mode": "'''NOTE :''' Du fait du [https://www.mediawiki.org/wiki/Manual:$wgMiserMode mode minimal], utiliser cela peut aboutir à moins de résultats que « $1limit » renvoyés avant de continuer ; dans les cas extrêmes, zéro résultats peuvent être renvoyés.",
+       "api-help-param-direction": "Dans quelle direction énumérer :\n;newer:Lister les plus anciens en premier. Note : $1start doit être avant $1end.\n;older:Lister les nouveaux en premier (par défaut). Note : $1start doit être postérieur à $1end.",
+       "api-help-param-continue": "Quand plus de résultats sont disponibles, utiliser cela pour continuer.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(aucune description)</span>",
        "api-help-examples": "{{PLURAL:$1|Exemple|Exemples}} :",
        "api-help-permissions": "{{PLURAL:$1|Droit|Droits}} :",
index 6b10584..ec494f4 100644 (file)
@@ -12,6 +12,7 @@
        "apihelp-help-example-main": "Bantuan untuk modul utama",
        "apihelp-help-example-recursive": "Segala bantuan dalam satu halaman",
        "apihelp-help-example-help": "Bantuan untuk modul bantuan",
+       "apihelp-query+prefixsearch-param-offset": "Bilangan hasil untuk dilangkau.",
        "apihelp-userrights-param-userid": "ID pengguna.",
        "apihelp-dbgfm-description": "Data output dalam format var_export() PHP (''pretty-print'' dalam HTML).",
        "apihelp-dump-description": "Output data dalam format var_dump() PHP.",
diff --git a/includes/api/i18n/te.json b/includes/api/i18n/te.json
new file mode 100644 (file)
index 0000000..8678f2a
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Veeven"
+               ]
+       },
+       "apihelp-feedrecentchanges-example-simple": "ఇటీవలి మార్పులను చూడండి"
+}
index b0c3e4f..3ce22d4 100644 (file)
        "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "不能与$3user一起使用。",
        "apihelp-query+alldeletedrevisions-param-from": "从此标题开始列出。",
        "apihelp-query+alldeletedrevisions-param-to": "列出至此标题为止。",
+       "apihelp-query+alldeletedrevisions-param-user": "只列出此用户做出的修订。",
+       "apihelp-query+alldeletedrevisions-param-excludeuser": "不要列出此用户做出的修订。",
        "apihelp-query+alldeletedrevisions-param-namespace": "只列出此名字空间的页面。",
        "apihelp-query+alldeletedrevisions-param-miser-user-namespace": "'''注意:'''由于[https://www.mediawiki.org/wiki/Manual:$wgMiserMode miser模式],同时使用$1user和$1namespace将导致继续前返回少于“$1limit”个结果,在极端条件下可能不返回任何结果。",
        "apihelp-query+alldeletedrevisions-example-user": "列出由User:Example作出的最近50次已删除贡献",
        "apihelp-query+alldeletedrevisions-example-ns-main": "列出最近50次已删除的主名字空间修订",
+       "apihelp-query+allfileusages-param-limit": "要返回的总计项目。",
        "apihelp-query+allfileusages-param-dir": "罗列所采用的方向。",
        "apihelp-query+allfileusages-example-unique": "列出唯一性的文件标题",
        "apihelp-query+allfileusages-example-unique-generator": "获取所有文件标题,并标记出缺失者",
        "apihelp-query+categoryinfo-example-simple": "获取有关[[:Category:Foo]]和[[:Category:Bar]]的信息",
        "apihelp-query+categorymembers-description": "在指定的分类中列出所有页面。",
        "apihelp-query+categorymembers-param-sort": "要作为排序方式的属性。",
+       "apihelp-query+categorymembers-param-dir": "排序的方向。",
+       "apihelp-query+categorymembers-param-start": "开始列举的时间戳。不能与$1sort=timestamp一起使用。",
+       "apihelp-query+categorymembers-param-end": "列举的结尾时间戳。不能与$1sort=timestamp一起使用。",
        "apihelp-query+categorymembers-param-startsortkey": "请改用$1starthexsortkey。",
        "apihelp-query+categorymembers-param-endsortkey": "请改用$1endhexsortkey。",
        "apihelp-query+categorymembers-example-simple": "获取[[:Category:Physics]]中的前10个页面。",
index 7d5450e..c79d008 100644 (file)
@@ -23,6 +23,7 @@
 use Cdb\Exception as CdbException;
 use Cdb\Reader as CdbReader;
 use Cdb\Writer as CdbWriter;
+
 /**
  * Class for caching the contents of localisation files, Messages*.php
  * and *.i18n.php.
index 55da52c..6d26a2d 100644 (file)
@@ -40,7 +40,9 @@ class ResourceFileCache extends FileCacheBase {
        public static function newFromContext( ResourceLoaderContext $context ) {
                $cache = new self();
 
-               if ( $context->getOnly() === 'styles' ) {
+               if ( $context->getImage() ) {
+                       $cache->mType = 'image';
+               } elseif ( $context->getOnly() === 'styles' ) {
                        $cache->mType = 'css';
                } else {
                        $cache->mType = 'js';
@@ -69,7 +71,8 @@ class ResourceFileCache extends FileCacheBase {
                // Get all query values
                $queryVals = $context->getRequest()->getValues();
                foreach ( $queryVals as $query => $val ) {
-                       if ( $query === 'modules' || $query === 'version' || $query === '*' ) {
+                       if ( in_array( $query, array( 'modules', 'image', 'variant', 'version', '*' ) ) ) {
+                               // Use file cache regardless of the value of this parameter
                                continue; // note: &* added as IE fix
                        } elseif ( $query === 'skin' && $val === $wgDefaultSkin ) {
                                continue;
@@ -79,6 +82,8 @@ class ResourceFileCache extends FileCacheBase {
                                continue;
                        } elseif ( $query === 'debug' && $val === 'false' ) {
                                continue;
+                       } elseif ( $query === 'format' && $val === 'rasterized' ) {
+                               continue;
                        }
 
                        return false;
index 4eed926..c9602cc 100644 (file)
@@ -73,7 +73,7 @@ class OldChangesList extends ChangesList {
        /**
         * @param RecentChange $rc
         * @param string[] &$classes
-        * @param boolean $watched
+        * @param bool $watched
         *
         * @return string
         */
index d187c54..bf0c644 100644 (file)
@@ -257,7 +257,7 @@ class RecentChange {
        public function getPerformer() {
                if ( $this->mPerformer === false ) {
                        if ( $this->mAttribs['rc_user'] ) {
-                               $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
+                               $this->mPerformer = User::newFromId( $this->mAttribs['rc_user'] );
                        } else {
                                $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
                        }
index 6c53f6a..eb9d9b3 100644 (file)
@@ -46,10 +46,6 @@ abstract class DatabaseBase implements IDatabase {
        /** Maximum time to wait before retry */
        const DEADLOCK_DELAY_MAX = 1500000;
 
-# ------------------------------------------------------------------------------
-# Variables
-# ------------------------------------------------------------------------------
-
        protected $mLastQuery = '';
        protected $mDoneWrites = false;
        protected $mPHPError = false;
@@ -157,11 +153,6 @@ abstract class DatabaseBase implements IDatabase {
         */
        protected $allViews = null;
 
-# ------------------------------------------------------------------------------
-# Accessors
-# ------------------------------------------------------------------------------
-       # These optionally set a variable and return the previous state
-
        /**
         * A string describing the current software version, and possibly
         * other details in a user-friendly way. Will be listed on Special:Version, etc.
@@ -588,9 +579,167 @@ abstract class DatabaseBase implements IDatabase {
                return $this->getSqlFilePath( 'update-keys.sql' );
        }
 
-# ------------------------------------------------------------------------------
-# Other functions
-# ------------------------------------------------------------------------------
+       /**
+        * Get the type of the DBMS, as it appears in $wgDBtype.
+        *
+        * @return string
+        */
+       abstract function getType();
+
+       /**
+        * Open a connection to the database. Usually aborts on failure
+        *
+        * @param string $server Database server host
+        * @param string $user Database user name
+        * @param string $password Database user password
+        * @param string $dbName Database name
+        * @return bool
+        * @throws DBConnectionError
+        */
+       abstract function open( $server, $user, $password, $dbName );
+
+       /**
+        * Fetch the next row from the given result object, in object form.
+        * Fields can be retrieved with $row->fieldname, with fields acting like
+        * member variables.
+        * If no more rows are available, false is returned.
+        *
+        * @param ResultWrapper|stdClass $res Object as returned from DatabaseBase::query(), etc.
+        * @return stdClass|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       abstract function fetchObject( $res );
+
+       /**
+        * Fetch the next row from the given result object, in associative array
+        * form. Fields are retrieved with $row['fieldname'].
+        * If no more rows are available, false is returned.
+        *
+        * @param ResultWrapper $res Result object as returned from DatabaseBase::query(), etc.
+        * @return array|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       abstract function fetchRow( $res );
+
+       /**
+        * Get the number of rows in a result object
+        *
+        * @param mixed $res A SQL result
+        * @return int
+        */
+       abstract function numRows( $res );
+
+       /**
+        * Get the number of fields in a result object
+        * @see http://www.php.net/mysql_num_fields
+        *
+        * @param mixed $res A SQL result
+        * @return int
+        */
+       abstract function numFields( $res );
+
+       /**
+        * Get a field name in a result object
+        * @see http://www.php.net/mysql_field_name
+        *
+        * @param mixed $res A SQL result
+        * @param int $n
+        * @return string
+        */
+       abstract function fieldName( $res, $n );
+
+       /**
+        * Get the inserted value of an auto-increment row
+        *
+        * The value inserted should be fetched from nextSequenceValue()
+        *
+        * Example:
+        * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
+        * $dbw->insert( 'page', array( 'page_id' => $id ) );
+        * $id = $dbw->insertId();
+        *
+        * @return int
+        */
+       abstract function insertId();
+
+       /**
+        * Change the position of the cursor in a result object
+        * @see http://www.php.net/mysql_data_seek
+        *
+        * @param mixed $res A SQL result
+        * @param int $row
+        */
+       abstract function dataSeek( $res, $row );
+
+       /**
+        * Get the last error number
+        * @see http://www.php.net/mysql_errno
+        *
+        * @return int
+        */
+       abstract function lastErrno();
+
+       /**
+        * Get a description of the last error
+        * @see http://www.php.net/mysql_error
+        *
+        * @return string
+        */
+       abstract function lastError();
+
+       /**
+        * mysql_fetch_field() wrapper
+        * Returns false if the field doesn't exist
+        *
+        * @param string $table Table name
+        * @param string $field Field name
+        *
+        * @return Field
+        */
+       abstract function fieldInfo( $table, $field );
+
+       /**
+        * Get information about an index into an object
+        * @param string $table Table name
+        * @param string $index Index name
+        * @param string $fname Calling function name
+        * @return mixed Database-specific index description class or false if the index does not exist
+        */
+       abstract function indexInfo( $table, $index, $fname = __METHOD__ );
+
+       /**
+        * Get the number of rows affected by the last write query
+        * @see http://www.php.net/mysql_affected_rows
+        *
+        * @return int
+        */
+       abstract function affectedRows();
+
+       /**
+        * Wrapper for addslashes()
+        *
+        * @param string $s String to be slashed.
+        * @return string Slashed string.
+        */
+       abstract function strencode( $s );
+
+       /**
+        * Returns a wikitext link to the DB's website, e.g.,
+        *   return "[http://www.mysql.com/ MySQL]";
+        * Should at least contain plain text, if for some reason
+        * your database has no website.
+        *
+        * @return string Wikitext of a link to the server software's web site
+        */
+       abstract function getSoftwareLink();
+
+       /**
+        * A string describing the current software version, like from
+        * mysql_get_server_info().
+        *
+        * @return string Version information from the database server.
+        */
+       abstract function getServerVersion();
 
        /**
         * Constructor.
@@ -1003,7 +1152,7 @@ abstract class DatabaseBase implements IDatabase {
                if ( $queryProf != '' ) {
                        $queryStartTime = microtime( true );
                        $queryProfile = new ScopedCallback(
-                               function() use ( $queryStartTime, $queryProf, $isMaster ) {
+                               function () use ( $queryStartTime, $queryProf, $isMaster ) {
                                        $trxProfiler = Profiler::instance()->getTransactionProfiler();
                                        $trxProfiler->recordQueryCompletion( $queryProf, $queryStartTime, $isMaster );
                                }
index e049a5d..0e89a5b 100644 (file)
@@ -109,7 +109,7 @@ abstract class LBFactory {
         * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
         * @param array $conf
         */
-       public abstract function __construct( array $conf );
+       abstract public function __construct( array $conf );
 
        /**
         * Create a new load balancer object. The resulting object will be untracked,
@@ -118,7 +118,7 @@ abstract class LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       public abstract function newMainLB( $wiki = false );
+       abstract public function newMainLB( $wiki = false );
 
        /**
         * Get a cached (tracked) load balancer object.
@@ -126,7 +126,7 @@ abstract class LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       public abstract function getMainLB( $wiki = false );
+       abstract public function getMainLB( $wiki = false );
 
        /**
         * Create a new load balancer for external storage. The resulting object will be
@@ -137,7 +137,7 @@ abstract class LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       protected abstract function newExternalLB( $cluster, $wiki = false );
+       abstract protected function newExternalLB( $cluster, $wiki = false );
 
        /**
         * Get a cached (tracked) load balancer for external storage
@@ -146,7 +146,7 @@ abstract class LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       public abstract function &getExternalLB( $cluster, $wiki = false );
+       abstract public function &getExternalLB( $cluster, $wiki = false );
 
        /**
         * Execute a function for each tracked load balancer
@@ -156,7 +156,7 @@ abstract class LBFactory {
         * @param callable $callback
         * @param array $params
         */
-       public abstract function forEachLB( $callback, array $params = array() );
+       abstract public function forEachLB( $callback, array $params = array() );
 
        /**
         * Prepare all tracked load balancers for shutdown
@@ -172,7 +172,7 @@ abstract class LBFactory {
         * @param array $args
         */
        private function forEachLBCallMethod( $methodName, array $args = array() ) {
-               $this->forEachLB( function( LoadBalancer $loadBalancer, $methodName, array $args ) {
+               $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
                        call_user_func_array( array( $loadBalancer, $methodName ), $args );
                }, array( $methodName, $args ) );
        }
index 5d084af..8808c20 100644 (file)
@@ -80,7 +80,7 @@ class SearchUpdate implements DeferrableUpdate {
 
                wfProfileIn( __METHOD__ );
 
-               $page = WikiPage::newFromId( $this->id, WikiPage::READ_LATEST );
+               $page = WikiPage::newFromID( $this->id, WikiPage::READ_LATEST );
 
                foreach ( SearchEngine::getSearchTypes() as $type ) {
                        $search = SearchEngine::create( $type );
index 9db04cb..83801b6 100644 (file)
  */
 class MWExceptionHandler {
 
+       protected static $reservedMemory;
+       protected static $fatalErrorTypes = array(
+               E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
+               /* HHVM's FATAL_ERROR level */ 16777217,
+       );
+
        /**
         * Install handlers with PHP.
         */
        public static function installHandler() {
                set_exception_handler( array( 'MWExceptionHandler', 'handleException' ) );
                set_error_handler( array( 'MWExceptionHandler', 'handleError' ) );
+
+               // Reserve 16k of memory so we can report OOM fatals
+               self::$reservedMemory = str_repeat( ' ', 16384 );
+               register_shutdown_function(
+                       array( 'MWExceptionHandler', 'handleFatalError' )
+               );
        }
 
        /**
@@ -194,6 +206,9 @@ class MWExceptionHandler {
                        case E_USER_DEPRECATED:
                                $levelName = 'Deprecated';
                                break;
+                       case /* HHVM's FATAL_ERROR */ 16777217:
+                               $levelName = 'Fatal';
+                               break;
                        default:
                                $levelName = 'Unknown error';
                                break;
@@ -207,6 +222,44 @@ class MWExceptionHandler {
                return false;
        }
 
+
+       /**
+        * Look for a fatal error as the cause of the request termination and log
+        * as an exception.
+        *
+        * Special handling is included for missing class errors as they may
+        * indicate that the user needs to install 3rd-party libraries via
+        * Composer or other means.
+        *
+        * @since 1.25
+        */
+       public static function handleFatalError() {
+               self::$reservedMemory = null;
+               $lastError = error_get_last();
+
+               if ( $lastError &&
+                       isset( $lastError['type'] ) &&
+                       in_array( $lastError['type'], self::$fatalErrorTypes )
+               ) {
+                       $msg = "Fatal Error: {$lastError['message']}";
+                       // HHVM: Class undefined: foo
+                       // PHP5: Class 'foo' not found
+                       if ( preg_match( "/Class (undefined: \w+|'\w+' not found)/",
+                               $lastError['message']
+                       ) ) {
+                               $msg = <<<TXT
+{$msg}
+
+MediaWiki or an installed extension requires this class but it is not embedded directly in MediaWiki's git repository and must be installed separately by the end user.
+
+Please see <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a> for help on installing the required components.
+TXT;
+                       }
+                       $e = new ErrorException( $msg, 0, $lastError['type'] );
+                       self::logError( $e );
+               }
+       }
+
        /**
         * Generate a string representation of an exception's stack trace
         *
index 9aa4ca8..c00fb81 100644 (file)
@@ -454,10 +454,10 @@ class FSFileBackend extends FileBackendStore {
                        wfDebugLog( 'FSFileBackend', __METHOD__ . ": cannot create directory $dir" );
                        $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
                } elseif ( !is_writable( $dir ) ) {
-                       wfDebugLog( 'FSFileBackend',  __METHOD__ . ": directory $dir is read-only" );
+                       wfDebugLog( 'FSFileBackend', __METHOD__ . ": directory $dir is read-only" );
                        $status->fatal( 'directoryreadonlyerror', $params['dir'] );
                } elseif ( !is_readable( $dir ) ) {
-                       wfDebugLog( 'FSFileBackend',  __METHOD__ . ": directory $dir is not readable" );
+                       wfDebugLog( 'FSFileBackend', __METHOD__ . ": directory $dir is not readable" );
                        $status->fatal( 'directorynotreadableerror', $params['dir'] );
                }
                $this->untrapWarnings();
index c82be50..ae6b659 100644 (file)
@@ -492,7 +492,7 @@ abstract class File {
                sort( $sortedBuckets );
 
                foreach ( $sortedBuckets as $bucket ) {
-                       if ( $bucket > $imageWidth ) {
+                       if ( $bucket >= $imageWidth ) {
                                return false;
                        }
 
index a5540db..c34d09e 100644 (file)
@@ -1073,7 +1073,7 @@ abstract class DatabaseUpdater {
                        $out = $wgProfiler['output'];
                        if ( $out === 'db' ) {
                                $profileToDb = true;
-                       } elseif( is_array( $out ) && in_array( 'db', $out ) ) {
+                       } elseif ( is_array( $out ) && in_array( 'db', $out ) ) {
                                $profileToDb = true;
                        }
                }
index 8ff3d2f..44ca7d3 100644 (file)
@@ -298,7 +298,7 @@ class WebInstallerOutput {
        </div>
 <?php
        $message = wfMessage( 'config-sidebar' )->plain();
-       foreach( explode( '----', $message ) as $section ) {
+       foreach ( explode( '----', $message ) as $section ) {
                echo '<div class="portal"><div class="body">';
                echo $this->parent->parse( $section, true );
                echo '</div></div>';
index 32e4c39..1301e92 100644 (file)
@@ -69,6 +69,7 @@
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] е инсталиран",
        "config-no-cache": "'''Предупреждение:''' Не бяха открити [http://www.php.net/apc APC] [http://xcache.lighttpd.net/ XCache] или [http://www.iis.net/download/WinCacheForPhp WinCache].\nОбектното кеширане не е включено.",
        "config-diff3-bad": "GNU diff3 не беше намерен.",
+       "config-git-bad": "Не е намерен софтуер за контрол на версиите Git.",
        "config-imagemagick": "Открит е ImageMagick: <code>$1</code>.\nПреоразмеряването на картинки ще бъде включено ако качването на файлове бъде разрешено.",
        "config-gd": "Открита е вградена графичната библиотека GD.\nАко качването на файлове бъде включено, ще бъде включена възможността за преоразмеряване на картинки.",
        "config-no-scaling": "Не са открити библиотеките GD или ImageMagick.\nПреоразмеряването на картинки ще бъде изключено.",
        "config-extensions": "Разширения",
        "config-extensions-help": "Разширенията от списъка по-горе бяха открити в директорията <code>./extensions</code>.\n\nВъзможно е те да изискват допълнително конфигуриране, но сега могат да бъдат включени.",
        "config-skins": "Облици",
+       "config-skins-use-as-default": "Използване на този облик по подразбиране",
        "config-install-alreadydone": "'''Предупреждение:''' Изглежда вече сте инсталирали МедияУики и се опитвате да го инсталирате отново.\nПродължете към следващата страница.",
        "config-install-begin": "Инсталацията на МедияУики ще започне след натискане на бутона „{{int:config-continue}}“.\nВ случай, че е необходимо да се направят промени, използва се бутона „{{int:config-back}}“.",
        "config-install-step-done": "готово",
index e0c985b..2b8a08e 100644 (file)
        "config-mssql-sqlauth": "Autenticació de l’SQL Server",
        "config-mssql-windowsauth": "Autenticació del Windows",
        "config-site-name": "Nom del wiki:",
+       "config-site-name-help": "Això apareixerà en la barra de títol del navegador i en altres llocs diferents.",
        "config-site-name-blank": "Introduïu un nom per al lloc.",
        "config-project-namespace": "Espai de noms del projecte:",
        "config-ns-generic": "Projecte",
        "config-admin-password-blank": "Introduïu una contrasenya per al compte d'administrador.",
        "config-admin-password-mismatch": "Les dues contrasenyes que heu introduït no coincideixen.",
        "config-admin-email": "Adreça electrònica:",
+       "config-admin-error-user": "S'ha produït un error intern en crear un administrador amb el nom «<nowiki>$1</nowiki>».",
+       "config-admin-error-password": "S'ha produït un error intern en definir una contrasenya per a l'administrador «<nowiki>$1</nowiki>»: <pre>$2</pre>",
        "config-admin-error-bademail": "Heu introduït una adreça electrònica no vàlida.",
        "config-almost-done": "Gairebé ja heu acabat!\nPodeu ometre el que queda de la configuració i procedir amb la instal·lació del wiki.",
        "config-optional-continue": "Fes-me més preguntes.",
        "config-cache-help": "L'encauament d'objectes s'utilitza per a millorar la rapidesa del MediaWiki afegint a la memòria cau les dades que s'utilitzen de forma freqüent. És recomanable que els llocs web mitjans o grans ho habilitin. També els llocs web petits en veuran els beneficis.",
        "config-cache-none": "Sense encauament (no se suprimeix cap funcionalitat, però la velocitat pot veure's afectada en els llocs wiki més grans)",
        "config-memcached-servers": "Servidors de Memcache:",
+       "config-memcache-badip": "Heu introduït una adreça IP no vàlida per al Memcached: $1.",
+       "config-memcache-noport": "No heu especificat un port per utilitzar el servidor Memcached: $1.\nSi no coneixeu el port, per defecte és 11211.",
        "config-extensions": "Extensions",
        "config-skins": "Aparences",
+       "config-skins-use-as-default": "Utilitza aquest tema per defecte",
+       "config-skins-missing": "No s'ha trobat cap tema; MediaWiki utilitzarà el tema per defecte fins que hi instal·leu alguns adequats.",
+       "config-skins-must-enable-some": "Heu de triar com a mínim un tema per habilitar.",
+       "config-skins-must-enable-default": "Cal habilitar el tema triat per defecte.",
        "config-install-step-done": "fet",
        "config-install-step-failed": "ha fallat",
        "config-install-extensions": "S'estan incloent les extensions",
        "config-install-pg-schema-not-exist": "No existeix un esquema PostgreSQL.",
        "config-install-pg-schema-failed": "La creació de les taules ha fallat.\nAssegureu-vos que l'usuari «$1» pot escriure a l'esquema «$2».",
        "config-install-pg-commit": "S'estan trametent els canvis",
+       "config-pg-no-create-privs": "El compte que heu especificat per a la instal·lació no té suficients permisos per crear un compte.",
        "config-install-user": "S'està creant l'usuari de la base de dades",
        "config-install-user-alreadyexists": "L'usuari «$1» ja existeix",
        "config-install-user-create-failed": "La creació de l'usuari «$1» ha fallat: $2",
        "config-install-interwiki-exists": "'''Avís:''' La taula d'interwiki sembla que ja té entrades. S'omet la llista per defecte.",
        "config-install-stats": "S'estan inicialitzant les estadístiques",
        "config-install-keys": "S'estan generant les claus secretes",
+       "config-install-updates": "Evita que s'executin actualitzacions no necessàries",
        "config-install-sysop": "S'està creant un compte d'usuari d'administrador",
        "config-install-subscribe-fail": "No s'ha pogut subscriure a mediawiki-announce: $1",
        "config-install-subscribe-notpossible": "El cURL no està instal·lat i <code>allow_url_fopen</code> no està disponible.",
index f88ab91..0a118f0 100644 (file)
@@ -14,7 +14,8 @@
                        "Lliehu",
                        "Syreeni",
                        "Stryn",
-                       "SMAUG"
+                       "SMAUG",
+                       "SuperPete"
                ]
        },
        "config-desc": "MediaWiki-asennin",
        "config-apc": "[http://www.php.net/apc APC] on asennettu.",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] on asennettu",
        "config-diff3-bad": "GNU diff3:a ei löytynyt.",
+       "config-git": "Löydetty Git versionhallintaohjelmisto: <code>$1</code>",
+       "config-git-bad": "Git versionhallintaohjelmistoa ei löydy.",
        "config-imagemagick": "Löydettiin ImageMagick: <code>$1</code>.\nKuvien esikatselukuvat otetaan samalla käyttöön jos otetaan tiedostojen tallennus.",
+       "config-gd": "Löydettiin sisäänrakennettu GD-grafiikkakirjasto.\nKuvista luodaan esikatseluversiot automaattisesti, jos otat käyttöön tiedostojen lähettämisen.",
+       "config-no-scaling": "GD-kirjastoa tai ImageMagick-ohjelmaa ei löydy. \nKuvista ei luoda esikatseluversioita.",
+       "config-no-uri": "Virhe: Tämänhetkistä URIa ei tunnisteta. Asennus keskeytetään.",
+       "config-no-cli-uri": "<strong>Varoitus:</strong> <code>--scriptpath</code> määrittämättä, käytetään oletusta: <code>$1</code>",
        "config-using-server": "Palvelimen nimenä käytetään \"<nowiki>$1</nowiki>\".",
        "config-using-uri": "Palvelinen URL-osoitteena käytetään \"<nowiki>$1$2</nowiki>\".",
+       "config-uploads-not-safe": "<strong>Varoitus:</strong> Tiedostojen lähetyshakemistoa <code>$1</code> ei ole suojattu haitalliselta koodilta. MediaWiki tarkistaa kaikki lähetetyt tiedostot, mutta suosittelemme toimimaan ohjeen [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security close this security vulnerability] mukaan ennen kuin tiedostojen lähetys otetaan käyttöön.",
+       "config-no-cli-uploads-check": "<strong>Varoitus:</strong> Tiedostojen lähetyshakemistoa (<code>$1</code>) ei ole tarkistettu haavoittuvuuksien varalta komentoriviasennuksen aikana.",
+       "config-brokenlibxml": "Järjestelmässäsi on käytössä PHP:n ja libxml2:n versioyhdistelmä, joka ei toimi kunnolla ja voi aiheuttaa tiedon vahingoittumista MediaWikissä ja muissa web-sovelluksissa.\nPäivitä libxml2 versioon 2.7.3 tai uudempaan ([https://bugs.php.net/bug.php?id=45996 bug filed with PHP]).\nAsennus keskeytetty.",
+       "config-suhosin-max-value-length": "Suhosin on asennettu ja se rajoittaa GET-parametrin <code>length</code> $1 tavuun.\nMediaWikin ResourceLoader-komponentti pystyy toimimaan tämän kanssa, mutta ohjelmiston suorituskyky heikkenee.\nMikäli mahdollista, aseta muuttuja <code>suhosin.get.max_value_length</code> arvoon 1024 (tai suurempaan) tiedostossa <code>php.ini</code> ja aseta myös <code>$wgResourceLoaderMaxQueryLength</code> samaksi arvoksi tiedostossa <code>LocalSettings.php</code>.",
        "config-db-type": "Tietokannan tyyppi",
        "config-db-host": "Tietokantapalvelin",
+       "config-db-host-help": "Jos tietokantapalvelimesi sijaitsee eri palvelimella, syötä palvelimen nimi tai ip-osoite tähän.\n\nJos käytössäsi on ulkoinen palveluntarjoaja, pitäisi palvelimen nimen löytyä yrityksen ohjesivuilta.\n\nJos asennat MediaWikiä Windows-palvelimelle ja käytät MySQL:ää ei palvelimen nimi \"localhost\" välttämättä toimi. Tässä tapauksessa koita käyttää osoitetta 127.0.0.1.\n\nJos käytät PostgreSQL:ää jätä tämä kenttä tyhjäksi.",
+       "config-db-host-oracle": "Tietokannan TNS:",
        "config-db-wiki-settings": "Identifioi tämä wiki",
        "config-db-name": "Tietokannan nimi",
+       "config-db-name-help": "Valitse wikiäsi kuvaava nimi.\nNimessä ei saa olla välilyöntejä.\n\nMikäli et pysty itse hallitsemaan tietokantojasi, pyydä palveluntarjoajaasi luomaan tietokanta tai tee se palveluntarjoajasi hallintapaneelissa.",
+       "config-db-name-oracle": "Tietokannan rakenne:",
        "config-db-install-account": "Asennuksessa käytettävä käyttäjätili",
        "config-db-username": "Tietokannan käyttäjätunnus",
        "config-db-password": "Tietokannan salasana",
        "config-db-password-empty": "Syötä salasana uudelle tietokannan käyttäjälle: $1.\nVaikka käyttäjä voidaan luoda ilman salasanaa, se ei ole turvallista.",
+       "config-db-username-empty": "Syötä arvo tiedolle \"{{int:config-db-username}}\".",
        "config-db-install-username": "Syötä käyttäjänimi jota käytetään muodostettaessa yhteys tietokantaan asennuksen aikana.\nTämä ei ole MediaWiki tilin käyttäjänimi; tämä on tietokannan käyttäjänimi.",
        "config-db-install-password": "Syötä salasana jota käytetään muodostettaessa yhteys tietokantaan asennuksen aikana.\nTämä ei ole MediaWiki tilin salasana; tämä on tietokannan salasana.",
        "config-db-install-help": "Anna käyttäjätunnus ja salasana, joita käytetään asennuksen aikana.",
        "config-charset-mysql4": "MySQL 4.0, taaksepäin yhteensopiva UTF-8",
        "config-mysql-old": "MediaWiki tarvitsee MySQL:n version $1 tai uudemman. Nykyinen versio on $2.",
        "config-db-port": "Tietokannan portti:",
+       "config-db-schema": "MediaWikin rakenne:",
+       "config-db-schema-help": "Tämä rakenne on normaalisti toimiva.\nMuuta rakennetta vain, mikäli on pakko ja tiedät, mitä teet.",
        "config-pg-test-error": "Tietokantaan <strong>$1 ei voida muodostaa yhteyttä</strong>: $2",
+       "config-sqlite-dir": "SQLiten datahakemisto:",
+       "config-sqlite-dir-help": "SQLite tallentaa kaiken sisällön yhteen tiedostoon.\n\nPalvelimen pitää pystyä kirjoittamaan tietoa hakemistoon asennuksen aikana.\n\nHakemiston <strong>ei</strong> tulisi olla nähtävissä www-selaimella. Siksi hakemisto on eri kuin missä PHP-tiedostot sijaitsevat.\n\nAsennusohjelma luo <code>.htaccess</code>-tiedoston, mutta jos sen luomisessa ilmenee ongelmia joku voi päästä käsiksi tietokantaasi. \nTietokannassa on kaikki sähköpostiosoitteet, salasanat, poistetut versiot ja kaikki muu tieto, joka ei näy wikissä.\n\nSuosittelemme tallentamaan tietokannan eri hakemistoon, esimerkiksi <code>/var/lib/mediawiki/yourwiki</code>.",
        "config-type-mysql": "MySQL (tai yhteensopiva)",
        "config-type-postgres": "PostgreSQL",
        "config-type-sqlite": "SQLite",
        "config-extensions": "Laajennukset",
        "config-extensions-help": "Yllä luetellut laajennukset löytyvät <code>./extensions</code> hakemistosta.\n\nNe saattavat vaatia lisäasetuksia, mutta voit ottaa ne käyttöön nyt.",
        "config-skins": "Ulkoasut",
+       "config-skins-help": "Seuraavat teemat löydettiin hakemistosta <code>./skins</code>. Ota käyttöön vähintään yksi teema ja aseta se oletukseksi.",
+       "config-skins-use-as-default": "Käytä tätä teemaa oletuksena.",
+       "config-skins-missing": "Teemoja ei löytynyt; MediaWiki käyttää väliaikaista teemaa, kunnes asennat toimivia.",
        "config-skins-must-enable-some": "Sinut täytyy valita ainakin yksi ulkoasu.",
+       "config-skins-must-enable-default": "Oletusteeman pitää olla käytössä.",
        "config-install-alreadydone": "<strong>Varoitus:</strong> MediaWiki on jo asennettu ja yrität asentaa sitä uudestaan.\nSiirry seuraavalle sivulle.",
        "config-install-begin": "Painamalla \"{{int:config-continue}}\", aloitetaan MediaWikin asentaminen. \nJos haluat vielä tehdä muutoksia, paina \"{{int:config-back}}\".",
        "config-install-step-done": "valmis",
        "config-install-step-failed": "epäonnistui",
        "config-install-extensions": "Sisällytetään laajennukset",
        "config-install-database": "Asennetaan tietokantaa",
+       "config-install-schema": "Luodaan rakennetta",
+       "config-install-pg-schema-not-exist": "PostgreSQL-rakennetta ei ole olemassa.",
+       "config-install-pg-schema-failed": "Taulun luominen epäonnistui.\nVarmista, että käyttäjätunnus \"$1\" pystyy kirjoittamaan rakenteeseen \"$2\".",
+       "config-install-pg-commit": "Muutoksia tallennetaan",
+       "config-install-pg-plpgsql": "Tarkistetaan PL/pgSQL:n kieltä.",
+       "config-pg-no-plpgsql": "PL/pgSQL-kieli pitää asentaa tietokantaan $1",
        "config-pg-no-create-privs": "Määrittelemälläsi tilillä ei ole riittävästi oikeuksia luoda tiliä.",
+       "config-pg-not-in-role": "Määrittelemäsi web-käyttäjän tili on jo olemassa.\nMäärittelemälläsi käyttäjätilillä ei ole pääkäyttäjäoikeuksia eikä se toimi web-käyttäjän roolissa. Käyttäjätili ei pysty luomaan tarvittavia objekteja.\n\nMediaWiki vaatii, että web-käyttäjän pitää pystyä hallitsemaan tauluja. Anna toinen web-käyttäjätunnus tai klikkaa \"takaisin\" ja määrittele käyttäjätunnus, joka toimii asennuksessa.",
        "config-install-user": "Luodaan tietokannalle käyttäjää",
        "config-install-user-alreadyexists": "Käyttäjä $1 on jo olemassa",
        "config-install-user-create-failed": "Käyttäjän \"$1\" luonti epäonnistui: $2",
        "config-install-tables": "Luodaan tauluja",
        "config-install-tables-exist": "<strong>Varoitus:</strong> MediaWiki taulut ovat jo olemassa.\nOhitetaan taulujen luonti.",
        "config-install-tables-failed": "<strong>Virhe:</strong> Taulujen luominen epäonnistui seuraavaan virheen takia: $1",
+       "config-install-interwiki": "Luodaan oletustaulua interwikille",
        "config-install-interwiki-list": "Tiedostoa <code>interwiki.list</code> ei voitu lukea.",
+       "config-install-interwiki-exists": "<strong>Varoitus:</strong> interwiki-taulussa on jo tietueita, ohitetaan oletuslista.",
+       "config-install-stats": "Alustetaan tilastoja",
        "config-install-keys": "Muodostetaan salausavaimia",
+       "config-install-updates": "Estä tarpeettomien päivitysten asennus",
        "config-install-sysop": "Luodaan ylläpitäjän tiliä",
        "config-install-subscribe-fail": "Liittyminen mediawiki-announce listalle epäonnistui: $1",
+       "config-install-subscribe-notpossible": "cURL-ohjelmaa ei ole asennettu eikä <code>allow_url_fopen</code> ole saatavilla.",
        "config-install-mainpage": "Luodaan etusivu oletussisällöllä",
        "config-install-extension-tables": "Luodaan tauluja käyttöönotetuille laajuennuksille",
        "config-install-mainpage-failed": "Etusivun lisääminen ei onnistunut: $1",
+       "config-install-done": "<strong>Onnittelut!</strong>\nMediaWiki on asennettu onnistuneesti\n\nAsennusohjelma on luonut <code>LocalSettings.php</code> -tiedoston.\nSiinä on kaikki MediaWikin asetukset.\n\nLataa tiedosto ja laita se MediaWikin asennushakemistoon (sama kuin missä on index.php). Lataamisen olisi pitänyt alkaa automaattisesti.\n\nMikäli keskeytit latauksen, käynnistä se uudestaan tästä linkistä:\n\n$3\n\n<strong>HUOM!</strong> Mikäli et nyt lataa tiedostoa, joudut aloittamaan asennuksen alusta.\n\nKun olet laittanut tiedoston oikeaan paikkaan voit <strong>[$2 mennä wikiisi]</strong>.",
        "config-download-localsettings": "Lataa <code>LocalSettings.php</code>",
        "config-help": "ohje",
+       "config-help-tooltip": "Klikkaa laajentaaksesi",
        "config-nofile": "Tiedostoa \"$1\" ei löytynyt. Onko se poistettu?",
        "config-extension-link": "Tiesitkö että wiki tukee [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions laajennuksia]?\n\nLaajennuksia voi hakea myös [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category luokittain].",
        "mainpagetext": "'''MediaWiki on onnistuneesti asennettu.'''",
index 86b760c..ba84435 100644 (file)
@@ -48,7 +48,6 @@
        "config-env-good": "పర్యావరణాన్ని పరీక్షించాం.\nఇక మీరు MediaWiki ని స్థాపించుకోవచ్చు.",
        "config-env-bad": "పర్యావరణాన్ని పరీక్షించాం.\nమీరు MediaWiki ని స్థాపించలేరు.",
        "config-env-php": "PHP $1 స్థాపించబడింది.",
-       "config-env-php-toolow": "PHP $1 స్థాపించబడింది.\nఅయితే, MediaWiki కి PHP $2 గానీ ఆ పైది గానీ కావాలి.",
        "config-unicode-using-utf8": "యూనికోడు నార్మలైజేషన్ కోసం బ్రయాన్ విబర్ గారి utf8_normalize.so ను వాడుతున్నాం.",
        "config-unicode-using-intl": "యూనికోడు నార్మలైజేషన్ కోసం [http://pecl.php.net/intl intl PECL పొడిగింత] ను వాడుతున్నాం.",
        "config-outdated-sqlite": "<strong>హెచ్చరిక:</strong> మీ వద్ద SQLite $1 ఉంది. అదికావలసిన వెర్షను $2 కంటే దిగువది. SQLite అందుబాటులో ఉండదు.",
        "config-memcache-badport": "Memcached పోర్టు సఖ్యలు $1, $2 ల మధ్య ఉండాలి.",
        "config-extensions": "పొడిగింతలు",
        "config-extensions-help": "పైన చూపిన పొడిగింతలు మీ <code>./extensions</code> డైరెక్టరీలో ఉన్నాయి.\n\nవాటికి అదనంగా కాన్ఫిగరేషన్ అవసరం కావచ్చు. అయితే మీరు వాటిని చేతనం చెయ్యవచ్చు.",
+       "config-skins": "అలంకారాలు",
        "config-install-alreadydone": "<strong>హెచ్చరిక:</strong> మీరు ఈసరికే MediaWiki ని స్థాపించినట్లుగా అనిపిస్తోంది. మళ్ళీ స్థాపించే ప్రయత్నం చేస్తున్నట్లున్నారు.\nతరువాత పేజీకి వెళ్ళండి.",
        "config-install-begin": "\"{{int:config-continue}}\" నొక్కి, MediaWiki స్థాపనను మొదలుపెట్టవచ్చు.\nఇంకా మార్పులు చెయ్యదలిస్తే, \"{{int:config-back}}\" నొక్కండి.",
        "config-install-step-done": "పూర్తయింది",
index 4be4d98..923a80d 100644 (file)
@@ -3,14 +3,18 @@
                "authors": [
                        "පසිඳු කාවින්ද",
                        "Minh Nguyen",
-                       "Withoutaname"
+                       "Withoutaname",
+                       "Dinhxuanduyet"
                ]
        },
        "config-desc": "Trình cài đặt MediaWiki",
        "config-title": "Cài đặt MediaWiki $1",
        "config-information": "Thông tin",
+       "config-localsettings-upgrade": "Một file <code>LocalSettings.php</code> đã được phát hiện.\nĐể nâng cấp cài đặt này, xin nhập giá trị của  <code>$wgUpgradeKey</code> trong hộp thoại bên dưới đây.\nBạn sẽ tìm thấy nó trong<code>LocalSettings.php</code>.",
+       "config-localsettings-cli-upgrade": "Một file <code>LocalSettings.php</code> đã được phát hiện.\nĐể nâng cấp cài đặt này, hãy chạy <code>update.php</code>",
        "config-localsettings-key": "Chìa khóa nâng cấp:",
        "config-localsettings-badkey": "Bạn đã cung cấp một chìa khóa sai.",
+       "config-upgrade-key-missing": "Có một bản cài đặt của MediaWiki sẵn trước đó đã được phát hiện.\nĐể nâng cấp cài đặt này, hãy đặt dòng sau vào dưới của của <code>LocalSettings.php</code>:\n\n$1",
        "config-session-error": "Lỗi khi bắt đầu phiên làm việc: $1",
        "config-your-language": "Ngôn ngữ của bạn:",
        "config-your-language-help": "Chọn một ngôn ngữ để sử dụng trong quá trình cài đặt.",
index db8a7ec..93ae83b 100644 (file)
@@ -117,7 +117,7 @@ abstract class GenericArrayObject extends ArrayObject {
         *
         * @param mixed $value
         *
-        * @return boolean
+        * @return bool
         */
        protected function hasValidType( $value ) {
                $class = $this->getObjectType();
@@ -171,7 +171,7 @@ abstract class GenericArrayObject extends ArrayObject {
         * @param integer|string $index
         * @param mixed $value
         *
-        * @return boolean
+        * @return bool
         */
        protected function preSetElement( $index, $value ) {
                return true;
@@ -232,7 +232,7 @@ abstract class GenericArrayObject extends ArrayObject {
         *
         * @since 1.20
         *
-        * @return boolean
+        * @return bool
         */
        public function isEmpty() {
                return $this->count() === 0;
index 3b9f1a8..c1c841e 100644 (file)
@@ -162,7 +162,7 @@ class IPSet {
         * Match an IP address against the set
         *
         * @param string $ip string IPv[46] address
-        * @return boolean true is match success, false is match failure
+        * @return bool true is match success, false is match failure
         *
         * If $ip is unparseable, inet_pton may issue an E_WARNING to that effect
         */
index 631b651..629c269 100644 (file)
 class ScopedCallback {
        /** @var callable */
        protected $callback;
+       /** @var array */
+       protected $params;
 
        /**
         * @param callable $callback
+        * @param array $params Callback arguments (since 1.25)
         * @throws Exception
         */
-       public function __construct( $callback ) {
+       public function __construct( $callback, array $params = array() ) {
                if ( !is_callable( $callback ) ) {
                        throw new InvalidArgumentException( "Provided callback is not valid." );
                }
                $this->callback = $callback;
+               $this->params = $params;
        }
 
        /**
@@ -67,7 +71,7 @@ class ScopedCallback {
         */
        function __destruct() {
                if ( $this->callback !== null ) {
-                       call_user_func( $this->callback );
+                       call_user_func_array( $this->callback, $this->params );
                }
        }
 }
index 990e2c3..98ff675 100644 (file)
@@ -267,13 +267,16 @@ class Xhprof {
                                foreach ( $stats as $name => $value ) {
                                        if ( $value instanceof RunningStat ) {
                                                $total = $value->m1 * $value->n;
+                                               $percent = ( isset( $main[$name] ) && $main[$name] )
+                                                       ? 100 * $total / $main[$name]
+                                                       : 0;
                                                $this->inclusive[$func][$name] = array(
                                                        'total' => $total,
                                                        'min' => $value->min,
                                                        'mean' => $value->m1,
                                                        'max' => $value->max,
                                                        'variance' => $value->m2,
-                                                       'percent' => 100 * $total / $main[$name],
+                                                       'percent' => $percent,
                                                );
                                        }
                                }
index aca857e..0d6c3a6 100644 (file)
@@ -70,7 +70,7 @@ class XmlTypeCheck {
         *        SAX element handler event. This gives you access to the element
         *        namespace, name, attributes, and text contents.
         *        Filter should return 'true' to toggle on $this->filterMatch
-        * @param boolean $isFile (optional) indicates if the first parameter is a
+        * @param bool $isFile (optional) indicates if the first parameter is a
         *        filename (default, true) or if it is a string (false)
         * @param array $options list of additional parsing options:
         *        processing_instruction_handler: Callback for xml_set_processing_instruction_handler
index 0292af8..3c23ab9 100644 (file)
@@ -344,7 +344,7 @@ class BitmapHandler extends TransformationalImageHandler {
 
                $src_image = call_user_func( $loader, $params['srcPath'] );
 
-               $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] )  ? $this->getRotation( $image ) : 0;
+               $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] ) ? $this->getRotation( $image ) : 0;
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
                $dst_image = imagecreatetruecolor( $width, $height );
 
index 8b26c23..75df27c 100644 (file)
@@ -1783,7 +1783,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $summary = $handler->getAutosummary( $old_content, $content, $flags );
                }
 
-               $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialFormat, true );
+               $editInfo = $this->prepareContentForEdit( $content, null, $user, $serialFormat );
                $serialized = $editInfo->pst;
 
                /**
@@ -2076,7 +2076,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.21
         */
        public function prepareContentForEdit(
-               Content $content, $revid = null, User $user = null, $serialFormat = null, $useCache = false
+               Content $content, $revid = null, User $user = null, $serialFormat = null, $useCache = true
        ) {
                global $wgContLang, $wgUser;
 
index ff62e9b..7b53776 100644 (file)
@@ -131,12 +131,12 @@ class MWTidy {
                $wrappedtext = $wrapper->getWrapped( $text );
 
                $retVal = null;
-               $correctedtext = self::clean( $wrappedtext, false, $retVal );
+               list( $correctedtext, $errors ) = self::clean( $wrappedtext, $retVal );
 
                if ( $retVal < 0 ) {
                        wfDebug( "Possible tidy configuration error!\n" );
                        return $text . "\n<!-- Tidy was unable to run -->\n";
-               } elseif ( is_null( $correctedtext ) ) {
+               } elseif ( $correctedtext === '' && $text !== '' ) {
                        wfDebug( "Tidy error detected!\n" );
                        return $text . "\n<!-- Tidy found serious XHTML errors -->\n";
                }
@@ -155,31 +155,23 @@ class MWTidy {
         */
        public static function checkErrors( $text, &$errorStr = null ) {
                $retval = 0;
-               $errorStr = self::clean( $text, true, $retval );
+               list( $outputStr, $errorStr ) = self::clean( $text, $retval );
                return ( $retval < 0 && $errorStr == '' ) || $retval == 0;
        }
 
        /**
         * Perform a clean/repair operation
         * @param string $text HTML to check
-        * @param bool $stderr Whether to read result from STDERR rather than STDOUT
         * @param int &$retval Exit code (-1 on internal error)
         * @return string|null
         */
-       private static function clean( $text, $stderr = false, &$retval = null ) {
+       private static function clean( $text, &$retval = null ) {
                global $wgTidyInternal;
 
                if ( $wgTidyInternal ) {
-                       if ( wfIsHHVM() ) {
-                               if ( $stderr ) {
-                                       throw new MWException( __METHOD__.": error text return from HHVM tidy is not supported" );
-                               }
-                               return self::hhvmClean( $text, $retval );
-                       } else {
-                               return self::phpClean( $text, $stderr, $retval );
-                       }
+                       return self::internalClean( $text, $retval );
                } else {
-                       return self::externalClean( $text, $stderr, $retval );
+                       return self::externalClean( $text, $retval );
                }
        }
 
@@ -188,32 +180,23 @@ class MWTidy {
         * Also called in OutputHandler.php for full page validation
         *
         * @param string $text HTML to check
-        * @param bool $stderr Whether to read result from STDERR rather than STDOUT
         * @param int &$retval Exit code (-1 on internal error)
         * @return string|null
         */
-       private static function externalClean( $text, $stderr = false, &$retval = null ) {
+       private static function externalClean( $text, &$retval = null ) {
                global $wgTidyConf, $wgTidyBin, $wgTidyOpts;
                wfProfileIn( __METHOD__ );
 
-               $cleansource = '';
                $opts = ' -utf8';
 
-               if ( $stderr ) {
-                       $descriptorspec = array(
-                               0 => array( 'pipe', 'r' ),
-                               1 => array( 'file', wfGetNull(), 'a' ),
-                               2 => array( 'pipe', 'w' )
-                       );
-               } else {
-                       $descriptorspec = array(
-                               0 => array( 'pipe', 'r' ),
-                               1 => array( 'pipe', 'w' ),
-                               2 => array( 'file', wfGetNull(), 'a' )
-                       );
-               }
+               $descriptorspec = array(
+                       0 => array( 'pipe', 'r' ),
+                       1 => array( 'pipe', 'w' ),
+                       2 => array( 'pipe', 'w' ),
+               );
 
-               $readpipe = $stderr ? 2 : 1;
+               $outputBuffer = '';
+               $errorBuffer = '';
                $pipes = array();
 
                $process = proc_open(
@@ -230,24 +213,25 @@ class MWTidy {
                        // for tidyParseStdin and tidySaveStdout in console/tidy.c
                        fwrite( $pipes[0], $text );
                        fclose( $pipes[0] );
-                       while ( !feof( $pipes[$readpipe] ) ) {
-                               $cleansource .= fgets( $pipes[$readpipe], 1024 );
+
+                       while ( !feof( $pipes[1] ) ) {
+                               $outputBuffer .= fgets( $pipes[1], 1024 );
                        }
-                       fclose( $pipes[$readpipe] );
+                       fclose( $pipes[1] );
+
+                       while ( !feof( $pipes[2] ) ) {
+                               $errorBuffer .= fgets( $pipes[2], 1024 );
+                       }
+                       fclose( $pipes[2] );
+
                        $retval = proc_close( $process );
                } else {
                        wfWarn( "Unable to start external tidy process" );
                        $retval = -1;
                }
 
-               if ( !$stderr && $cleansource == '' && $text != '' ) {
-                       // Some kind of error happened, so we couldn't get the corrected text.
-                       // Just give up; we'll use the source text and append a warning.
-                       $cleansource = null;
-               }
-
                wfProfileOut( __METHOD__ );
-               return $cleansource;
+               return array( $outputBuffer, $errorBuffer );
        }
 
        /**
@@ -255,15 +239,16 @@ class MWTidy {
         * saving the overhead of spawning a new process.
         *
         * @param string $text HTML to check
-        * @param bool $stderr Whether to read result from error status instead of output
         * @param int &$retval Exit code (-1 on internal error)
         * @return string|null
         */
-       private static function phpClean( $text, $stderr = false, &$retval = null ) {
+       private static function internalClean( $text, &$retval = null ) {
                global $wgTidyConf, $wgDebugTidy;
                wfProfileIn( __METHOD__ );
 
-               if ( !class_exists( 'tidy' ) ) {
+               if ( ( !wfIsHHVM() && !class_exists( 'tidy' ) ) ||
+                       ( wfIsHHVM() && !function_exists( 'tidy_repair_string' ) )
+               ) {
                        wfWarn( "Unable to load internal tidy class." );
                        $retval = -1;
 
@@ -271,57 +256,36 @@ class MWTidy {
                        return null;
                }
 
-               $tidy = new tidy;
-               $tidy->parseString( $text, $wgTidyConf, 'utf8' );
-
-               if ( $stderr ) {
-                       $retval = $tidy->getStatus();
-
-                       wfProfileOut( __METHOD__ );
-                       return $tidy->errorBuffer;
-               }
-
-               $tidy->cleanRepair();
-               $retval = $tidy->getStatus();
-               if ( $retval == 2 ) {
-                       // 2 is magic number for fatal error
-                       // http://www.php.net/manual/en/function.tidy-get-status.php
-                       $cleansource = null;
+               $outputBuffer = '';
+               $errorBuffer = '';
+
+               if ( wfIsHHVM() ) {
+                       // Use the tidy extension for HHVM from
+                       // https://github.com/wikimedia/mediawiki-php-tidy
+                       //
+                       // This currently does not support the object-oriented interface, but
+                       // tidy_repair_string() can be used for the most common tasks.
+                       $result = tidy_repair_string( $text, $wgTidyConf, 'utf8' );
+                       $outputBuffer .= $result;
+                       $retval = $result === false ? -1 : 0;
                } else {
-                       $cleansource = tidy_get_output( $tidy );
-                       if ( $wgDebugTidy && $retval > 0 ) {
-                               $cleansource .= "<!--\nTidy reports:\n" .
-                                       str_replace( '-->', '--&gt;', $tidy->errorBuffer ) .
-                                       "\n-->";
+                       $tidy = new tidy;
+                       $tidy->parseString( $text, $wgTidyConf, 'utf8' );
+                       $tidy->cleanRepair();
+                       $retval = $tidy->getStatus();
+                       $outputBuffer .= tidy_get_output( $tidy );
+                       if ( $retval > 0 ) {
+                               $errorBuffer .= $tidy->errorBuffer;
                        }
                }
 
-               wfProfileOut( __METHOD__ );
-               return $cleansource;
-       }
-
-       /**
-        * Use the tidy extension for HHVM from
-        * https://github.com/wikimedia/mediawiki-php-tidy
-        *
-        * This currently does not support the object-oriented interface, but
-        * tidy_repair_string() can be used for the most common tasks.
-        *
-        * @param string $text HTML to check
-        * @param int &$retval Exit code (-1 on internal error)
-        * @return string|null
-        */
-       private static function hhvmClean( $text, &$retval ) {
-               global $wgTidyConf;
-               wfProfileIn( __METHOD__ );
-               $cleansource = tidy_repair_string( $text, $wgTidyConf, 'utf8' );
-               if ( $cleansource === false ) {
-                       $cleansource = null;
-                       $retval = -1;
-               } else {
-                       $retval = 0;
+               if ( $wgDebugTidy && $errorBuffer && $retval > 0 ) {
+                       $outputBuffer .= "<!--\nTidy reports:\n" .
+                               str_replace( '-->', '--&gt;', $tidy->errorBuffer ) .
+                               "\n-->";
                }
+
                wfProfileOut( __METHOD__ );
-               return $cleansource;
+               return array( $outputBuffer, $errorBuffer );
        }
 }
index 1b926c4..7ea57be 100644 (file)
@@ -532,14 +532,14 @@ class Parser {
 
                        // Add on template profiling data
                        $dataByFunc = $this->mProfiler->getFunctionStats();
-                       uasort( $dataByFunc, function( $a, $b ) {
+                       uasort( $dataByFunc, function ( $a, $b ) {
                                return $a['real'] < $b['real']; // descending order
                        } );
                        $profileReport = "Transclusion expansion time report (%,ms,calls,template)\n";
                        foreach ( array_slice( $dataByFunc, 0, 10 ) as $item ) {
                                $profileReport .= sprintf( "%6.2f%% %8.3f %6d - %s\n",
                                        $item['%real'], $item['real'], $item['calls'],
-                                       htmlspecialchars($item['name'] ) );
+                                       htmlspecialchars( $item['name'] ) );
                        }
                        $text .= "\n<!-- \n$profileReport-->\n";
 
index b570fa5..a61dbf0 100644 (file)
@@ -641,6 +641,7 @@ class ParserOptions {
 
                wfProfileIn( __METHOD__ );
 
+               // *UPDATE* ParserOptions::matches() if any of this changes as needed
                $this->mInterwikiMagic = $wgInterwikiMagic;
                $this->mAllowExternalImages = $wgAllowExternalImages;
                $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
@@ -666,6 +667,32 @@ class ParserOptions {
                wfProfileOut( __METHOD__ );
        }
 
+       /**
+        * Check if these options match that of another options set
+        *
+        * This ignores report limit settings that only affect HTML comments
+        *
+        * @return bool
+        * @since 1.25
+        */
+       public function matches( ParserOptions $other ) {
+               $fields = array_keys( get_class_vars( __CLASS__ ) );
+               $fields = array_diff( $fields, array(
+                       'mEnableLimitReport', // only effects HTML comments
+                       'onAccessCallback', // only used for ParserOutput option tracking
+               ) );
+               foreach ( $fields as $field ) {
+                       if ( !is_object( $this->$field ) && $this->$field !== $other->$field ) {
+                               return false;
+                       }
+               }
+               // Check the object and lazy-loaded options
+               return (
+                       $this->mUserLang->getCode() === $other->mUserLang->getCode() &&
+                       $this->getDateFormat() === $other->getDateFormat()
+               );
+       }
+
        /**
         * Registers a callback for tracking which ParserOptions which are used.
         * This is a private API with the parser.
index 2be142f..667a9e2 100644 (file)
@@ -241,14 +241,16 @@ abstract class Profiler {
         * This makes filtering them out easier and follows the xhprof style.
         *
         * @return array List of method entries arrays, each having:
-        *   - name    : method name
-        *   - calls   : the number of invoking calls
-        *   - real    : real time ellapsed (ms)
-        *   - %real   : percent real time
-        *   - cpu     : CPU time ellapsed (ms)
-        *   - %cpu    : percent CPU time
-        *   - memory  : memory used (bytes)
-        *   - %memory : percent memory used
+        *   - name     : method name
+        *   - calls    : the number of invoking calls
+        *   - real     : real time ellapsed (ms)
+        *   - %real    : percent real time
+        *   - cpu      : CPU time ellapsed (ms)
+        *   - %cpu     : percent CPU time
+        *   - memory   : memory used (bytes)
+        *   - %memory  : percent memory used
+        *   - min_real : min real time in a call (ms)
+        *   - max_real : max real time in a call (ms)
         * @since 1.25
         */
        abstract public function getFunctionStats();
index 87706e6..b40519e 100644 (file)
@@ -222,7 +222,7 @@ class ProfilerStandard extends Profiler {
                $this->profileIn( $section );
 
                $that = $this;
-               return new ScopedCallback( function() use ( $that, $section ) {
+               return new ScopedCallback( function () use ( $that, $section ) {
                        $that->profileOut( $section );
                } );
        }
@@ -485,8 +485,8 @@ class ProfilerStandard extends Profiler {
                                '%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0,
                                'memory' => $data['memory'],
                                '%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0,
-                               'min' => $data['min_real'] * 1000,
-                               'max' => $data['max_real'] * 1000
+                               'min_real' => $data['min_real'] * 1000,
+                               'max_real' => $data['max_real'] * 1000
                        );
                }
 
index 2db06e4..9a7ec8c 100644 (file)
@@ -34,7 +34,7 @@ class ProfilerStub extends Profiler {
        }
 
        public function scopedProfileIn( $section ) {
-               return new ScopedCallback( function() {
+               return new ScopedCallback( function () {
                        // no-op
                } );
        }
index d91b429..a40c44a 100644 (file)
  * @see https://github.com/facebook/hhvm/blob/master/hphp/doc/profiling.md
  */
 class ProfilerXhprof extends Profiler {
-
        /**
         * @var Xhprof $xhprof
         */
        protected $xhprof;
 
        /**
-        * Type of report to send when logData() is called.
-        * @var string $logType
-        */
-       protected $logType;
-
-       /**
-        * Should profile report sent to in page content be visible?
-        * @var bool $visible
+        * Profiler for explicit, arbitrary, frame labels
+        * @var SectionProfiler
         */
-       protected $visible;
+       protected $sprofiler;
 
        /**
         * @param array $params
         * @see Xhprof::__construct()
         */
        public function __construct( array $params = array() ) {
-               $params = array_merge(
-                       array(
-                               'log' => 'text',
-                               'visible' => false
-                       ),
-                       $params
-               );
                parent::__construct( $params );
-               $this->logType = $params['log'];
-               $this->visible = $params['visible'];
                $this->xhprof = new Xhprof( $params );
+               $this->sprofiler = new SectionProfiler();
        }
 
        /**
@@ -118,22 +103,7 @@ class ProfilerXhprof extends Profiler {
        }
 
        public function scopedProfileIn( $section ) {
-               static $exists = null;
-               // Only HHVM supports this, not the standard PECL extension
-               if ( $exists === null ) {
-                       $exists = function_exists( 'xhprof_frame_begin' );
-               }
-
-               if ( $exists ) {
-                       xhprof_frame_begin( $section );
-                       return new ScopedCallback( function() {
-                               xhprof_frame_end();
-                       } );
-               }
-
-               return new ScopedCallback( function() {
-                       // no-op
-               } );
+               return $this->sprofiler->scopedProfileIn( $section );
        }
 
        /**
@@ -146,9 +116,10 @@ class ProfilerXhprof extends Profiler {
                $metrics = $this->xhprof->getCompleteMetrics();
                $profile = array();
 
+               $main = null; // units in ms
                foreach ( $metrics as $fname => $stats ) {
                        // Convert elapsed times from μs to ms to match ProfilerStandard
-                       $profile[] = array(
+                       $entry = array(
                                'name' => $fname,
                                'calls' => $stats['ct'],
                                'real' => $stats['wt']['total'] / 1000,
@@ -157,9 +128,22 @@ class ProfilerXhprof extends Profiler {
                                '%cpu' => isset( $stats['cpu'] ) ? $stats['cpu']['percent'] : 0,
                                'memory' => isset( $stats['mu'] ) ? $stats['mu']['total'] : 0,
                                '%memory' => isset( $stats['mu'] ) ? $stats['mu']['percent'] : 0,
-                               'min' => $stats['wt']['min'] / 1000,
-                               'max' => $stats['wt']['max'] / 1000
+                               'min_real' => $stats['wt']['min'] / 1000,
+                               'max_real' => $stats['wt']['max'] / 1000
                        );
+                       $profile[] = $entry;
+                       if ( $fname === 'main()' ) {
+                               $main = $entry;
+                       }
+               }
+
+               // Merge in all of the custom profile sections
+               foreach ( $this->sprofiler->getFunctionStats() as $stats ) {
+                       // @note: getFunctionStats() values already in ms
+                       $stats['%real'] = $stats['real'] / $main['real'];
+                       $stats['%cpu'] = $main['cpu'] ? $stats['cpu'] / $main['cpu'] * 100 : 0;
+                       $stats['%memory'] = $main['memory'] ? $stats['memory'] / $main['memory'] * 100 : 0;
+                       $profile[] = $stats; // assume no section names collide with $metrics
                }
 
                return $profile;
@@ -191,8 +175,13 @@ class ProfilerXhprof extends Profiler {
         * @return string
         */
        protected function getFunctionReport() {
-               $data = $this->xhprof->getInclusiveMetrics();
-               uasort( $data, Xhprof::makeSortFunction( 'wt', 'total' ) );
+               $data = $this->getFunctionStats();
+               usort( $data, function( $a, $b ) {
+                       if ( $a['real'] === $b['real'] ) {
+                               return 0;
+                       }
+                       return ( $a['real'] > $b['real'] ) ? -1 : 1; // descending
+               } );
 
                $width = 140;
                $nameWidth = $width - 65;
@@ -201,50 +190,18 @@ class ProfilerXhprof extends Profiler {
                $out[] = sprintf( "%-{$nameWidth}s %6s %9s %9s %9s %9s %7s %9s",
                        'Name', 'Calls', 'Total', 'Min', 'Each', 'Max', '%', 'Mem'
                );
-               foreach ( $data as $func => $stats ) {
-                       $out[] = sprintf( $format,
-                               $func,
-                               $stats['ct'],
-                               $stats['wt']['total'],
-                               $stats['wt']['min'],
-                               $stats['wt']['mean'],
-                               $stats['wt']['max'],
-                               $stats['wt']['percent'],
-                               isset( $stats['mu'] ) ? $stats['mu']['total'] : 0
-                       );
-               }
-               return implode( "\n", $out );
-       }
-
-       /**
-        * Get a brief report of profiled functions sorted by inclusive wall clock
-        * time in descending order.
-        *
-        * Each line of the report includes this data:
-        * - Percentage of total wall clock time spent in function
-        * - Total wall clock time spent in function in seconds
-        * - Number of times function was called
-        * - Function name
-        *
-        * @param string $header Header text to prepend to report
-        * @param string $footer Footer text to append to report
-        * @return string
-        */
-       protected function getSummaryReport( $header = '', $footer = '' ) {
-               $data = $this->xhprof->getInclusiveMetrics();
-               uasort( $data, Xhprof::makeSortFunction( 'wt', 'total' ) );
-
-               $format = '%6.2f%% %3.6f %6d - %s';
-               $out = array( $header );
-               foreach ( $data as $func => $stats ) {
+               foreach ( $data as $stats ) {
                        $out[] = sprintf( $format,
-                               $stats['wt']['percent'],
-                               $stats['wt']['total'] / 1e6,
-                               $stats['ct'],
-                               $func
+                               $stats['name'],
+                               $stats['calls'],
+                               $stats['real'] * 1000,
+                               $stats['min_real'] * 1000,
+                               $stats['real'] / $stats['calls'] * 1000,
+                               $stats['max_real'] * 1000,
+                               $stats['%real'],
+                               $stats['memory']
                        );
                }
-               $out[] = $footer;
                return implode( "\n", $out );
        }
 }
index 89eebbe..2c36b68 100644 (file)
  * @since 1.25
  */
 class SectionProfiler {
+       /** @var array Map of (mem,real,cpu) */
+       protected $start;
+       /** @var array Map of (mem,real,cpu) */
+       protected $end;
        /** @var array List of resolved profile calls with start/end data */
        protected $stack = array();
        /** @var array Queue of open profile calls with start data */
@@ -37,11 +41,13 @@ class SectionProfiler {
        protected $collated = array();
        /** @var bool */
        protected $collateDone = false;
+
        /** @var bool Whether to collect the full stack trace or just aggregates */
        protected $collateOnly = true;
-
        /** @var array Cache of a standard broken collation entry */
        protected $errorEntry;
+       /** @var callable Cache of a profile out callback */
+       protected $profileOutCallback;
 
        /**
         * @param array $params
@@ -49,6 +55,9 @@ class SectionProfiler {
        public function __construct( array $params = array() ) {
                $this->errorEntry = $this->getErrorEntry();
                $this->collateOnly = empty( $params['trace'] );
+               $this->profileOutCallback = function ( $profiler, $section ) {
+                       $profiler->profileOutInternal( $section );
+               };
        }
 
        /**
@@ -59,9 +68,7 @@ class SectionProfiler {
                $this->profileInInternal( $section );
 
                $that = $this;
-               return new ScopedCallback( function() use ( $that, $section ) {
-                       $that->profileOutInternal( $section );
-               } );
+               return new ScopedCallback( $this->profileOutCallback, array( $that, $section ) );
        }
 
        /**
@@ -89,18 +96,15 @@ class SectionProfiler {
         *   - %cpu    : percent real time
         *   - memory  : memory used (bytes)
         *   - %memory : percent memory used
+        *   - min_real : min real time in a call (ms)
+        *   - max_real : max real time in a call (ms)
         */
        public function getFunctionStats() {
                $this->collateData();
 
-               $totalCpu = 0.0;
-               $totalReal = 0.0;
-               $totalMem = 0;
-               foreach ( $this->collated as $fname => $data ) {
-                       $totalCpu += $data['cpu'];
-                       $totalReal += $data['real'];
-                       $totalMem += $data['memory'];
-               }
+               $totalCpu = max( $this->end['cpu'] - $this->start['cpu'], 0 );
+               $totalReal = max( $this->end['real'] - $this->start['real'], 0 );
+               $totalMem = max( $this->end['memory'] - $this->start['memory'], 0 );
 
                $profile = array();
                foreach ( $this->collated as $fname => $data ) {
@@ -113,6 +117,8 @@ class SectionProfiler {
                                '%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0,
                                'memory' => $data['memory'],
                                '%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0,
+                               'min_real' => 1000 * $data['min_real'],
+                               'max_real' => 1000 * $data['max_real']
                        );
                }
 
@@ -125,11 +131,25 @@ class SectionProfiler {
                        '%cpu' => 100,
                        'memory' => $totalMem,
                        '%memory' => 100,
+                       'min_real' => 1000 * $totalReal,
+                       'max_real' => 1000 * $totalReal
                );
 
                return $profile;
        }
 
+       /**
+        * Clear all of the profiling data for another run
+        */
+       public function reset() {
+               $this->start = null;
+               $this->end = null;
+               $this->stack = array();
+               $this->workStack = array();
+               $this->collated = array();
+               $this->collateDone = false;
+       }
+
        /**
         * @return array Initial collation entry
         */
@@ -138,7 +158,9 @@ class SectionProfiler {
                        'cpu'      => 0.0,
                        'real'     => 0.0,
                        'memory'   => 0,
-                       'count'    => 0
+                       'count'    => 0,
+                       'min_real' => 0.0,
+                       'max_real' => 0.0
                );
        }
 
@@ -169,6 +191,8 @@ class SectionProfiler {
                $entry['real'] += $elapsedReal;
                $entry['memory'] += $memChange > 0 ? $memChange : 0;
                $entry['count']++;
+               $entry['min_real'] = min( $entry['min_real'], $elapsedReal );
+               $entry['max_real'] = max( $entry['max_real'], $elapsedReal );
        }
 
        /**
@@ -177,12 +201,25 @@ class SectionProfiler {
         * @param string $functionname
         */
        public function profileInInternal( $functionname ) {
+               // Once the data is collated for reports, any future calls
+               // should clear the collation cache so the next report will
+               // reflect them. This matters when trace mode is used.
+               $this->collateDone = false;
+
+               $cpu = $this->getTime( 'cpu' );
+               $real = $this->getTime( 'wall' );
+               $memory = memory_get_usage();
+
+               if ( $this->start === null ) {
+                       $this->start = array( 'cpu' => $cpu, 'real' => $real, 'memory' => $memory );
+               }
+
                $this->workStack[] = array(
                        $functionname,
                        count( $this->workStack ),
-                       $this->getTime( 'time' ),
-                       $this->getTime( 'cpu' ),
-                       memory_get_usage()
+                       $real,
+                       $cpu,
+                       $memory
                );
        }
 
@@ -217,17 +254,25 @@ class SectionProfiler {
                                $this->stack[] = array( $message, 0, 0.0, 0.0, 0, 0.0, 0.0, 0 );
                        }
                }
+
                $realTime = $this->getTime( 'wall' );
                $cpuTime = $this->getTime( 'cpu' );
+               $memUsage = memory_get_usage();
+
                if ( $this->collateOnly ) {
                        $elapsedcpu = $cpuTime - $octime;
                        $elapsedreal = $realTime - $ortime;
-                       $memchange = memory_get_usage() - $omem;
+                       $memchange = $memUsage - $omem;
                        $this->updateEntry( $functionname, $elapsedcpu, $elapsedreal, $memchange );
                } else {
-                       $this->stack[] = array_merge( $item,
-                               array( $realTime, $cpuTime,     memory_get_usage() ) );
+                       $this->stack[] = array_merge( $item, array( $realTime, $cpuTime, $memUsage ) );
                }
+
+               $this->end = array(
+                       'cpu'      => $cpuTime,
+                       'real'     => $realTime,
+                       'memory'   => $memUsage
+               );
        }
 
        /**
@@ -320,6 +365,7 @@ class SectionProfiler {
                $this->collated = array();
 
                # Estimate profiling overhead
+               $oldEnd = $this->end;
                $profileCount = count( $this->stack );
                $this->calculateOverhead( $profileCount );
 
@@ -355,7 +401,7 @@ class SectionProfiler {
                        $subcalls = $this->calltreeCount( $this->stack, $index );
 
                        if ( substr( $fname, 0, 9 ) !== '-overhead' ) {
-                               # Adjust for profiling overhead (except special values with elapsed=0
+                               # Adjust for profiling overhead (except special values with elapsed=0)
                                if ( $elapsed ) {
                                        $elapsed -= $overheadInternal;
                                        $elapsed -= ( $subcalls * $overheadTotal );
@@ -368,6 +414,9 @@ class SectionProfiler {
 
                $this->collated['-overhead-total']['count'] = $profileCount;
                arsort( $this->collated, SORT_NUMERIC );
+
+               // Unclobber the end info map (the overhead checking alters it)
+               $this->end = $oldEnd;
        }
 
        /**
index ab42802..76d62d2 100644 (file)
@@ -38,7 +38,7 @@ class ProfilerOutputDb extends ProfilerOutput {
                // Initialize per-host profiling from config, back-compat if available
                if ( isset( $this->params['perHost'] ) ) {
                        $this->perHost = $this->params['perHost'];
-               } elseif( $wgProfilePerHost ) {
+               } elseif ( $wgProfilePerHost ) {
                        $this->perHost = $wgProfilePerHost;
                }
        }
index b24bbef..d37d74f 100644 (file)
@@ -43,11 +43,11 @@ class ProfilerOutputText extends ProfilerOutput {
 
                        // Filter out really tiny entries
                        $min = $this->thresholdMs;
-                       $stats = array_filter( $stats, function( $a ) use ( $min ) {
+                       $stats = array_filter( $stats, function ( $a ) use ( $min ) {
                                return $a['real'] > $min;
                        } );
                        // Sort descending by time elapsed
-                       usort( $stats, function( $a, $b ) {
+                       usort( $stats, function ( $a, $b ) {
                                return $a['real'] < $b['real'];
                        } );
 
index d5c7e5c..7da03c1 100644 (file)
@@ -45,19 +45,19 @@ class ProfilerOutputUdp extends ProfilerOutput {
                // Initialize port, host, and format from config, back-compat if available
                if ( isset( $this->params['udpport'] ) ) {
                        $this->port = $this->params['udpport'];
-               } elseif( $wgUDPProfilerPort ) {
+               } elseif ( $wgUDPProfilerPort ) {
                        $this->port = $wgUDPProfilerPort;
                }
 
                if ( isset( $this->params['udphost'] ) ) {
                        $this->host = $this->params['udphost'];
-               } elseif( $wgUDPProfilerHost ) {
+               } elseif ( $wgUDPProfilerHost ) {
                        $this->host = $wgUDPProfilerHost;
                }
 
                if ( isset( $this->params['udpformat'] ) ) {
                        $this->format = $this->params['udpformat'];
-               } elseif( $wgUDPProfilerFormatString ) {
+               } elseif ( $wgUDPProfilerFormatString ) {
                        $this->format = $wgUDPProfilerFormatString;
                }
        }
index 2c54f69..14a6f30 100644 (file)
@@ -63,8 +63,11 @@ class ResourceLoader {
         */
        protected $sources = array();
 
-       /** @var bool */
-       protected $hasErrors = false;
+       /**
+        * Errors accumulated during current respond() call.
+        * @var array
+        */
+       protected $errors = array();
 
        /**
         * Load information stored in the database about modules.
@@ -140,7 +143,7 @@ class ResourceLoader {
                foreach ( array_keys( $modulesWithoutMessages ) as $name ) {
                        $module = $this->getModule( $name );
                        if ( $module ) {
-                               $module->setMsgBlobMtime( $lang, 0 );
+                               $module->setMsgBlobMtime( $lang, 1 );
                        }
                }
        }
@@ -209,9 +212,7 @@ class ResourceLoader {
                } catch ( Exception $e ) {
                        MWExceptionHandler::logException( $e );
                        wfDebugLog( 'resourceloader', __METHOD__ . ": minification failed: $e" );
-                       $this->hasErrors = true;
-                       // Return exception as a comment
-                       $result = self::formatException( $e );
+                       $this->errors[] = self::formatExceptionNoComment( $e );
                }
 
                wfProfileOut( __METHOD__ );
@@ -579,7 +580,6 @@ class ResourceLoader {
                ob_start();
 
                wfProfileIn( __METHOD__ );
-               $errors = '';
 
                // Find out which modules are missing and instantiate the others
                $modules = array();
@@ -591,10 +591,7 @@ class ResourceLoader {
                                // This is a security issue, see bug 34907.
                                if ( $module->getGroup() === 'private' ) {
                                        wfDebugLog( 'resourceloader', __METHOD__ . ": request for private module '$name' denied" );
-                                       $this->hasErrors = true;
-                                       // Add exception to the output as a comment
-                                       $errors .= self::makeComment( "Cannot show private module \"$name\"" );
-
+                                       $this->errors[] = "Cannot show private module \"$name\"";
                                        continue;
                                }
                                $modules[$name] = $module;
@@ -609,9 +606,7 @@ class ResourceLoader {
                } catch ( Exception $e ) {
                        MWExceptionHandler::logException( $e );
                        wfDebugLog( 'resourceloader', __METHOD__ . ": preloading module info failed: $e" );
-                       $this->hasErrors = true;
-                       // Add exception to the output as a comment
-                       $errors .= self::formatException( $e );
+                       $this->errors[] = self::formatExceptionNoComment( $e );
                }
 
                wfProfileIn( __METHOD__ . '-getModifiedTime' );
@@ -629,9 +624,7 @@ class ResourceLoader {
                        } catch ( Exception $e ) {
                                MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": calculating maximum modified time failed: $e" );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $errors .= self::formatException( $e );
+                               $this->errors[] = self::formatExceptionNoComment( $e );
                        }
                }
 
@@ -646,19 +639,15 @@ class ResourceLoader {
                // Generate a response
                $response = $this->makeModuleResponse( $context, $modules, $missing );
 
-               // Prepend comments indicating exceptions
-               $response = $errors . $response;
-
                // Capture any PHP warnings from the output buffer and append them to the
-               // response in a comment if we're in debug mode.
+               // error list if we're in debug mode.
                if ( $context->getDebug() && strlen( $warnings = ob_get_contents() ) ) {
-                       $response = self::makeComment( $warnings ) . $response;
-                       $this->hasErrors = true;
+                       $this->errors[] = $warnings;
                }
 
                // Save response to file cache unless there are errors
-               if ( isset( $fileCache ) && !$errors && !count( $missing ) ) {
-                       // Cache single modules...and other requests if there are enough hits
+               if ( isset( $fileCache ) && !$this->errors && !count( $missing ) ) {
+                       // Cache single modules and images...and other requests if there are enough hits
                        if ( ResourceFileCache::useFileCache( $context ) ) {
                                if ( $fileCache->isCacheWorthy() ) {
                                        $fileCache->saveText( $response );
@@ -669,10 +658,28 @@ class ResourceLoader {
                }
 
                // Send content type and cache related headers
-               $this->sendResponseHeaders( $context, $mtime, $this->hasErrors );
+               $this->sendResponseHeaders( $context, $mtime, (bool)$this->errors );
 
                // Remove the output buffer and output the response
                ob_end_clean();
+
+               if ( $context->getImageObj() && $this->errors ) {
+                       // We can't show both the error messages and the response when it's an image.
+                       $errorText = '';
+                       foreach ( $this->errors as $error ) {
+                               $errorText .= $error . "\n";
+                       }
+                       $response = $errorText;
+               } elseif ( $this->errors ) {
+                       // Prepend comments indicating errors
+                       $errorText = '';
+                       foreach ( $this->errors as $error ) {
+                               $errorText .= self::makeComment( $error );
+                       }
+                       $response = $errorText . $response;
+               }
+
+               $this->errors = array();
                echo $response;
 
                wfProfileOut( __METHOD__ );
@@ -682,7 +689,7 @@ class ResourceLoader {
         * Send content type and last modified headers to the client.
         * @param ResourceLoaderContext $context
         * @param string $mtime TS_MW timestamp to use for last-modified
-        * @param bool $errors Whether there are commented-out errors in the response
+        * @param bool $errors Whether there are errors in the response
         * @return void
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime, $errors ) {
@@ -699,7 +706,14 @@ class ResourceLoader {
                        $maxage = $rlMaxage['versioned']['client'];
                        $smaxage = $rlMaxage['versioned']['server'];
                }
-               if ( $context->getOnly() === 'styles' ) {
+               if ( $context->getImageObj() ) {
+                       // Output different headers if we're outputting textual errors.
+                       if ( $errors ) {
+                               header( 'Content-Type: text/plain; charset=utf-8' );
+                       } else {
+                               $context->getImageObj()->sendResponseHeaders( $context );
+                       }
+               } elseif ( $context->getOnly() === 'styles' ) {
                        header( 'Content-Type: text/css; charset=utf-8' );
                        header( 'Access-Control-Allow-Origin: *' );
                } else {
@@ -823,15 +837,26 @@ class ResourceLoader {
         * Handle exception display.
         *
         * @param Exception $e Exception to be shown to the user
-        * @return string Sanitized text that can be returned to the user
+        * @return string Sanitized text in a CSS/JS comment that can be returned to the user
         */
        public static function formatException( $e ) {
+               return self::makeComment( self::formatExceptionNoComment( $e ) );
+       }
+
+       /**
+        * Handle exception display.
+        *
+        * @since 1.25
+        * @param Exception $e Exception to be shown to the user
+        * @return string Sanitized text that can be returned to the user
+        */
+       protected static function formatExceptionNoComment( $e ) {
                global $wgShowExceptionDetails;
 
                if ( $wgShowExceptionDetails ) {
-                       return self::makeComment( $e->__toString() );
+                       return $e->__toString();
                } else {
-                       return self::makeComment( wfMessage( 'internalerror' )->text() );
+                       return wfMessage( 'internalerror' )->text();
                }
        }
 
@@ -847,7 +872,6 @@ class ResourceLoader {
                array $modules, array $missing = array()
        ) {
                $out = '';
-               $exceptions = '';
                $states = array();
 
                if ( !count( $modules ) && !count( $missing ) ) {
@@ -858,6 +882,17 @@ class ResourceLoader {
 
                wfProfileIn( __METHOD__ );
 
+               $image = $context->getImageObj();
+               if ( $image ) {
+                       $data = $image->getImageData( $context );
+                       if ( $data === false ) {
+                               $data = '';
+                               $this->errors[] = 'Image generation failed';
+                       }
+                       wfProfileOut( __METHOD__ );
+                       return $data;
+               }
+
                // Pre-fetch blobs
                if ( $context->shouldIncludeMessages() ) {
                        try {
@@ -868,9 +903,7 @@ class ResourceLoader {
                                        'resourceloader',
                                        __METHOD__ . ": pre-fetching blobs from MessageBlobStore failed: $e"
                                );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $exceptions .= self::formatException( $e );
+                               $this->errors[] = self::formatExceptionNoComment( $e );
                        }
                } else {
                        $blobs = array();
@@ -994,9 +1027,7 @@ class ResourceLoader {
                        } catch ( Exception $e ) {
                                MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": generating module package failed: $e" );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $exceptions .= self::formatException( $e );
+                               $this->errors[] = self::formatExceptionNoComment( $e );
 
                                // Respond to client with error-state instead of module implementation
                                $states[$name] = 'error';
@@ -1022,9 +1053,8 @@ class ResourceLoader {
                        }
                } else {
                        if ( count( $states ) ) {
-                               $exceptions .= self::makeComment(
-                                       'Problematic modules: ' . FormatJson::encode( $states, ResourceLoader::inDebugMode() )
-                               );
+                               $this->errors[] = 'Problematic modules: ' .
+                                       FormatJson::encode( $states, ResourceLoader::inDebugMode() );
                        }
                }
 
@@ -1037,7 +1067,7 @@ class ResourceLoader {
                }
 
                wfProfileOut( __METHOD__ );
-               return $exceptions . $out;
+               return $out;
        }
 
        /* Static Methods */
@@ -1188,6 +1218,27 @@ class ResourceLoader {
                );
        }
 
+       /**
+        * Remove empty values from the end of an array.
+        *
+        * Values considered empty:
+        *
+        * - null
+        * - empty array
+        *
+        * @param Array $array
+        */
+       private static function trimArray( Array &$array ) {
+               $i = count( $array );
+               while ( $i-- ) {
+                       if ( $array[$i] === null || $array[$i] === array() ) {
+                               unset( $array[$i] );
+                       } else {
+                               break;
+                       }
+               }
+       }
+
        /**
         * Returns JS code which calls mw.loader.register with the given
         * parameters. Has three calling conventions:
@@ -1219,16 +1270,37 @@ class ResourceLoader {
                $dependencies = null, $group = null, $source = null, $skip = null
        ) {
                if ( is_array( $name ) ) {
+                       // Build module name index
+                       $index = array();
+                       foreach ( $name as $i => &$module ) {
+                               $index[$module[0]] = $i;
+                       }
+
+                       // Transform dependency names into indexes when possible, they will be resolved by
+                       // mw.loader.register on the other end
+                       foreach ( $name as &$module ) {
+                               if ( isset( $module[2] ) ) {
+                                       foreach ( $module[2] as &$dependency ) {
+                                               if ( isset( $index[$dependency] ) ) {
+                                                       $dependency = $index[$dependency];
+                                               }
+                                       }
+                               }
+                       }
+
+                       array_walk( $name, array( 'self', 'trimArray' ) );
+
                        return Xml::encodeJsCall(
                                'mw.loader.register',
                                array( $name ),
                                ResourceLoader::inDebugMode()
                        );
                } else {
-                       $version = (int)$version > 1 ? (int)$version : 1;
+                       $registration = array( $name, $version, $dependencies, $group, $source, $skip );
+                       self::trimArray( $registration );
                        return Xml::encodeJsCall(
                                'mw.loader.register',
-                               array( $name, $version, $dependencies, $group, $source, $skip ),
+                               $registration,
                                ResourceLoader::inDebugMode()
                        );
                }
index 02744a6..a6a7d34 100644 (file)
@@ -41,7 +41,11 @@ class ResourceLoaderContext {
        protected $version;
        protected $hash;
        protected $raw;
+       protected $image;
+       protected $variant;
+       protected $format;
        protected $userObj;
+       protected $imageObj;
 
        /* Methods */
 
@@ -66,6 +70,10 @@ class ResourceLoaderContext {
                $this->only = $request->getVal( 'only' );
                $this->version = $request->getVal( 'version' );
                $this->raw = $request->getFuzzyBool( 'raw' );
+               // Image requests
+               $this->image = $request->getVal( 'image' );
+               $this->variant = $request->getVal( 'variant' );
+               $this->format = $request->getVal( 'format' );
 
                $skinnames = Skin::getSkinNames();
                // If no skin is specified, or we don't recognize the skin, use the default skin
@@ -232,6 +240,62 @@ class ResourceLoaderContext {
                return $this->raw;
        }
 
+       /**
+        * @return string|null
+        */
+       public function getImage() {
+               return $this->image;
+       }
+
+       /**
+        * @return string|null
+        */
+       public function getVariant() {
+               return $this->variant;
+       }
+
+       /**
+        * @return string|null
+        */
+       public function getFormat() {
+               return $this->format;
+       }
+
+       /**
+        * If this is a request for an image, get the ResourceLoaderImage object.
+        *
+        * @since 1.25
+        * @return ResourceLoaderImage|bool false if a valid object cannot be created
+        */
+       public function getImageObj() {
+               if ( $this->imageObj === null ) {
+                       $this->imageObj = false;
+
+                       if ( !$this->image ) {
+                               return $this->imageObj;
+                       }
+
+                       $modules = $this->getModules();
+                       if ( count( $modules ) !== 1 ) {
+                               return $this->imageObj;
+                       }
+
+                       $module = $this->getResourceLoader()->getModule( $modules[0] );
+                       if ( !$module || !$module instanceof ResourceLoaderImageModule ) {
+                               return $this->imageObj;
+                       }
+
+                       $image = $module->getImage( $this->image );
+                       if ( !$image ) {
+                               return $this->imageObj;
+                       }
+
+                       $this->imageObj = $image;
+               }
+
+               return $this->imageObj;
+       }
+
        /**
         * @return bool
         */
@@ -260,6 +324,7 @@ class ResourceLoaderContext {
                if ( !isset( $this->hash ) ) {
                        $this->hash = implode( '|', array(
                                $this->getLanguage(), $this->getDirection(), $this->getSkin(), $this->getUser(),
+                               $this->getImage(), $this->getVariant(), $this->getFormat(),
                                $this->getDebug(), $this->getOnly(), $this->getVersion()
                        ) );
                }
diff --git a/includes/resourceloader/ResourceLoaderImage.php b/includes/resourceloader/ResourceLoaderImage.php
new file mode 100644 (file)
index 0000000..0e43f65
--- /dev/null
@@ -0,0 +1,356 @@
+<?php
+/**
+ * Class encapsulating an image used in a ResourceLoaderImageModule.
+ *
+ * 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
+ */
+
+/**
+ * Class encapsulating an image used in a ResourceLoaderImageModule.
+ *
+ * @since 1.25
+ */
+class ResourceLoaderImage {
+
+       /**
+        * Map of allowed file extensions to their MIME types.
+        * @var array
+        */
+       protected static $fileTypes = array(
+               'svg' => 'image/svg+xml',
+               'png' => 'image/png',
+               'gif' => 'image/gif',
+               'jpg' => 'image/jpg',
+       );
+
+       /**
+        * @param string $name Image name
+        * @param string $module Module name
+        * @param string|array $descriptor Path to image file, or array structure containing paths
+        * @param string $basePath Directory to which paths in descriptor refer
+        * @param array $variants
+        * @throws MWException
+        */
+       public function __construct( $name, $module, $descriptor, $basePath, $variants ) {
+               $this->name = $name;
+               $this->module = $module;
+               $this->descriptor = $descriptor;
+               $this->basePath = $basePath;
+               $this->variants = $variants;
+
+               // Ensure that all files have common extension.
+               $extensions = array();
+               $descriptor = (array)$descriptor;
+               array_walk_recursive( $descriptor, function ( $path ) use ( &$extensions ) {
+                       $extensions[] = pathinfo( $path, PATHINFO_EXTENSION );
+               } );
+               $extensions = array_unique( $extensions );
+               if ( count( $extensions ) !== 1 ) {
+                       throw new MWException( 'Image type for various images differs.' );
+               }
+               $ext = $extensions[0];
+               if ( !isset( self::$fileTypes[$ext] ) ) {
+                       throw new MWException( 'Invalid image type; svg, png, gif or jpg required.' );
+               }
+               $this->extension = $ext;
+       }
+
+       /**
+        * Get name of this image.
+        *
+        * @return string
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        * Get name of the module this image belongs to.
+        *
+        * @return string
+        */
+       public function getModule() {
+               return $this->module;
+       }
+
+       /**
+        * Get the list of variants this image can be converted to.
+        *
+        * @return string[]
+        */
+       public function getVariants() {
+               return array_keys( $this->variants );
+       }
+
+       /**
+        * Get the path to image file for given context.
+        *
+        * @param ResourceLoaderContext $context Any context
+        * @return string
+        */
+       protected function getPath( ResourceLoaderContext $context ) {
+               $desc = $this->descriptor;
+               if ( is_string( $desc ) ) {
+                       return $this->basePath . '/' . $desc;
+               } elseif ( isset( $desc['lang'][ $context->getLanguage() ] ) ) {
+                       return $this->basePath . '/' . $desc['lang'][ $context->getLanguage() ];
+               } elseif ( isset( $desc[ $context->getDirection() ] ) ) {
+                       return $this->basePath . '/' . $desc[ $context->getDirection() ];
+               } else {
+                       return $this->basePath . '/' . $desc['default'];
+               }
+       }
+
+       /**
+        * Get the extension of the image.
+        *
+        * @param string $format Format to get the extension for, 'original' or 'rasterized'
+        * @return string Extension without leading dot, e.g. 'png'
+        */
+       public function getExtension( $format = 'original' ) {
+               if ( $format === 'rasterized' && $this->extension === 'svg' ) {
+                       return 'png';
+               } else {
+                       return $this->extension;
+               }
+       }
+
+       /**
+        * Get the MIME type of the image.
+        *
+        * @param string $format Format to get the MIME type for, 'original' or 'rasterized'
+        * @return string
+        */
+       public function getMimeType( $format = 'original' ) {
+               $ext = $this->getExtension( $format );
+               return self::$fileTypes[$ext];
+       }
+
+       /**
+        * Get the load.php URL that will produce this image.
+        *
+        * @param ResourceLoaderContext $context Any context
+        * @param string $script URL to load.php
+        * @param string|null $variant Variant to get the URL for
+        * @param string $format Format to get the URL for, 'original' or 'rasterized'
+        * @return string
+        */
+       public function getUrl( ResourceLoaderContext $context, $script, $variant, $format ) {
+               $query = array(
+                       'modules' => $this->getModule(),
+                       'image' => $this->getName(),
+                       'variant' => $variant,
+                       'format' => $format,
+                       'lang' => $context->getLanguage(),
+                       'version' => $context->getVersion(),
+               );
+
+               return wfExpandUrl( wfAppendQuery( $script, $query ), PROTO_RELATIVE );
+       }
+
+       /**
+        * Get the data: URI that will produce this image.
+        *
+        * @param ResourceLoaderContext $context Any context
+        * @param string|null $variant Variant to get the URI for
+        * @param string $format Format to get the URI for, 'original' or 'rasterized'
+        * @return string
+        */
+       public function getDataUri( ResourceLoaderContext $context, $variant, $format ) {
+               $type = $this->getMimeType( $format );
+               $contents = $this->getImageData( $context, $variant, $format );
+               return CSSMin::encodeStringAsDataURI( $contents, $type );
+       }
+
+       /**
+        * Get actual image data for this image. This can be saved to a file or sent to the browser to
+        * produce the converted image.
+        *
+        * Call getExtension() or getMimeType() with the same $format argument to learn what file type the
+        * returned data uses.
+        *
+        * @param ResourceLoaderContext $context Image context, or any context of $variant and $format
+        *     given.
+        * @param string|null $variant Variant to get the data for. Optional, if given, overrides the data
+        *     from $context.
+        * @param string $format Format to get the data for, 'original' or 'rasterized'. Optional, if
+        *     given, overrides the data from $context.
+        * @return string|false Possibly binary image data, or false on failure
+        */
+       public function getImageData( ResourceLoaderContext $context, $variant = false, $format = false ) {
+               if ( $variant === false ) {
+                       $variant = $context->getVariant();
+               }
+               if ( $format === false ) {
+                       $format = $context->getFormat();
+               }
+
+               if ( $this->getExtension() !== 'svg' ) {
+                       return file_get_contents( $this->getPath( $context ) );
+               }
+
+               if ( $variant && isset( $this->variants[$variant] ) ) {
+                       $data = $this->variantize( $this->variants[$variant], $context );
+               } else {
+                       $data = file_get_contents( $this->getPath( $context ) );
+               }
+
+               if ( $format === 'rasterized' ) {
+                       $data = $this->rasterize( $data );
+               }
+
+               return $data;
+       }
+
+       /**
+        * Send response headers (using the header() function) that are necessary to correctly serve the
+        * image data for this image, as returned by getImageData().
+        *
+        * Note that the headers are independent of the language or image variant.
+        *
+        * @param ResourceLoaderContext $context Image context
+        */
+       public function sendResponseHeaders( ResourceLoaderContext $context ) {
+               $format = $context->getFormat();
+               $mime = $this->getMimeType( $format );
+               $filename = $this->getName() . '.' . $this->getExtension( $format );
+
+               header( 'Content-Type: ' . $mime );
+               header( 'Content-Disposition: ' .
+                       FileBackend::makeContentDisposition( 'inline', $filename ) );
+       }
+
+       /**
+        * Convert this image, which is assumed to be SVG, to given variant.
+        *
+        * @param array $variantConf Array with a 'color' key, its value will be used as fill color
+        * @param ResourceLoaderContext $context Image context
+        * @return string New SVG file data
+        */
+       protected function variantize( $variantConf, ResourceLoaderContext $context ) {
+               $dom = new DomDocument;
+               $dom->load( $this->getPath( $context ) );
+               $root = $dom->documentElement;
+               $wrapper = $dom->createElement( 'g' );
+               while ( $root->firstChild ) {
+                       $wrapper->appendChild( $root->firstChild );
+               }
+               $root->appendChild( $wrapper );
+               $wrapper->setAttribute( 'fill', $variantConf['color'] );
+               return $dom->saveXml();
+       }
+
+       /**
+        * Massage the SVG image data for converters which doesn't understand some path data syntax.
+        *
+        * This is necessary for rsvg and ImageMagick when compiled with rsvg support.
+        * Upstream bug is https://bugzilla.gnome.org/show_bug.cgi?id=620923, fixed 2014-11-10, so
+        * this will be needed for a while. (T76852)
+        *
+        * @param string $svg SVG image data
+        * @return string Massaged SVG image data
+        */
+       protected function massageSvgPathdata( $svg ) {
+               $dom = new DomDocument;
+               $dom->loadXml( $svg );
+               foreach ( $dom->getElementsByTagName( 'path' ) as $node ) {
+                       $pathData = $node->getAttribute( 'd' );
+                       // Make sure there is at least one space between numbers, and that leading zero is not omitted.
+                       // rsvg has issues with syntax like "M-1-2" and "M.445.483" and especially "M-.445-.483".
+                       $pathData = preg_replace( '/(-?)(\d*\.\d+|\d+)/', ' ${1}0$2 ', $pathData );
+                       // Strip unnecessary leading zeroes for prettiness, not strictly necessary
+                       $pathData = preg_replace( '/([ -])0(\d)/', '$1$2', $pathData );
+                       $node->setAttribute( 'd', $pathData );
+               }
+               return $dom->saveXml();
+       }
+
+       /**
+        * Convert passed image data, which is assumed to be SVG, to PNG.
+        *
+        * @param string $svg SVG image data
+        * @return string|bool PNG image data, or false on failure
+        */
+       protected function rasterize( $svg ) {
+               // This code should be factored out to a separate method on SvgHandler, or perhaps a separate
+               // class, with a separate set of configuration settings.
+               //
+               // This is a distinct use case from regular SVG rasterization:
+               // * we can skip many sanity and security checks (as the images come from a trusted source,
+               //   rather than from the user)
+               // * we need to provide extra options to some converters to achieve acceptable quality for very
+               //   small images, which might cause performance issues in the general case
+               // * we need to directly pass image data to the converter instead of a file path
+               //
+               // See https://phabricator.wikimedia.org/T76473#801446 for examples of what happens with the
+               // default settings.
+               //
+               // For now, we special-case rsvg (used in WMF production) and do a messy workaround for other
+               // converters.
+
+               global $wgSVGConverter, $wgSVGConverterPath;
+
+               $svg = $this->massageSvgPathdata( $svg );
+
+               if ( $wgSVGConverter === 'rsvg' ) {
+                       $command = 'rsvg-convert'; // Should be just 'rsvg'? T76476
+                       if ( $wgSVGConverterPath ) {
+                               $command = wfEscapeShellArg( "$wgSVGConverterPath/" ) . $command;
+                       }
+
+                       $process = proc_open(
+                               $command,
+                               array( 0 => array( 'pipe', 'r' ), 1 => array( 'pipe', 'w' ) ),
+                               $pipes
+                       );
+
+                       if ( is_resource( $process ) ) {
+                               fwrite( $pipes[0], $svg );
+                               fclose( $pipes[0] );
+                               $png = stream_get_contents( $pipes[1] );
+                               fclose( $pipes[1] );
+                               proc_close( $process );
+
+                               return $png ?: false;
+                       }
+                       return false;
+
+               } else {
+                       // Write input to and read output from a temporary file
+                       $tempFilenameSvg = tempnam( wfTempDir(), 'ResourceLoaderImage' );
+                       $tempFilenamePng = tempnam( wfTempDir(), 'ResourceLoaderImage' );
+
+                       file_put_contents( $tempFilenameSvg, $svg );
+
+                       $metadata = SVGMetadataExtractor::getMetadata( $tempFilenameSvg );
+                       if ( !isset( $metadata['width'] ) || !isset( $metadata['height'] ) ) {
+                               return false;
+                       }
+
+                       $handler = new SvgHandler;
+                       $handler->rasterize( $tempFilenameSvg, $tempFilenamePng, $metadata['width'], $metadata['height'] );
+
+                       $png = file_get_contents( $tempFilenamePng );
+
+                       unlink( $tempFilenameSvg );
+                       unlink( $tempFilenamePng );
+
+                       return $png ?: false;
+               }
+       }
+}
diff --git a/includes/resourceloader/ResourceLoaderImageModule.php b/includes/resourceloader/ResourceLoaderImageModule.php
new file mode 100644 (file)
index 0000000..2ea3c06
--- /dev/null
@@ -0,0 +1,294 @@
+<?php
+/**
+ * Resource loader module for generated and embedded images.
+ *
+ * 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 Trevor Parscal
+ */
+
+/**
+ * Resource loader module for generated and embedded images.
+ *
+ * @since 1.25
+ */
+class ResourceLoaderImageModule extends ResourceLoaderModule {
+
+       /**
+        * Local base path, see __construct()
+        * @var string
+        */
+       protected $localBasePath = '';
+
+       protected $origin = self::ORIGIN_CORE_SITEWIDE;
+
+       protected $images = array();
+       protected $variants = array();
+       protected $prefix = array();
+
+       /**
+        * Constructs a new module from an options array.
+        *
+        * @param array $options List of options; if not given or empty, an empty module will be
+        *     constructed
+        * @param string $localBasePath Base path to prepend to all local paths in $options. Defaults
+        *     to $IP
+        *
+        * Below is a description for the $options array:
+        * @par Construction options:
+        * @code
+        *     array(
+        *         // Base path to prepend to all local paths in $options. Defaults to $IP
+        *         'localBasePath' => [base path],
+        *         // CSS class prefix to use in all style rules
+        *         'prefix' => [CSS class prefix],
+        *         // List of variants that may be used for the image files
+        *         'variants' => array(
+        *             // ([image type] is a string, used in generated CSS class names and to match variants to images)
+        *             [image type] => array(
+        *                 [variant name] => array(
+        *                     'color' => [color string, e.g. '#ffff00'],
+        *                     'global' => [boolean, if true, this variant is available for all images of this type],
+        *                 ),
+        *             )
+        *         ),
+        *         // List of image files and their options
+        *         'images' => array(
+        *             [image type] => array(
+        *                 [file path string],
+        *                 [file path string] => array(
+        *                     'name' => [image name string, defaults to file name],
+        *                     'variants' => [array of variant name strings, variants available for this image],
+        *                 ),
+        *             )
+        *         ),
+        *     )
+        * @endcode
+        * @throws MWException
+        */
+       public function __construct( $options = array(), $localBasePath = null ) {
+               $this->localBasePath = self::extractLocalBasePath( $options, $localBasePath );
+
+               if ( !isset( $options['prefix'] ) || !$options['prefix'] ) {
+                       throw new MWException(
+                               "Required 'prefix' option not given or empty."
+                       );
+               }
+
+               foreach ( $options as $member => $option ) {
+                       switch ( $member ) {
+                               case 'images':
+                                       if ( !is_array( $option ) ) {
+                                               throw new MWException(
+                                                       "Invalid collated file path list error. '$option' given, array expected."
+                                               );
+                                       }
+                                       foreach ( $option as $key => $value ) {
+                                               if ( !is_string( $key ) ) {
+                                                       throw new MWException(
+                                                               "Invalid collated file path list key error. '$key' given, string expected."
+                                                       );
+                                               }
+                                               $this->{$member}[$key] = (array)$value;
+                                       }
+                                       break;
+
+                               case 'variants':
+                                       if ( !is_array( $option ) ) {
+                                               throw new MWException(
+                                                       "Invalid variant list error. '$option' given, array expected."
+                                               );
+                                       }
+                                       $this->{$member} = $option;
+                                       break;
+
+                               case 'prefix':
+                                       $this->{$member} = (string)$option;
+                                       break;
+                       }
+               }
+       }
+
+       /**
+        * Get CSS class prefix used by this module.
+        * @return string
+        */
+       public function getPrefix() {
+               return $this->prefix;
+       }
+
+       /**
+        * Get a ResourceLoaderImage object for given image.
+        * @param string $name Image name
+        * @return ResourceLoaderImage|null
+        */
+       public function getImage( $name ) {
+               $images = $this->getImages();
+               return isset( $images[$name] ) ? $images[$name] : null;
+       }
+
+       /**
+        * Get ResourceLoaderImage objects for all images.
+        * @return ResourceLoaderImage[] Array keyed by image name
+        */
+       public function getImages() {
+               if ( !isset( $this->imageObjects ) ) {
+                       $this->imageObjects = array();
+
+                       foreach ( $this->images as $type => $list ) {
+                               foreach ( $list as $name => $options ) {
+                                       $imageDesc = is_string( $options ) ? $options : $options['image'];
+
+                                       $allowedVariants = array_merge(
+                                               isset( $options['variants'] ) ? $options['variants'] : array(),
+                                               $this->getGlobalVariants( $type )
+                                       );
+                                       $variantConfig = array_intersect_key(
+                                               $this->variants[$type],
+                                               array_fill_keys( $allowedVariants, true )
+                                       );
+
+                                       $image = new ResourceLoaderImage( $name, $this->getName(), $imageDesc, $this->localBasePath, $variantConfig );
+                                       $this->imageObjects[ $image->getName() ] = $image;
+                               }
+                       }
+               }
+
+               return $this->imageObjects;
+       }
+
+       /**
+        * Get list of variants in this module that are 'global' for given type of images, i.e., available
+        * for every image of given type regardless of image options.
+        * @param string $type Image type
+        * @return string[]
+        */
+       public function getGlobalVariants( $type ) {
+               if ( !isset( $this->globalVariants[$type] ) ) {
+                       $this->globalVariants[$type] = array();
+
+                       foreach ( $this->variants[$type] as $name => $config ) {
+                               if ( isset( $config['global'] ) && $config['global'] ) {
+                                       $this->globalVariants[$type][] = $name;
+                               }
+                       }
+               }
+
+               return $this->globalVariants[$type];
+       }
+
+       /**
+        * Get the type of given image.
+        * @param string $imageName Image name
+        * @return string
+        */
+       public function getImageType( $imageName ) {
+               foreach ( $this->images as $type => $list ) {
+                       foreach ( $list as $key => $value ) {
+                               $file = is_int( $key ) ? $value : $key;
+                               $options = is_array( $value ) ? $value : array();
+                               $name = isset( $options['name'] ) ? $options['name'] : pathinfo( $file, PATHINFO_FILENAME );
+                               if ( $name === $imageName ) {
+                                       return $type;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @return array
+        */
+       public function getStyles( ResourceLoaderContext $context ) {
+               // Build CSS rules
+               $rules = array();
+               $script = $context->getResourceLoader()->getLoadScript( $this->getSource() );
+               $prefix = $this->getPrefix();
+
+               foreach ( $this->getImages() as $name => $image ) {
+                       $type = $this->getImageType( $name );
+
+                       $declarations = $this->getCssDeclarations(
+                               $image->getDataUri( $context, null, 'original' ),
+                               $image->getUrl( $context, $script, null, 'rasterized' )
+                       );
+                       $declarations = implode( "\n\t", $declarations );
+                       $rules[] = ".$prefix-$type-$name {\n\t$declarations\n}";
+
+                       // TODO: Get variant configurations from $context->getSkin()
+                       foreach ( $image->getVariants() as $variant ) {
+                               $declarations = $this->getCssDeclarations(
+                                       $image->getDataUri( $context, $variant, 'original' ),
+                                       $image->getUrl( $context, $script, $variant, 'rasterized' )
+                               );
+                               $declarations = implode( "\n\t", $declarations );
+                               $rules[] = ".$prefix-$type-$name-$variant {\n\t$declarations\n}";
+                       }
+               }
+
+               $style = implode( "\n", $rules );
+               if ( $this->getFlip( $context ) ) {
+                       $style = CSSJanus::transform( $style, true, false );
+               }
+               return array( 'all' => $style );
+       }
+
+       /**
+        * @param string $primary Primary URI
+        * @param string $fallback Fallback URI
+        * @return string[] CSS declarations to use given URIs as background-image
+        */
+       protected function getCssDeclarations( $primary, $fallback ) {
+               // SVG support using a transparent gradient to guarantee cross-browser
+               // compatibility (browsers able to understand gradient syntax support also SVG).
+               // http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique
+               return array(
+                       "background-image: url($fallback);",
+                       "background-image: -webkit-linear-gradient(transparent, transparent), url($primary);",
+                       "background-image: linear-gradient(transparent, transparent), url($primary);",
+               );
+       }
+
+       /**
+        * @return bool
+        */
+       public function supportsURLLoading() {
+               return false;
+       }
+
+       /**
+        * Extract a local base path from module definition information.
+        *
+        * @param array $options Module definition
+        * @param string $localBasePath Path to use if not provided in module definition. Defaults
+        *     to $IP
+        * @return string Local base path
+        */
+       public static function extractLocalBasePath( $options, $localBasePath = null ) {
+               global $IP;
+
+               if ( $localBasePath === null ) {
+                       $localBasePath = $IP;
+               }
+
+               if ( array_key_exists( 'localBasePath', $options ) ) {
+                       $localBasePath = (string)$options['localBasePath'];
+               }
+
+               return $localBasePath;
+       }
+}
index 4c49fae..3f95ce6 100644 (file)
@@ -388,12 +388,12 @@ abstract class ResourceLoaderModule {
         * Get the last modification timestamp of the message blob for this
         * module in a given language.
         * @param string $lang Language code
-        * @return int UNIX timestamp, or 0 if the module doesn't have messages
+        * @return int UNIX timestamp
         */
        public function getMsgBlobMtime( $lang ) {
                if ( !isset( $this->msgBlobMtime[$lang] ) ) {
                        if ( !count( $this->getMessages() ) ) {
-                               return 0;
+                               return 1;
                        }
 
                        $dbr = wfGetDB( DB_SLAVE );
@@ -416,7 +416,7 @@ abstract class ResourceLoaderModule {
         * Set a preloaded message blob last modification timestamp. Used so we
         * can load this information for all modules at once.
         * @param string $lang Language code
-        * @param int $mtime UNIX timestamp or 0 if there is no such blob
+        * @param int $mtime UNIX timestamp
         */
        public function setMsgBlobMtime( $lang, $mtime ) {
                $this->msgBlobMtime[$lang] = $mtime;
@@ -443,7 +443,6 @@ abstract class ResourceLoaderModule {
         * @return int UNIX timestamp
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
-               // 0 would mean now
                return 1;
        }
 
@@ -451,13 +450,12 @@ abstract class ResourceLoaderModule {
         * Helper method for calculating when the module's hash (if it has one) changed.
         *
         * @param ResourceLoaderContext $context
-        * @return int UNIX timestamp or 0 if no hash was provided
-        *  by getModifiedHash()
+        * @return int UNIX timestamp
         */
        public function getHashMtime( ResourceLoaderContext $context ) {
                $hash = $this->getModifiedHash( $context );
                if ( !is_string( $hash ) ) {
-                       return 0;
+                       return 1;
                }
 
                $cache = wfGetCache( CACHE_ANYTHING );
@@ -469,7 +467,7 @@ abstract class ResourceLoaderModule {
                        return $data['timestamp'];
                }
 
-               $timestamp = wfTimestamp();
+               $timestamp = time();
                $cache->set( $key, array(
                        'hash' => $hash,
                        'timestamp' => $timestamp,
@@ -497,15 +495,14 @@ abstract class ResourceLoaderModule {
         * @since 1.23
         *
         * @param ResourceLoaderContext $context
-        * @return int UNIX timestamp or 0 if no definition summary was provided
-        *  by getDefinitionSummary()
+        * @return int UNIX timestamp
         */
        public function getDefinitionMtime( ResourceLoaderContext $context ) {
                wfProfileIn( __METHOD__ );
                $summary = $this->getDefinitionSummary( $context );
                if ( $summary === null ) {
                        wfProfileOut( __METHOD__ );
-                       return 0;
+                       return 1;
                }
 
                $hash = md5( json_encode( $summary ) );
@@ -640,16 +637,12 @@ abstract class ResourceLoaderModule {
         * Safe version of filemtime(), which doesn't throw a PHP warning if the file doesn't exist
         * but returns 1 instead.
         * @param string $filename File name
-        * @return int UNIX timestamp, or 1 if the file doesn't exist
+        * @return int UNIX timestamp
         */
        protected static function safeFilemtime( $filename ) {
-               if ( file_exists( $filename ) ) {
-                       return filemtime( $filename );
-               } else {
-                       // We only ever map this function on an array if we're gonna call max() after,
-                       // so return our standard minimum timestamps here. This is 1, not 0, because
-                       // wfTimestamp(0) == NOW
+               if ( !file_exists( $filename ) ) {
                        return 1;
                }
+               return filemtime( $filename );
        }
 }
index 6de1be0..9835932 100644 (file)
@@ -41,7 +41,7 @@ class ResourceLoaderSkinModule extends ResourceLoaderFileModule {
 
        /**
         * @param $context ResourceLoaderContext
-        * @return boolean
+        * @return bool
         */
        public function isKnownEmpty( ResourceLoaderContext $context ) {
                // Regardless of whether the files are specified, we always
index ee66288..2eccf81 100644 (file)
@@ -147,7 +147,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
        }
 
        /**
-        * Optimize the dependency tree in $this->modules and return it.
+        * Optimize the dependency tree in $this->modules.
         *
         * The optimization basically works like this:
         *      Given we have module A with the dependencies B and C
@@ -155,11 +155,11 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
         *      Now we don't have to tell the client to explicitly fetch module
         *              C as that's already included in module B.
         *
-        * This way we can reasonably reduce the amout of module registration
+        * This way we can reasonably reduce the amount of module registration
         * data send to the client.
         *
         * @param array &$registryData Modules keyed by name with properties:
-        *  - string 'version'
+        *  - number 'version'
         *  - array 'dependencies'
         *  - string|null 'group'
         *  - string 'source'
@@ -211,12 +211,10 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                                continue;
                        }
 
-                       // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always
-                       // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX
+                       // Coerce module timestamp to UNIX timestamp.
+                       // getModifiedTime() is supposed to return a UNIX timestamp, but custom implementations
+                       // might forget. TODO: Maybe emit warning?
                        $moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) );
-                       $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $this->getConfig()->get( 'CacheEpoch' ) ) );
-
-                       // FIXME: Convert to numbers, wfTimestamp always gives us stings, even for TS_UNIX
 
                        $skipFunction = $module->getSkipFunction();
                        if ( $skipFunction !== null && !ResourceLoader::inDebugMode() ) {
@@ -229,8 +227,14 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                                );
                        }
 
+                       $mtime = max(
+                               $moduleMtime,
+                               wfTimestamp( TS_UNIX, $this->getConfig()->get( 'CacheEpoch' ) )
+                       );
+
                        $registryData[$name] = array(
-                               'version' => $mtime,
+                               // Convert to numbers as wfTimestamp always returns a string, even for TS_UNIX
+                               'version' => (int) $mtime,
                                'dependencies' => $module->getDependencies(),
                                'group' => $module->getGroup(),
                                'source' => $module->getSource(),
@@ -251,7 +255,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        if ( $data['loader'] !== false ) {
                                $out .= ResourceLoader::makeCustomLoaderScript(
                                        $name,
-                                       wfTimestamp( TS_ISO_8601_BASIC, $data['version'] ),
+                                       $data['version'],
                                        $data['dependencies'],
                                        $data['group'],
                                        $data['source'],
@@ -260,57 +264,16 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                                continue;
                        }
 
-                       if (
-                               !count( $data['dependencies'] ) &&
-                               $data['group'] === null &&
-                               $data['source'] === 'local' &&
-                               $data['skip'] === null
-                       ) {
-                               // Modules with no dependencies, group, foreign source or skip function;
-                               // call mw.loader.register(name, timestamp)
-                               $registrations[] = array( $name, $data['version'] );
-                       } elseif (
-                               $data['group'] === null &&
-                               $data['source'] === 'local' &&
-                               $data['skip'] === null
-                       ) {
-                               // Modules with dependencies but no group, foreign source or skip function;
-                               // call mw.loader.register(name, timestamp, dependencies)
-                               $registrations[] = array( $name, $data['version'], $data['dependencies'] );
-                       } elseif (
-                               $data['source'] === 'local' &&
-                               $data['skip'] === null
-                       ) {
-                               // Modules with a group but no foreign source or skip function;
-                               // call mw.loader.register(name, timestamp, dependencies, group)
-                               $registrations[] = array(
-                                       $name,
-                                       $data['version'],
-                                       $data['dependencies'],
-                                       $data['group']
-                               );
-                       } elseif ( $data['skip'] === null ) {
-                               // Modules with a foreign source but no skip function;
-                               // call mw.loader.register(name, timestamp, dependencies, group, source)
-                               $registrations[] = array(
-                                       $name,
-                                       $data['version'],
-                                       $data['dependencies'],
-                                       $data['group'],
-                                       $data['source']
-                               );
-                       } else {
-                               // Modules with a skip function;
-                               // call mw.loader.register(name, timestamp, dependencies, group, source, skip)
-                               $registrations[] = array(
-                                       $name,
-                                       $data['version'],
-                                       $data['dependencies'],
-                                       $data['group'],
-                                       $data['source'],
-                                       $data['skip']
-                               );
-                       }
+                       // Call mw.loader.register(name, timestamp, dependencies, group, source, skip)
+                       $registrations[] = array(
+                               $name,
+                               $data['version'],
+                               $data['dependencies'],
+                               $data['group'],
+                               // Swap default (local) for null
+                               $data['source'] === 'local' ? null : $data['source'],
+                               $data['skip']
+                       );
                }
 
                // Register modules
@@ -352,7 +315,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
 
                // Get the latest version
                $loader = $context->getResourceLoader();
-               $version = 0;
+               $version = 1;
                foreach ( $moduleNames as $moduleName ) {
                        $version = max( $version,
                                $loader->getModule( $moduleName )->getModifiedTime( $context )
index 9d97eea..472ceb2 100644 (file)
@@ -90,11 +90,4 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule {
        public function getGroup() {
                return 'private';
        }
-
-       /**
-        * @return array
-        */
-       public function getDependencies() {
-               return array( 'mediawiki.user' );
-       }
 }
diff --git a/includes/resourceloader/ResourceLoaderUserDefaultsModule.php b/includes/resourceloader/ResourceLoaderUserDefaultsModule.php
new file mode 100644 (file)
index 0000000..d78fa9d
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Resource loader module for default user preferences.
+ *
+ * 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 Ori Livneh
+ */
+
+/**
+ * Module for default user preferences.
+ */
+class ResourceLoaderUserDefaultsModule extends ResourceLoaderModule {
+
+       /* Protected Members */
+
+       protected $targets = array( 'desktop', 'mobile' );
+
+       /* Methods */
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @return string Hash
+        */
+       public function getModifiedHash( ResourceLoaderContext $context ) {
+               return md5( serialize( User::getDefaultOptions() ) );
+       }
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @return int
+        */
+       public function getModifiedTime( ResourceLoaderContext $context ) {
+               return $this->getHashMtime( $context );
+       }
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @return string
+        */
+       public function getScript( ResourceLoaderContext $context ) {
+               return Xml::encodeJsCall( 'mw.user.options.set', array( User::getDefaultOptions() ) );
+       }
+}
index e6b07c1..84c1906 100644 (file)
@@ -37,9 +37,16 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
 
        /* Methods */
 
+       /**
+        * @return array List of module names as strings
+        */
+       public function getDependencies() {
+               return array( 'user.defaults' );
+       }
+
        /**
         * @param ResourceLoaderContext $context
-        * @return array|int|mixed
+        * @return int
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
                $hash = $context->getHash();
@@ -56,7 +63,7 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
         */
        public function getScript( ResourceLoaderContext $context ) {
                return Xml::encodeJsCall( 'mw.user.options.set',
-                       array( $context->getUserObj()->getOptions() ),
+                       array( $context->getUserObj()->getOptions( User::GETOPTIONS_EXCLUDE_DEFAULTS ) ),
                        ResourceLoader::inDebugMode()
                );
        }
index e195cf2..1a1a6d0 100644 (file)
@@ -164,10 +164,10 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
 
        /**
         * @param ResourceLoaderContext $context
-        * @return int|mixed
+        * @return int
         */
        public function getModifiedTime( ResourceLoaderContext $context ) {
-               $modifiedTime = 1; // wfTimestamp() interprets 0 as "now"
+               $modifiedTime = 1;
                $titleInfo = $this->getTitleInfo( $context );
                if ( count( $titleInfo ) ) {
                        $mtimes = array_map( function ( $value ) {
@@ -226,8 +226,8 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
         * Get the modification times of all titles that would be loaded for
         * a given context.
         * @param ResourceLoaderContext $context Context object
-        * @return array keyed by page dbkey, with value is an array with 'length' and 'timestamp'
-        *               keys, where the timestamp is a unix one
+        * @return array Keyed by page dbkey. Value is an array with 'length' and 'timestamp'
+        *               keys, where the timestamp is a UNIX timestamp
         */
        protected function getTitleInfo( ResourceLoaderContext $context ) {
                $dbr = $this->getDB();
index e7aed73..6ae0afc 100644 (file)
@@ -32,7 +32,7 @@ class RevDelArchiveList extends RevDelRevisionList {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @return mixed
         */
        public function doQuery( $db ) {
index aec51b1..f2b99ae 100644 (file)
@@ -32,7 +32,7 @@ class RevDelArchivedFileList extends RevDelFileList {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @return mixed
         */
        public function doQuery( $db ) {
index 57e15d8..2295eaa 100644 (file)
@@ -49,7 +49,7 @@ class RevDelFileList extends RevDelList {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @return mixed
         */
        public function doQuery( $db ) {
index ad04042..86c5af3 100644 (file)
@@ -55,7 +55,7 @@ class RevDelLogList extends RevDelList {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @return mixed
         */
        public function doQuery( $db ) {
index 2545072..733fa97 100644 (file)
@@ -54,7 +54,7 @@ class RevDelRevisionList extends RevDelList {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @return mixed
         */
        public function doQuery( $db ) {
index 55c46c5..79802d6 100644 (file)
@@ -36,14 +36,14 @@ class RevisionDeleteUser {
         * @param string $name Username
         * @param int $userId User id
         * @param string $op Operator '|' or '&'
-        * @param null|DatabaseBase $dbw If you happen to have one lying around
+        * @param null|IDatabase $dbw If you happen to have one lying around
         * @return bool
         */
        private static function setUsernameBitfields( $name, $userId, $op, $dbw ) {
                if ( !$userId || ( $op !== '|' && $op !== '&' ) ) {
                        return false; // sanity check
                }
-               if ( !$dbw instanceof DatabaseBase ) {
+               if ( !$dbw instanceof IDatabase ) {
                        $dbw = wfGetDB( DB_MASTER );
                }
 
index c0ecab1..f2a95a8 100644 (file)
@@ -72,7 +72,7 @@ class SiteListFileCache {
                $sites = new SiteList();
 
                // @todo lazy initialize the site objects in the site list (e.g. only when needed to access)
-               foreach( $data['sites'] as $siteArray ) {
+               foreach ( $data['sites'] as $siteArray ) {
                        $sites[] = $this->newSiteFromArray( $siteArray );
                }
 
@@ -116,7 +116,7 @@ class SiteListFileCache {
                $site->setExtraData( $data['data'] );
                $site->setExtraConfig( $data['config'] );
 
-               foreach( $data['identifiers'] as $identifier ) {
+               foreach ( $data['identifiers'] as $identifier ) {
                        $site->addLocalId( $identifier['type'], $identifier['key'] );
                }
 
index 0a71aae..7307a6f 100644 (file)
@@ -81,7 +81,7 @@ class SiteListFileCacheBuilder {
                $siteIdentifiers = $this->buildLocalIdentifiers( $site );
                $identifiersArray = array();
 
-               foreach( $siteIdentifiers as $identifier ) {
+               foreach ( $siteIdentifiers as $identifier ) {
                        $identifiersArray[] = $identifier;
                }
 
index c28aa86..17a651f 100644 (file)
@@ -317,9 +317,9 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Return a DatabaseBase object for reading
+        * Return a IDatabase object for reading
         *
-        * @return DatabaseBase
+        * @return IDatabase
         */
        protected function getDB() {
                return wfGetDB( DB_SLAVE );
index c837d1a..c4e53ee 100644 (file)
@@ -35,7 +35,7 @@ abstract class ImageQueryPage extends QueryPage {
         *
         * @param OutputPage $out OutputPage to print to
         * @param Skin $skin User skin to use [unused]
-        * @param DatabaseBase $dbr (read) connection to use
+        * @param IDatabase $dbr (read) connection to use
         * @param ResultWrapper $res Result pointer
         * @param int $num Number of available result rows
         * @param int $offset Paging offset
index afc0227..d72744b 100644 (file)
@@ -32,7 +32,7 @@ abstract class PageQueryPage extends QueryPage {
         * like page existence and information for stub color and redirect hints.
         * This should be done for live data and cached data.
         *
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @param ResultWrapper $res
         */
        public function preprocessResults( $db, $res ) {
index 167135b..f902acf 100644 (file)
@@ -346,7 +346,7 @@ abstract class QueryPage extends SpecialPage {
 
        /**
         * Get a DB connection to be used for slow recache queries
-        * @return DatabaseBase
+        * @return IDatabase
         */
        function getRecacheDB() {
                return wfGetDB( DB_SLAVE, array( $this->getName(), 'QueryPage::recache', 'vslow' ) );
@@ -576,7 +576,7 @@ abstract class QueryPage extends SpecialPage {
         *
         * @param OutputPage $out OutputPage to print to
         * @param Skin $skin User skin to use
-        * @param DatabaseBase $dbr Database (read) connection to use
+        * @param IDatabase $dbr Database (read) connection to use
         * @param ResultWrapper $res Result pointer
         * @param int $num Number of available result rows
         * @param int $offset Paging offset
@@ -649,7 +649,7 @@ abstract class QueryPage extends SpecialPage {
 
        /**
         * Do any necessary preprocessing of the result object.
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @param ResultWrapper $res
         */
        function preprocessResults( $db, $res ) {
index be2f1e8..c4140de 100644 (file)
@@ -37,7 +37,7 @@ abstract class WantedQueryPage extends QueryPage {
 
        /**
         * Cache page existence for performance
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @param ResultWrapper $res
         */
        function preprocessResults( $db, $res ) {
@@ -109,7 +109,7 @@ abstract class WantedQueryPage extends QueryPage {
         * @note This will only be run if the page is cached (ie $wgMiserMode = true)
         *   unless forceExistenceCheck() is true.
         * @since 1.24
-        * @return boolean
+        * @return bool
         */
        protected function existenceCheck( Title $title ) {
                return $title->isKnown();
index 1e829b1..583778f 100644 (file)
@@ -672,10 +672,7 @@ class ContribsPager extends ReverseChronologicalPager {
                $msgs = array(
                        'diff',
                        'hist',
-                       'newarticle',
                        'pipe-separator',
-                       'rev-delundel',
-                       'rollbacklink',
                        'uctop'
                );
 
index 16127d9..8a1a6c6 100644 (file)
@@ -99,7 +99,7 @@ class WantedFilesPage extends WantedQueryPage {
         * Use wfFindFile so we still think file namespace pages without
         * files are missing, but valid file redirects and foreign files are ok.
         *
-        * @return boolean
+        * @return bool
         */
        protected function existenceCheck( Title $title ) {
                return (bool)wfFindFile( $title );
index 727f485..4e65e11 100644 (file)
@@ -227,7 +227,7 @@ class ClassCollector {
                if ( is_string( $token ) ) {
                        return;
                }
-               switch( $token[0] ) {
+               switch ( $token[0] ) {
                case T_NAMESPACE:
                case T_CLASS:
                case T_INTERFACE:
@@ -241,7 +241,7 @@ class ClassCollector {
         * @param array
         */
        protected function tryEndExpect( $token ) {
-               switch( $this->startToken[0] ) {
+               switch ( $this->startToken[0] ) {
                case T_NAMESPACE:
                        if ( $token === ';' || $token === '{' ) {
                                $this->namespace = $this->implodeTokens() . '\\';
index 31c8f1a..7a907fc 100644 (file)
@@ -46,7 +46,8 @@
                        "Test Create account",
                        "Kuwaity26",
                        "Calak",
-                       "Omda4wady"
+                       "Omda4wady",
+                       "Bibas"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
@@ -74,7 +75,7 @@
        "tog-shownumberswatching": "اعرض عدد المستخدمين المراقبين",
        "tog-oldsig": "التوقيع الحالي:",
        "tog-fancysig": "وضع الوصلة يدويا واستعمال نص الويكي",
-       "tog-uselivepreview": "استعمال المعاينة المباشرة (تجريبي)",
+       "tog-uselivepreview": "استعمال المعاينة المباشرة",
        "tog-forceeditsummary": "نبهني عند عدم إدخال ملخص تعديل",
        "tog-watchlisthideown": "أخف تعديلاتي من قائمة المراقبة",
        "tog-watchlisthidebots": "أخف تعديلات البوتات من قائمة المراقبة",
        "filerenameerror": "تعذّر تغيير اسم الملف \"$1\" إلى \"$2\".",
        "filedeleteerror": "تعذّر حذف الملف \"$1\".",
        "directorycreateerror": "تعذّر إنشاء الدليل \"$1\".",
+       "directoryreadonlyerror": "المجلد «$1» للقراءة فقط.",
+       "directorynotreadableerror": "المجلد «$1» لا يمكن قراءته.",
        "filenotfound": "تعذّر إيجاد الملف \"$1\".",
        "unexpected": "قيمة غير متوقعة: \"$1\"=\"$2\".",
        "formerror": "عطل: تعذّر إيداع الاستمارة",
        "content-model-text": "نص عادي",
        "content-model-javascript": "جافاسكربت",
        "content-model-css": "CSS",
+       "duplicate-args-category": "صفحات تستعمل قالبا ببيانات مكررة",
        "expensive-parserfunction-warning": "'''تحذير:''' هذه الصفحة تحتوي على استدعاءات دالة محلل كثيرة مكلفة.\n\nينبغي أن تكون أقل من {{PLURAL:$2||استدعاء واحد|استدعاءين|$2 استدعاءات|$2 استدعاء}}، يوجد الآن {{PLURAL:$1|استدعاء واحد|استدعاءان|$2 استدعاءات|$2 استدعاء}}.",
        "expensive-parserfunction-category": "تجاوزات الدوال المكلفة",
        "post-expand-template-inclusion-warning": "'''تحذير:''' حجم تضمين القالب كبير جدا.\nبعض القوالب لن تضمن.",
        "revdelete-show-file-confirm": "هل أنت متأكد أنك تريد رؤية مراجعة محذوفة للملف \"<nowiki>$1</nowiki>\" بتاريخ $2 الساعة $3؟",
        "revdelete-show-file-submit": "نعم",
        "revdelete-selected-text": "{{PLURAL:$1|نسخة مختارة|نسخ مختارة}} ل[[:$2]]:",
+       "revdelete-selected-file": "{{PLURAL:$1|النسخة المختارة من الملف|النسخ المختارة من الملف}} لـ [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|حدث السجل المختار|أحداث السجل المختارة}}:",
+       "revdelete-text-text": "المراجعات المحذوفة ستظل تظهر في تاريخ الصفحة، ولكن أجزاءا من محتواها سيكون محجوبا عن الجميع.",
        "revdelete-text-others": "سيتمكن الإداريون الآخرون على {{SITENAME}} من الوصول إلى المحتوى المخفي وإلغاء حذفه مجددا من خلال ذات الواجهة ما لم تطبق قيود إضافية.",
        "revdelete-confirm": "الإداريون الآخرون في {{SITENAME}} سيظل بإمكانهم رؤية المحتوى المخفي ويمكنهم استرجاعه مجددا من خلال هذه الواجهة نفسها، مالم يتم وضع قيود إضافية.\nمن فضلك أكد أنك تنوي فعل هذا، وأنك تفهم العواقب، وأنك تفعل هذا بالتوافق مع [[{{MediaWiki:Policy-url}}|السياسة]].",
        "revdelete-suppress-text": "ينبغي للإخفاء أن يستخدم '''فقط''' في الحالات التالية:\n* معلومات يحتمل أن تكون تشهيرية\n* معلومات شخصية غير ملائمة\n*: ''عناوين المنازل وأرقام الهواتف وأرقام الهويات الوطنية إلى آخره.''",
        "unwatchedpages": "صفحات غير مراقبة",
        "listredirects": "عرض التحويلات",
        "listduplicatedfiles": "قائمة الملفات مع المكررات",
+       "listduplicatedfiles-entry": "[[:File:$1|$1]] مكرر في [[$3|{{PLURAL:$2||مكان آخر واحد|مكانين آخرين اثنين|$2 أماكن أخرى|$2 مكاناً آخر|$2مكان آخر}}]].",
        "unusedtemplates": "قوالب غير مستخدمة",
        "unusedtemplatestext": "هذه الصفحة تعرض كل الصفحات في نطاق {{ns:template}} غير المضمنة في صفحة أخرى.\nتذكر بأن تتحقق من الوصلات الأخرى للقوالب قبل حذفها.",
        "unusedtemplateswlh": "وصلات أخرى",
        "suppress": "أوفرسايت",
        "querypage-disabled": "تم تعطيل هذه الصفحة الخاصة لأسباب تتعلق بالأداء.",
        "apihelp": "مساعدة API",
+       "apihelp-no-such-module": "الوحدة \"$1\" غير موجودة.",
        "booksources": "مصادر كتاب",
        "booksources-search-legend": "البحث عن مصادر الكتب",
        "booksources-isbn": "ردمك:",
        "listgrouprights-removegroup-self-all": "يمكنه إزالة كل المجموعات من حسابه الخاص",
        "listgrouprights-namespaceprotection-header": "قيود النطاق",
        "listgrouprights-namespaceprotection-namespace": "النطاق",
+       "listgrouprights-namespaceprotection-restrictedto": "الصلاحيات التي تسمح للمستخدم بالتعديل",
        "trackingcategories": "تصانيف التتبع",
        "trackingcategories-summary": "تسرد هذه الصفحة تصانيف التتبع التي ينشئها برنامج ميدياويكي. يمكن تغيير أسمائها بتغيير رسائل النظام في نطاق {{ns:8}}.",
        "trackingcategories-msg": "تصانيف التتبع",
        "delete-edit-reasonlist": "عدل أسباب الحذف",
        "delete-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقُيّد محذف مثل هذه الصفحات لمنع الاضطراب المفاجئة في {{SITENAME}}.",
        "delete-warning-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقد يؤدي حذفها إلى اضطراب عمليات قاعدة البيانات في {{SITENAME}}؛\nاستمر مع الحذر.",
+       "deleteprotected": "لا يمكنك حذف هذه الصفحة لأنها محمية.",
        "deleting-backlinks-warning": "[[Special:WhatLinksHere/{{FULLPAGENAME}}|تتصل صفحات أخرى]] بالصفحة التي تريد حذفها.",
        "rollback": "استرجاع التعديلات",
        "rollback_short": "استرجع",
        "sp-contributions-newbies-sub": "للحسابات الجديدة",
        "sp-contributions-newbies-title": "مساهمات المستخدم للحسابات الجديدة",
        "sp-contributions-blocklog": "سجل المنع",
+       "sp-contributions-suppresslog": "مساهمات المستخدم المحذوفة",
        "sp-contributions-deleted": "مساهمات المستخدم المحذوفة",
        "sp-contributions-uploads": "مرفوعات",
        "sp-contributions-logs": "سجلات",
        "import-logentry-upload": "استورد [[$1]] بواسطة رفع ملف",
        "import-logentry-upload-detail": "{{PLURAL:$1|لا مراجعات|مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}}",
        "import-logentry-interwiki": "استورد عبر الويكي $1",
-       "import-logentry-interwiki-detail": "{{PLURAL:$1||مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}} من $2",
+       "import-logentry-interwiki-detail": "تم استيراد {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}} من $2",
        "javascripttest": "اختبار جافاسكربت",
        "javascripttest-title": "تشغيل أختبارات $1",
        "javascripttest-pagetext-noframework": "هذه الصفحة محجوزة لإجراء أختبارات الجافا سكريبت.",
        "revdelete-uname-unhid": "اسم المستخدم غير مخفي",
        "revdelete-restricted": "طبق الضوابط لمديري النظام",
        "revdelete-unrestricted": "أزال الضوابط لمديري النظام",
+       "logentry-merge-merge": "{{GENDER:$2|دمج|دمجت}} $1 $3 إلى $4 (المراجعات حتى $5).",
        "logentry-move-move": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4",
        "logentry-move-move-noredirect": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4 دون ترك تحويلة",
        "logentry-move-move_redir": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4 على تحويلة",
        "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3 من $4 إلى $5",
        "logentry-rights-rights-legacy": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3",
        "logentry-rights-autopromote": "تمت تلقائيا ترقية {{GENDER:$2|المستخدم|المستخدمة}} $1 من  $4 إلى $5",
+       "logentry-upload-upload": " {{GENDER:$2|رفع|رفعت}} $1 $3",
+       "logentry-upload-overwrite": "{{GENDER:$2|رفع|رفعت}} $1 نسخة جديدة من  $3",
+       "logentry-upload-revert": "{{GENDER:$2|رفع|رفعت}} $1 $3",
        "rightsnone": "(لا شيء)",
        "revdelete-summary": "ملخص التعديل",
        "feedback-bugornote": "إن كنت مستعدا لشرح  مشكلة تقنية بالتفصيل، رجاءا [$1 قدم تقريرا بالخلل].\nبخلاف ذلك، يمكنك أستخدام الطريقة الأسهل أسفله، سيتم إضافة تعليقك للصفحة \"[$3 $2]\"، بالإضافة إلى اسم المستخدم و نوع المتصفح الذي تستخدمه حاليا.",
        "right-pagelang": "تغيير لغة الصفحة",
        "action-pagelang": "تغيير لغة الصفحة",
        "log-name-pagelang": "تغيير سجل الصفحة",
+       "logentry-pagelang-pagelang": " {{GENDER:$2|غيّر|غيّرت}} $1 لغة الصفحة «$3» من $4 إلى $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (مفعل)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''معطل''')",
        "mediastatistics": "إحصاءات الميديا",
index d417347..1b350c0 100644 (file)
@@ -42,7 +42,7 @@
        "tog-shownumberswatching": "Паказваць колькасьць назіральнікаў",
        "tog-oldsig": "Цяперашні подпіс:",
        "tog-fancysig": "Апрацоўваць подпіс як вікітэкст (без аўтаматычнай спасылкі)",
-       "tog-uselivepreview": "Выкарыстоўваць хуткі папярэдні прагляд (экспэрымэнтальна)",
+       "tog-uselivepreview": "Выкарыстоўваць хуткі папярэдні прагляд",
        "tog-forceeditsummary": "Папярэджваць пра адсутнасьць кароткага апісаньня зьменаў",
        "tog-watchlisthideown": "Хаваць мае праўкі ў сьпісе назіраньня",
        "tog-watchlisthidebots": "Хаваць праўкі робатаў у сьпісе назіраньня",
        "filecopyerror": "Немагчыма cкапіяваць файл «$1» у «$2».",
        "filerenameerror": "Немагчыма перайменаваць файл «$1» у «$2».",
        "filedeleteerror": "Немагчыма выдаліць файл «$1».",
-       "directorycreateerror": "Ð\9dемагÑ\87Ñ\8bма Ñ\81Ñ\82ваÑ\80Ñ\8bÑ\86Ñ\8c Ð´Ñ\8bÑ\80Ñ\8dкÑ\82оÑ\80Ñ\8bÑ\8e «$1».",
+       "directorycreateerror": "Ð\9dемагÑ\87Ñ\8bма Ñ\81Ñ\82ваÑ\80Ñ\8bÑ\86Ñ\8c ÐºÐ°Ñ\82алÑ\91г «$1».",
        "directoryreadonlyerror": "Тэчка «$1» толькі для чытаньня.",
        "directorynotreadableerror": "Тэчка «$1» не чытаецца.",
        "filenotfound": "Немагчыма знайсьці файл «$1».",
        "unexpected": "Нечаканае значэньне: «$1»=«$2».",
-       "formerror": "Памылка: не атрымалася адаслаць зьвесткі формы",
+       "formerror": "Памылка: не атрымалася адаслаць зьвесткі формы.",
        "badarticleerror": "Гэтае дзеяньне немагчыма выканаць на гэтай старонцы.",
        "cannotdelete": "Немагчыма выдаліць старонку альбо файл «$1». Магчыма, яна ўжо выдаленая кімсьці іншым.",
        "cannotdelete-title": "Немагчыма выдаліць старонку «$1»",
        "anoneditwarning": "<strong>Папярэджаньне</strong>: вы не ўвайшлі ў сыстэму. Ваш IP-адрас будзе бачны ўсім, калі вы адрэдагуеце старонку. Калі вы <strong>[$1 ўвойдзеце]</strong> або <strong>[$2 створыце рахунак]</strong>, вашыя рэдагаваньні будуць зьвязаныя з вашым імем карыстальніка, а таксама вам будуць даступныя дадатковыя перавагі.",
        "anonpreviewwarning": "''Вы не ўвайшлі ў сыстэму. Падчас захаваньня Ваш IP-адрас будзе дададзены ў гісторыю рэдагаваньняў старонкі.''",
        "missingsummary": "'''Напамін:''' Вы не пазначылі кароткае апісаньне зьменаў.\nКалі Вы націсьніце кнопку «Запісаць» яшчэ раз, Вашае рэдагаваньне будзе запісанае без апісаньня.",
+       "selfredirect": "<strong>Папярэджаньне:</strong> вы ствараеце перанакіраваньне на гэты самы артыкул.\nКалі вы націсьніце «{{int:savearticle}}» яшчэ раз, перанакіраваньне будзе створанае.",
        "missingcommenttext": "Калі ласка, увядзіце камэнтар ніжэй.",
        "missingcommentheader": "'''Напамін:''' Вы не пазначылі загаловак камэнтара.\nКалі Вы націсьніце кнопку «{{int:savearticle}}» яшчэ раз, Ваш камэнтар захаваецца бяз тэмы.",
        "summary-preview": "Папярэдні прагляд апісаньня:",
index a6192ee..b111bf5 100644 (file)
        "login-userblocked": "এই ব্যবহারকারীকে বাধা দেওয়া হয়েছে। লগ-ইন সম্ভব নয়।",
        "wrongpassword": "আপনি ভুল পাসওয়ার্ড ব্যবহার করেছেন। অনুগ্রহ করে আবার চেষ্টা করুন।",
        "wrongpasswordempty": "পাসওয়ার্ড প্রবেশের ঘরটি খালি ছিল। দয়া করে আবার চেষ্টা করুন।",
-       "passwordtooshort": "পাসà¦\93য়ারà§\8dড à¦\85বশà§\8dযà¦\87 {{PLURAL:$1|১ à¦\85à¦\95à§\8dষরà§\87র|$1 à¦\85à¦\95à§\8dষরà§\87র}} à¦¹à¦¤à§\87 à¦¹à¦¬à§\87।",
+       "passwordtooshort": "পাসà¦\93য়ারà§\8dড à¦\95মপà¦\95à§\8dষà§\87 {{PLURAL:$1|১ à¦\85à¦\95à§\8dষরà§\87র|$1 à¦\85à¦\95à§\8dষরà§\87র}} à¦¹à¦¤à§\87 à¦¹à¦¬à§\87।",
        "password-name-match": "আপনার পাসওয়ার্ড আপনার ব্যবহারকারী নাম থেকে আলাদা হতে হবে।",
        "password-login-forbidden": "এই ব্যবহারকারীর নাম এবং পাসওয়ার্ডটি ব্যবহার নিষিদ্ধ করা হয়েছে।",
        "mailmypassword": "পাসওয়ার্ড রিসেট",
        "action-viewmywatchlist": "আপনার নজরতালিকা দেখুন",
        "action-viewmyprivateinfo": "আপনার ব্যক্তিগত তথ্য দেখুন",
        "action-editmyprivateinfo": "আপনার ব্যক্তিগত তথ্য সম্পাদনা করুন",
-       "nchanges": "$1 {{PLURAL:$1|পরিবর্তন|পরিবর্তনসমূহ}}",
+       "nchanges": "$1টি {{PLURAL:$1|পরিবর্তন}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|সর্বশেষ প্রদর্শনের পর}}",
        "enhancedrc-history": "ইতিহাস",
        "recentchanges": "সাম্প্রতিক পরিবর্তনসমূহ",
        "activeusers-hidesysops": "প্রশাসক লুকাও",
        "activeusers-noresult": "কোনো ব্যবহারকারী পাওয়া যায়নি।",
        "listgrouprights": "দলগত ব্যবহারকারী অধিকার",
-       "listgrouprights-summary": "এই উইকির ব্যবহারকারীদের একটি গ্রুপগুলোর তালিকা দেখানো হচ্ছে, সাথে গ্রুপের কার্যপরিধিও উল্লেখ করা হয়েছে।\nনির্দিষ্ট গ্রুপের কার্যপরিধি সম্পর্কে জানতে দেখুন [[{{MediaWiki:Listgrouprights-helppage}}|additional information]]।",
+       "listgrouprights-summary": "এই উইকির ব্যবহারকারীদের একটি গ্রুপগুলোর তালিকা দেখানো হচ্ছে, সাথে গ্রুপের কার্যপরিধিও উল্লেখ করা হয়েছে।\nনির্দিষ্ট গ্রুপের কার্যপরিধি সম্পর্কে জানতে [[{{MediaWiki:Listgrouprights-helppage}}|অতিরিক্ত তথ্য]] দেখুন।",
        "listgrouprights-key": "লিজেন্ড:\n* <span class=\"listgrouprights-granted\">অনুমোদিত অধিকার</span>\n* <span class=\"listgrouprights-revoked\">বাধাপ্রাপ্ত অধিকার</span>",
        "listgrouprights-group": "দল",
        "listgrouprights-rights": "অধিকারসমূহ",
        "tags-active-yes": "হ্যাঁ",
        "tags-active-no": "না",
        "tags-edit": "সম্পাদনা",
-       "tags-hitcount": "$1 {{PLURAL:$1|পরিবর্তন|পরিবর্তনসমূহ}}",
+       "tags-hitcount": "$1টি {{PLURAL:$1|পরিবর্তন}}",
        "comparepages": "পাতার তুলনা",
        "compare-page1": "পাতা ১",
        "compare-page2": "পাতা ২",
index 133825c..882a312 100644 (file)
@@ -44,7 +44,8 @@
                        "Calak",
                        "F3RaN",
                        "ESM",
-                       "Loupeter"
+                       "Loupeter",
+                       "Macofe"
                ]
        },
        "tog-underline": "Subratlla els enllaços:",
@@ -72,7 +73,7 @@
        "tog-shownumberswatching": "Mostra el nombre d'usuaris que hi vigilen",
        "tog-oldsig": "Signatura actual:",
        "tog-fancysig": "Tractar la signatura com a text wiki (sense enllaç automàtic)",
-       "tog-uselivepreview": "Utilitza la previsualització automàtica (cal JavaScript) (experimental)",
+       "tog-uselivepreview": "Utilitza la previsualització automàtica",
        "tog-forceeditsummary": "Avisa'm en deixar el resum de la modificació en blanc",
        "tog-watchlisthideown": "Amaga les meues edicions de la llista de seguiment",
        "tog-watchlisthidebots": "Amaga de la llista de seguiment les edicions fetes per usuaris bots",
        "viewsourcetext": "Podeu visualitzar i copiar el codi font d’aquesta pàgina:",
        "viewyourtext": "Vostè pot veure i copiar la font de ' ' les modificacions ' ' d'aquesta pàgina:",
        "protectedinterface": "Aquesta pàgina proporciona el text de la interfície del software d'aquest wiki i està protegida per evitar els abusos.\nPer afegir o canviar les traduccions per a tots els wikis, feu servir [//translatewiki.net/ translatewiki.net], el projecte de localització de MediaWiki.",
-       "editinginterface": "'''Avís:''' Esteu editant una pàgina que conté cadenes de text per a la interfície d'aquest programari. Tingueu en compte que els canvis que es fan a aquesta pàgina afecten a l'aparença de la interfície d'altres usuaris. Per afegir o modificar traduccions a totes les wikis, plantegeu-vos utilitzar la [//translatewiki.net/ translatewiki.net], el projecte de localització de MediaWiki.",
+       "editinginterface": "'''Avís:''' esteu editant una pàgina que s'utilitza per proporcionar text d'interfície per al programari. Els canvis que es facin a la pàgina afectaran l'aparença de la interfície d'altres usuaris del wiki.",
        "translateinterface": "Per afegir o canviar traduccions per a tots els wikis, utilitzeu [//translatewiki.net/ translatewiki.net], el projecte de localització de MediaWiki.",
        "cascadeprotected": "Aquesta pàgina està protegida i no es pot modificar perquè està inclosa en {{PLURAL:$1|la següent pàgina, que té|les següents pàgines, que tenen}} activada l'opció de «protecció en cascada»:\n$2",
        "namespaceprotected": "No teniu permís per a modificar pàgines en l'espai de noms '''$1'''.",
index 91f08a2..3a9a46d 100644 (file)
@@ -15,7 +15,9 @@
        "tog-underline": "下劃綫鏈接",
        "tog-hideminor": "囥起最近改變其過幼修改",
        "tog-hidepatrolled": "囥起最近改變其巡邏修改",
+       "tog-newpageshidepatrolled": "共巡邏視頁趁新建頁列表𡅏囥起去",
        "tog-extendwatchlist": "敆擴展監視單單臺中顯示所有其更改,伓啻最近其更改",
+       "tog-usenewrc": "按頁顯示最近修改共監視列表臺中其群組改變",
        "tog-numberheadings": "自動編號其標題",
        "tog-showtoolbar": "顯示編輯工具欄",
        "tog-editondblclick": "雙擊就修改頁面",
        "tog-watchdefault": "添加我編輯其頁面共文件遘我其監視單",
        "tog-watchmoves": "添加我移動其頁面共文件遘我其監視單",
        "tog-watchdeletion": "添加我刪掉其頁面共文件遘我其監視單",
+       "tog-watchrollback": "敆我其監視列表臺中添加我做過回滚其頁面",
        "tog-minordefault": "默認共所有其編輯都當作過幼修改",
        "tog-previewontop": "敆編輯框以前顯示預覽",
        "tog-previewonfirst": "敆頭蜀回編輯時候看預覽",
        "tog-enotifwatchlistpages": "我其監視單有變時候,發電子郵件乞我",
        "tog-enotifusertalkpages": "我其討論頁有變時候,發電子郵件乞我",
        "tog-enotifminoredits": "就㑚講是過幼編輯,也着發電子郵件乞我",
+       "tog-enotifrevealaddr": "敆通知郵件臺中顯示我其電子郵件地址",
        "tog-shownumberswatching": "顯示監視用戶其數量",
        "tog-oldsig": "存在其簽名",
        "tog-fancysig": "共簽名當成維基文本(無自動鏈接)",
@@ -38,7 +42,7 @@
        "tog-watchlisthideown": "趁監視單𡅏囥起我其修改",
        "tog-watchlisthidebots": "囥起監視單其機器人其修改",
        "tog-watchlisthideminor": "囥起監視單其過幼修改",
-       "tog-watchlisthideliu": "共已經躒底其用戶其編輯趁監視單𡅏囥起咯",
+       "tog-watchlisthideliu": "共已經登錄其用戶其編輯趁監視單𡅏囥起咯",
        "tog-watchlisthideanons": "共匿名其用戶其編輯趁監視單𡅏囥起咯",
        "tog-watchlisthidepatrolled": "共巡查其編輯趁監視單𡅏囥起咯",
        "tog-ccmeonemails": "共我發乞其他用戶其電子郵件其備份發乞我。",
@@ -46,6 +50,7 @@
        "tog-showhiddencats": "㪗藏類別",
        "tog-norollbackdiff": "敆回滾其時候,無叕𣍐蜀様其地方",
        "tog-useeditwarning": "我編輯頁面其時候離開,起動警告我蜀下",
+       "tog-prefershttps": "登錄以後全程使用安全連接",
        "underline-always": "直頭",
        "underline-never": "頭𡅏無",
        "underline-default": "皮膚或者瀏覽器默認其",
        "mytalk": "我其討論",
        "anontalk": "茲隻IP其討論頁",
        "navigation": "引導",
-       "and": "&#32;and",
+       "and": "&#32;",
        "qbfind": "討",
        "qbbrowse": "覷蜀覷",
        "qbedit": "修改",
        "permalink": "永久鏈接",
        "print": "拍印",
        "view": "覷蜀覷",
+       "view-foreign": "敆$1𡅏看",
        "edit": "修改",
+       "edit-local": "編輯當地描述",
        "create": "創建",
+       "create-local": "添加當地描述",
        "editthispage": "修改茲頁",
        "create-this-page": "創建茲蜀頁",
        "delete": "刪除",
        "categorypage": "看分類頁",
        "viewtalkpage": "看討論",
        "otherlanguages": "其它其語言",
-       "redirectedfrom": "($1重定向過來)",
+       "redirectedfrom": "($1重定向過來)",
        "redirectpagesub": "重定向頁",
+       "redirectto": "重定向遘",
        "lastmodifiedat": "茲蜀頁是着$1 $2其辰候最後修改其。",
        "viewcount": "茲蜀頁已經乞訪問$1回了。{{PLURAL:$1}}",
        "protectedpage": "保護頁",
        "jumptonavigation": "引導:",
        "jumptosearch": "尋討",
        "view-pool-error": "對不住,服務器茲蜀萆時候已弳過載了。\n過価用戶敆𡅏覷茲蜀頁。\n起動等仂久再來覷茲蜀頁。\n\n$1",
+       "generic-pool-error": "對不住,現刻時服務器過載了。\n實在過価用戶敆𡅏訪問茲蜀萆資源。\n起動汝等蜀刻再訪問茲蜀萆資源。",
        "pool-timeout": "等待鎖定其時間遘了",
        "pool-queuefull": "隊列池已經滿了",
        "pool-errorunknown": "𣍐八什乇鄭咯",
        "aboutsite": "關於{{SITENAME}}",
        "aboutpage": "Project:關於",
-       "copyright": "å\85§å®¹æ\95\86$1ä¸\8båº\95æ\9c\83使ç\8d²å¾\97。",
+       "copyright": "å\85§å®¹æ\9c\83使æ\95\86$1ä¸\8båº\95æ\9c\83使ç\8d²å¾\97é\81\98ï¼\8cè\8b¥ç\84¡æ\9c\83給å\87ºå\85¶å®\83æ\8f\90示。",
        "copyrightpage": "{{ns:project}}:版權",
        "currentevents": "大樹下",
        "currentevents-url": "Project:大樹下",
        "youhavenewmessages": "汝有$1($2)。",
        "youhavenewmessagesfromusers": "汝有趁$3用戶($2)來其$1萆信息{{PLURAL:$3}}",
        "youhavenewmessagesmanyusers": "汝有趁雅価用戶($2)其$1信息",
-       "newmessageslinkplural": "$1條新其信息{{PLURAL:$1}}",
-       "newmessagesdifflinkplural": "最後其改變{{PLURAL:$1}}",
+       "newmessageslinkplural": "{{PLURAL:$1|蜀條新其消息|999=新其消息}}",
+       "newmessagesdifflinkplural": "最後{{PLURAL:$1|回改變|999=回改變}}",
        "youhavenewmessagesmulti": "汝有趁$1來其新信息",
        "editsection": "修改",
        "editold": "修改",
        "hidetoc": "囥起",
        "collapsible-collapse": "隱",
        "collapsible-expand": "現",
+       "confirmable-confirm": "汝會確定𣍐?",
+       "confirmable-yes": "是",
+       "confirmable-no": "伓是",
        "thisisdeleted": "卜看或者恢復$1?",
        "viewdeleted": "看$1?",
        "restorelink": "$1萆乞刪掉其修改{{PLURAL:$1}}",
        "nospecialpagetext": "<strong>汝請求蜀萆𣍐合法其特殊頁面。</strong>\n\n合法其特殊頁面清單會使敆[[Special:SpecialPages|{{int:特殊頁面}}]]頁面討著",
        "error": "鄭咯",
        "databaseerror": "數據庫有綻",
+       "databaseerror-text": "數據庫查詢發生錯誤。\n嚽可能是軟件底裡其程序缺陷。",
+       "databaseerror-textcl": "數據庫查詢發生錯誤。",
+       "databaseerror-query": "查詢語句:$1",
+       "databaseerror-function": "函數名:$1",
+       "databaseerror-error": "錯誤信息:$1",
        "laggedslavemode": "'''警告:'''頁面可能無最近其更新。",
        "readonly": "數據庫乞鎖起咯",
+       "enterlockreason": "拍底汝鎖定數據庫其原因,包括汝估計其釋放鎖其時間",
        "readonlytext": "Só-gé̤ṳ-kó cī-buàng ké̤ṳk nè̤ng sō̤ kī lāu, mâ̤-sāi siā sĭng dèu-mĕ̤k hĕ̤k có̤ siŭ-gāi, ô kō̤-nèng sê ôi-lāu nĭk-siòng mì-hô, cĭ-hâiu cêu â̤ ciáng-siòng.\n\nSō̤ kī só-gé̤ṳ-kó gì guāng-lī-uòng cūng-kuāng gāi-sék: $1",
+       "missing-article": "數據庫未討遘本身應當著討遘其名叫\"$1\"其頁面$2其文本。\n\n嚽可能是下底其過時其diff或者已經删除其歴史鏈接造成其。\n\n如果伓是茲兩種情況,汝可能發現著蜀萆服務器其缺陷。\n起動汝共茲蜀萆缺陷匯報乞[[Special:ListUsers/sysop|管理員]],附上網址。",
+       "missingarticle-rev": "(版本#:$1)",
        "missingarticle-diff": "(比並:$1、$2)",
+       "readonly_lag": "從數據庫跟上主數據庫其辰候,數據庫已經自動鎖定",
        "internalerror": "內部錯誤",
        "internalerror_info": "內部錯誤:$1",
        "filecopyerror": "𣍐使趁「$1」𡅏複製文件遘「$2」。",
        "filerenameerror": "𣍐使共「$1」其名字改去「$2」。",
        "filedeleteerror": "𣍐使刪掉文件「$1」。",
        "directorycreateerror": "𣍐使刪掉目錄「$1」。",
+       "directoryreadonlyerror": "目錄$1是只讀目錄。",
+       "directorynotreadableerror": "目錄$1是禁讀目錄。",
        "filenotfound": "討𣍐著文件「$1」。",
        "unexpected": "伓是卜挃其值:「$1」=「$2」。",
        "formerror": "賺:𣍐使提交表單。",
+       "badarticleerror": "不允許敆茲蜀萆做茲蜀種行為。",
        "cannotdelete": "無能耐刪掉頁面或者文件「$1」。\n可能茲已經共別儂刪掉咯了。",
        "cannotdelete-title": "無辦法刪掉頁面「$1」",
        "delete-hook-aborted": "刪除乞鉤子拍斷咯。\n無給出解釋。",
+       "no-null-revision": "𣍐使敆頁面$1𡅏新建空操作。",
        "badtitle": "獃其標題",
        "perfcached": "下底其數據乞緩存固加可能伓是最新其。{{PLURAL:$1|$1條結果}}會敆緩存臺中討著。",
        "perfcachedts": "下底其數據已經緩存過了,最後更新遘$1。{{PLURAL:$4|$4條結果}}會敆緩存臺中討著。",
        "protectedpagetext": "茲頁已經乞保護起咯,𣍐使修改或者其它行動。",
        "viewsourcetext": "汝會使看共複製茲蜀頁其源代碼:",
        "viewyourtext": "汝會使覷蜀覷或者複製茲頁'''汝其修改'''其源代碼:",
-       "editinginterface": "'''警告:'''汝敆𡅏修改其頁面廮𡅏提供茲蜀萆軟件其界面文本。\n茲蜀頁其改變會影響遘其它用戶其用戶界面其顯示。\n如果卜想修改維基其翻譯,起動遘MediaWiki本地化計劃[//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net]。",
+       "editinginterface": "<strong>警告:</strong>汝敆𡅏修改其頁面廮𡅏提供茲蜀萆軟件其界面文本。\n茲蜀頁其改變會影響遘其它用戶其用戶界面其顯示。",
+       "cascadeprotected": "茲蜀頁受保護,𣍐使編辑,因為茲蜀頁包含敆下底{{PLURAL:$1|頁|頁}}開起“級聯”選項其受保護頁面底裡。\n$2",
        "namespaceprotected": "汝𣍐使修改敆'''$1'''命名空間其頁面。",
        "customcssprotected": "汝𣍐使修改茲蜀萆CSS頁面,因為伊有別蜀隻用戶其設定。",
        "customjsprotected": "汝𣍐使修改茲蜀萆JavaScript頁面,因為伊有別蜀隻用戶其設定。",
        "mycustomcssprotected": "汝𣍐使修改茲蜀萆CSS頁面。",
        "mycustomjsprotected": "汝𣍐使修改茲蜀萆JavaScript頁面。",
+       "myprivateinfoprotected": "汝無權限编輯汝其私人信息。",
+       "mypreferencesprotected": "汝無權限編輯偏好。",
        "ns-specialprotected": "𣍐使修改特殊頁面。",
        "titleprotected": "茲蜀萆標題共[[User:$1|$1]]保護其咯。\n原因是「''$2''」。",
-       "exception-nologin": "未躒底其",
-       "exception-nologin-text": "茲蜀頁其行動卜挃汝躒底茲蜀萆維基百科。",
+       "exception-nologin": "未登錄",
+       "exception-nologin-text": "起動汝登錄以後再訪問茲蜀頁,或者做茲蜀萆操作。",
+       "exception-nologin-text-manual": "起動汝$1,以後才會使訪問茲蜀頁,或者做茲蜀萆行為。",
        "virus-badscanner": "獃其配置:𣍐八其病毒掃描器:''$1''",
        "virus-scanfailed": "掃描失敗(代碼$1)",
        "virus-unknownscanner": "𣍐八其反病毒:",
-       "logouttext": "'''汝現在躒出了。'''\n\n汝會使使無名方式繼續覷{{SITENAME}},或者汝會使蜀様或者𣍐蜀様其用戶<span class='plainlinks'>[$1 再躒底其]</span>。\n注意有其頁面可能繼續顯示真像汝應經躒底其了,除開汝清理汝其瀏覽器緩存。",
+       "logouttext": "<strong>汝現在退出了。</strong>\n\n注意有其頁面可能繼續顯示真像汝已經登錄了,除開汝清理瀏覽器緩存。",
        "welcomeuser": "歡迎,$1!",
        "welcomecreation-msg": "汝其賬戶已經開好了。\n伓嗵𣍐記改蜀改汝其[[Special:Preferences|{{SITENAME}}設定]]。",
        "yourname": "用戶名:",
        "userlogin-yourname": "用戶名",
        "userlogin-yourname-ph": "輸底汝其用戶名",
+       "createacct-another-username-ph": "輸底汝其用戶名",
        "yourpassword": "密碼:",
        "userlogin-yourpassword": "密碼",
        "userlogin-yourpassword-ph": "輸底汝其密碼",
        "yourdomainname": "汝其域名:",
        "password-change-forbidden": "汝𣍐使敆茲蜀萆維基百科𡅏修改密碼。",
        "externaldberror": "可能是驗證數據庫綻咯,或者是汝𣍐使升級汝其外部賬戶。",
-       "login": "躒底",
-       "nav-login-createaccount": "躒底/開賬戶",
-       "userlogin": "躒底/開賬戶",
-       "userloginnocreate": "躒底",
-       "logout": "出",
-       "userlogout": "出",
-       "notloggedin": "未躒底",
+       "login": "登錄",
+       "nav-login-createaccount": "登錄/開賬戶",
+       "userlogin": "登錄/開賬戶",
+       "userloginnocreate": "登錄",
+       "logout": "退出",
+       "userlogout": "退出",
+       "notloggedin": "未登錄",
        "userlogin-noaccount": "汝無賬戶?",
        "userlogin-joinproject": "共{{SITENAME}}加底其",
        "nologin": "汝無賬戶?$1",
        "nologinlink": "開蜀隻賬戶",
        "createaccount": "開賬戶",
        "gotaccount": "已經有賬戶了?'''$1'''。",
-       "gotaccountlink": "躒底",
-       "userlogin-resetlink": "躒底其資料𣍐記咯?",
+       "gotaccountlink": "登錄",
+       "userlogin-resetlink": "登錄其資料𣍐記咯?",
        "userlogin-resetpassword-link": "密码𣍐記?",
-       "userlogin-helplink2": "對手汝躒底",
+       "userlogin-helplink2": "對手汝登錄",
+       "userlogin-loggedin": "汝已經使$1登錄過了。\n卜想使其他用戶登錄,請使下底其表格來登錄。",
+       "userlogin-createanother": "新建另外蜀萆賬號",
        "createacct-emailrequired": "電子郵件地址",
        "createacct-emailoptional": "電子郵件地址(愛寫就寫)",
        "createacct-email-ph": "輸底汝其電子郵件地址",
+       "createacct-another-email-ph": "輸底電子郵件地址",
        "createaccountmail": "使臨時其隨機密碼,共伊送遘指定其電子郵件地址",
        "createacct-realname": "實際其名字(愛寫就寫)",
        "createaccountreason": "原因:",
        "createacct-captcha": "安全檢查",
        "createacct-imgcaptcha-ph": "輸底汝敆懸頂看見其文字",
        "createacct-submit": "開賬戶",
+       "createacct-another-submit": "新建另外蜀萆賬號",
        "createacct-benefit-heading": "{{SITENAME}}是共汝蜀様其儂做其。",
        "createacct-benefit-body1": "{{PLURAL:$1|修改}}",
        "createacct-benefit-body2": "{{PLURAL:$1|頁面}}",
        "loginerror": "躒底有鄭",
        "createacct-error": "賬戶開出毛病咯",
        "createaccounterror": "無能獃開賬戶:$1",
+       "nocookiesnew": "用戶賬號已經創建好了,但是汝未登錄。\n{{SITENAME}}使cookie來記錄已經登錄其用戶。\n但是汝禁用了cookie。\n起動汝開啟cookie,然後再使汝其新用戶共密碼來登錄。",
+       "nocookieslogin": "{{SITENAME}}使cookies來記錄已經登錄其用戶。\n但是汝禁用了cookie。\n起動汝開起cookie,然後再試蜀試。",
        "noname": "汝未指定蜀萆合法其用戶名。",
        "loginsuccesstitle": "躒底成功",
        "loginsuccess": "'''汝現在已經「$1」其成功躒底{{SITENAME}}了。'''",
        "passwordsent": "新密碼已經寄遘「$1」註冊其電子郵件地址了。\n收遘後,請再躒底蜀頭部。",
        "mailerror": "發電子郵件有賺:$1",
        "acct_creation_throttle_hit": "使汝其IP訪問茲蜀萆維基百科訪問者其已經敆最後蜀日創建{{PLURAL:$1|$1萆賬戶}}去了。茲蜀段時間最価若允許創建茲滿価萆賬戶。故此講使茲蜀萆IP訪問其儂敆現刻時𣍐使再開賬戶了。",
-       "emailauthenticated": "汝其電子郵件地址已經敆$2$3驗證過了。",
+       "emailauthenticated": "汝其電子郵件地址已經敆$2$3確定過了。",
+       "emailnotauthenticated": "汝其電子郵件固未確定過。\n下底其所有特性都𣍐發電子郵件乞汝。",
        "emailconfirmlink": "確認汝其電子郵件地址",
        "emaildisabled": "茲萆站點𣍐使發電子郵件。",
        "accountcreated": "賬戶創建了",
        "createaccount-title": "{{SITENAME}}其開賬戶",
        "login-abort-generic": "汝其躒底𣍐成功——放棄去了",
        "loginlanguagelabel": "語言:$1",
+       "pt-login": "登錄",
+       "pt-login-button": "登錄",
+       "pt-createaccount": "開新賬號",
+       "pt-userlogout": "退出",
        "php-mail-error-unknown": "PHP其mail()函數,𣍐八什乇賺去。",
        "changepassword": "改變密碼",
        "resetpass_header": "改變賬戶其密碼",
        "preview": "預覽",
        "showpreview": "顯示預覽",
        "showdiff": "看改變其部分",
-       "anoneditwarning": "'''警告:'''汝未躒底。\n汝其IP地址會乞記着茲頁面其修改歷史裏勢。",
+       "anoneditwarning": "<strong>警告:</strong>汝未登錄。\n如果汝做修改,汝其IP地址會敆編輯歷史底裡公開。如果你<strong>[$1登錄]</strong>或者<strong>[$2註册新賬號]</strong>,汝其修改記錄會顯示汝其用戶名,固有其它其好處。",
+       "anonpreviewwarning": "<em>汝未登錄。如果汝保存茲蜀頁其修改,汝其IP地址會記錄敆茲蜀頁其編輯歴史臺中。</em>",
        "missingcommenttext": "起動敆下底輸底蜀條評論。",
        "summary-preview": "總結預覽:",
        "blockedtitle": "用戶乞封鎖了",
        "loginreqlink": "躒底",
        "loginreqpagetext": "著$1才會使看其它頁面。",
        "accmailtitle": "密碼寄出了",
-       "accmailtext": "共[[User talk:$1|$1]]用戶其臨時產生其密碼已經發$2了。\n\n茲蜀萆新其賬戶其密碼會使敆用戶躒底以後著''[[Special:ChangePassword|改密碼]]''頁面𡅏改變。",
+       "accmailtext": "共[[User talk:$1|$1]]用戶隨機生成其密碼已經發遘$2了。汝登錄以後會使敆[[Special:ChangePassword|修改密碼]]頁面修改茲蜀萆密碼。",
        "newarticle": "(新)",
        "newarticletext": "汝已經跟鏈接跟遘無存在其頁面了。\n卜想創建頁面,敆下底其框框𡅏拍字(覷蜀覷[$1 幫助頁面]有無更更価其幫助)。\n如果汝是無注意來遘茲蜀萆頁面,篤囇汝其瀏覽器上其「返回」按鈕。",
        "anontalkpagetext": "''茲是未躒底其用戶討論頁面。''\n故此儂家著使數字IP來確定伊。\n總款其IP地址會乞雅価用戶共享。\n如果蜀隻未躒底其用戶見覺無關係其評論指向汝,起動[[Special:UserLogin/signup|開賬戶]]或者[[Special:UserLogin|躒底]]來避免以後共其它未躒底其用戶混蜀堆。",
        "noarticletext": "現在敆茲蜀頁𡅏無文字。汝會使敆其它其頁面𡅏[[Special:Search/{{PAGENAME}}|討蜀討茲蜀萆標題]],<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 討相關其記錄],或者[{{fullurl:{{FULLPAGENAME}}|action=edit}}編輯茲蜀頁]</span>。",
        "clearyourcache": "'''注意:'''保存以後,汝可能固著刷新汝其瀏覽器緩存來看遘變化。\n* '''火狐/Safari:'''擪下''Shift''篤蜀篤''重新載入'',或者擪蜀擪''Ctrl+F5''或者''Ctrl+R'' (''⌘-R''敆Mac懸頂)\n* '''Google Chrome:'''擪''Ctrl+Shift+R''(敆Mac𡅏使''⌘-Shift-R'')\n* '''Internet Explorer:'''擪''Ctrl''其時候篤蜀篤''刷新'',或者擪''Ctrl+F5''\n* '''Opera:'''敆''工具→首選項''𡅏清除緩存",
+       "note": "<strong>注意:</strong>",
        "previewnote": "'''記定茲若是蜀萆預覽。'''\n汝其改變固𡅏未保存!",
        "continue-editing": "行去編輯區",
        "editing": "修改 $1",
        "template-protected": "(保護)",
        "template-semiprotected": "(半保護)",
        "recreate-moveddeleted-warn": "'''注意:汝敆𡅏重新創建舊底已經乞刪唻其頁面。'''\n\n汝應該考慮蜀下繼續去編輯茲蜀頁到底是伓是合適其。茲蜀頁其刪除記錄共移動記錄都敆嚽塊:",
+       "edit-conflict": "編輯衝突",
+       "content-model-wikitext": "維基文本",
+       "content-model-text": "純文本",
+       "content-model-javascript": "JavaScript",
+       "content-model-css": "CSS",
        "undo-summary": "取消[[Special:Contributions/$2|$2]]([[User talk:$2|Tō̤-lâung]])其$1修改",
        "cantcreateaccounttitle": "無能獃開賬戶",
        "viewpagelogs": "看茲頁其歷史",
        "rclistfrom": "顯示由$3 $2開始其新其改變",
        "rcshowhideminor": "$1過幼修改",
        "rcshowhidebots": "$1機器人",
-       "rcshowhideliu": "$1躒底用戶",
+       "rcshowhideliu": "$1已註冊其用戶",
        "rcshowhideanons": "$1無名用戶",
        "rcshowhidemine": "$1我其修改",
        "rclinks": "顯示$2日以內產生其$1回改變<br />$3",
        "filesource": "來源:",
        "ignorewarning": "無視警告保存文件",
        "ignorewarnings": "無視警告",
-       "fileexists": "名字蜀樣其文件已經存在去了。如果𣍐確定汝是伓是卜想刪掉伊,起動檢查蜀下<strong>[[:$1]]</strong>。\n[[$1|thumb]]",
+       "fileexists": "名字蜀樣其文件已經存在去了。如果{{GENDER:|汝}}𣍐確定汝是伓是卜想刪掉伊,起動檢查蜀下<strong>[[:$1]]</strong>。\n[[$1|thumb]]",
        "uploadwarning": "上傳警告",
        "savefile": "保存文件",
        "uploadvirus": "茲文件有病毒!\n細底:$1",
        "watchthispage": "監視茲頁",
        "unwatch": "伓使監視",
        "unwatchthispage": "停止監視",
-       "watchlist-details": "{{PLURAL:$1}}$1頁敆汝其監視單𡅏,無算討論頁。",
+       "watchlist-details": "{{PLURAL:$1|$1頁|$1頁}}敆汝其監視單𡅏,無單獨算討論頁。",
        "wlshowlast": "顯示最$1點鐘$2日",
        "watchlist-options": "監視單選項",
        "watching": "監視...",
        "excontent": "乇是:「$1」",
        "excontentauthor": "乇是:「$1」(並且作者囇有「[[Special:Contributions/$2|$2]]」)",
        "exbeforeblank": "空白以前其乇是:「$1」",
-       "historywarning": "'''警告:'''汝卜想刪掉其頁面有蜀段大概$1隻{{PLURAL:$1|版本}}其它歷史:",
+       "historywarning": "<strong>警告:</strong>汝卜想刪掉其頁面有$1隻{{PLURAL:$1|版本|版本}}其蜀段歷史:",
        "confirmdeletetext": "汝準備全隻頁面共文章連伊敆蜀塊其歷史全部刪掉。\n請汝確認:汝當真卜想總款做,汝瞭解總款做其後果,並且汝總款做事符合[[{{MediaWiki:Policy-url}}]]其。",
        "actioncomplete": "行動成功",
        "actionfailed": "操作失敗",
        "whatlinkshere-hidelinks": "$1鏈接",
        "whatlinkshere-hideimages": "$1 文件鏈接",
        "whatlinkshere-filters": "過濾器",
-       "blockip": "封鎖用戶",
+       "blockip": "封鎖{{GENDER:$1|用戶}}",
        "blockiptext": "使下底其表單來封鎖趁指定IP地址或者用戶名其寫入訪問。茲囇使廮𡅏防止破壞,固加著符合[[{{MediaWiki:Policy-url}}|政策]]。敆下底填底指定其原因(比如講:引用乞破壞其頁面)。",
        "ipaddressorusername": "IP地址或者用戶名:",
        "ipbexpiry": "過期:",
index 7977fb6..cf06a01 100644 (file)
        "anontalk": "Дийцаре хӀокху IP-адресна",
        "navigation": "Навигаци",
        "and": "&#32;а",
-       "qbfind": "Лаха",
+       "qbfind": "Лахар",
        "qbbrowse": "Хьажар",
        "qbedit": "Нисъе",
        "qbpageoptions": "Агlо нисйар",
        "returnto": "ЮхагӀо оцу агӀоне $1.",
        "tagline": "Гlирс хlокхуьна бу {{grammar:genitive|{{SITENAME}}}}",
        "help": "ГӀо",
-       "search": "Лаха",
+       "search": "Лахар",
        "searchbutton": "Лаха",
        "go": "Дехьа гӀо",
        "searcharticle": "Дехьа гӀо",
        "note": "'''Билгалдаккхар:'''",
        "previewnote": "'''ХӀара хьлха хьажар ду, йоза хӀинца язданза ду!'''",
        "continue-editing": "Кхин дӀа тадар",
-       "session_fail_preview": "СеÑ\80веÑ\80 Ð»Ð°Ñ\80а Ñ\86а Ð¹Ð¸Ñ\80а Ð°Ñ\85Ñ\8cа Ð±Ð¸Ð½Ð° Ñ\85ийÑ\86амаÑ\88 Ð´Ó\80аÑ\8fзба. Ð\9aÑ\85иÑ\8a Ñ\86кÑ\8aа Ð° Ð³Ó\80оÑ\80Ñ\82аÑ\85Ñ\8c.\nÐ\9dагаÑ\85Ñ\8c Ñ\81анна Ñ\85Ó\80аÑ\80а Ð³Ó\80алаÑ\82 Ñ\8eÑ\85а Ð° Ð´Ð°Ð»Ð°Ñ\85Ñ\8c, [[Special:UserLogout|Ñ\81еанÑ\81 Ð´Ó\80а Ð° ÐºÑ\8aоÑ\8cвлин]], Ñ\8eÑ\85а Ð° Ñ\81иÑ\81Ñ\82емин Ñ\87Ñ\83вала/Ñ\8fла Ñ\85Ñ\8cажа.",
+       "session_fail_preview": "СеÑ\80веÑ\80 Ð»Ð°Ñ\80а Ñ\86а Ð¹Ð¸Ñ\80а Ð°Ñ\85Ñ\8cа Ð±Ð¸Ð½Ð° Ñ\85ийÑ\86амаÑ\88 Ð´Ó\80аÑ\8fзба. Ð\9aÑ\85иÑ\8a Ñ\86кÑ\8aа Ð° Ð³Ó\80оÑ\80Ñ\82аÑ\85Ñ\8c.\nÐ\9dагаÑ\85Ñ\8c Ñ\81анна Ñ\85Ó\80аÑ\80а Ð³Ó\80алаÑ\82 Ñ\8eÑ\85а Ð° Ð´Ð°Ð»Ð°Ñ\85Ñ\8c, [[Special:UserLogout|Ñ\81еанÑ\81 Ð´Ó\80а Ð° ÐºÑ\8aоÑ\8cвлин]], Ñ\8eÑ\85а Ð° Ñ\81иÑ\81Ñ\82емин Ñ\87Ñ\83гÓ\80о.",
        "edit_form_incomplete": "'''Цхьайолу тадаран формаш серверан тӀекхаьчча яц. Тидаме хьажа хьай нисдарш доьхна дуй, ТӀакха южу гӀорта.'''",
        "editing": "Тадар: $1",
        "creating": "АгӀо кхоллар «$1»",
        "diff-multi-sameuser": "(ца {{PLURAL:$1|гайтина юккъера цхьа верси|гайтина юккъера цхьа версеш}} оьцу декъашхочун)",
        "diff-multi-otherusers": "(ца {{PLURAL:$1|гайтина юккъера верси|гайтина юккъера версеш}} {{PLURAL:$2|кхин цхьан декъашхочун|$2 декъашхойн}})",
        "diff-multi-manyusers": "({{PLURAL:$1|гайтина яц $1 юккъера верси, йина|не показаны $1 юккъера версеш, йина}} {{PLURAL:$2|$2 декъашхочо|$2 декъашхоша}})",
-       "searchresults": "Ð\9bаÑ\85аÑ\80на Ñ\85илам",
-       "searchresults-title": "Лаха «$1»",
+       "searchresults": "Ð\9aаÑ\80ийнаÑ\80Ñ\88",
+       "searchresults-title": "Лахар «$1»",
        "titlematches": "АгӀонийн цӀераш цхьаьнанисялар",
        "textmatches": "АгӀонийн йоза цхьаьнанисдалар",
        "notextmatches": "АгӀонаш чура йозанашца цхьатера йогlуш яц",
        "searchall": "массо",
        "showingresults": "Лахахьа {{PLURAL:$1|гойту}} <strong>$1</strong> {{PLURAL:$1|хилам}}, дӀаболало кху № <strong>$2</strong>.",
        "showingresultsinrange": "Лахахь гайтина {{PLURAL:$1|<strong>1</strong> хилам}} диапазонехь <strong>$2</strong> тӀера <strong>$3</strong> кхаччалц.",
-       "search-showingresults": "{{PLURAL:$4|Хилам <strong>$1</strong> <strong>$3</strong> Ð½Ð°Ñ\85}}",
+       "search-showingresults": "{{PLURAL:$4|Ð\9aаÑ\80ийна <strong>$1</strong> â\80\94 Ñ\86Ñ\85Ñ\8cаÑ\8a Ð°Ð³Ó\80о|Ð\9aаÑ\80ийна <strong>$3</strong> Ð°Ð³Ó\80о, Ñ\86аÑ\80аÑ\85 Ð°Ð³Ó\80онгаÑ\85Ñ\8c Ð³Ð¾Ð¹Ñ\82Ñ\83 $2 Ð°Ð³Ó\80о}}",
        "search-nonefound": "Дехаре терра цхьа хӀума ца карийна.",
-       "powersearch-legend": "Шуьро лахар",
+       "powersearch-legend": "Шуьйра лахар",
        "powersearch-ns": "ЦӀерийн меттигашкахь лахар:",
        "powersearch-togglelabel": "Билгалдан:",
        "powersearch-toggleall": "Массо",
        "prefs-editing": "Тадар",
        "rows": "МогӀанаш:",
        "columns": "БӀогӀамаш:",
-       "searchresultshead": "Лаха",
+       "searchresultshead": "Лахар",
        "stub-threshold": "Кеч яран доза <a href=\"#\" class=\"stub\">коьртамогӀамна хьажоргаш</a> (байташках):",
        "stub-threshold-disabled": "ДӀадайина",
        "recentchangesdays": "Керла нисдар гайта динахь:",
        "randomredirect": "Цахууш нисделла дIасахьажор",
        "randomredirect-nopages": "«$1» цӀерийн меттиган чохь дӀасахьажораш яц.",
        "statistics": "Статистика",
-       "statistics-header-pages": "АгӀонийн жамӀ",
+       "statistics-header-pages": "АгӀонийн статистика",
        "statistics-header-edits": "Нисдарийн статистика",
        "statistics-header-users": "Декъашхойн статистика",
        "statistics-header-hooks": "Кхин статистика",
        "pageswithprop-legend": "АгӀонаш цхьадолу къастамашца",
        "pageswithprop-text": "Кхузахь гойтуш ю агӀонаш цхьадолу къастамаш куьйга юху билгал даьхнарш.",
        "pageswithprop-prop": "Къастаман цӀе:",
-       "pageswithprop-submit": "Ð\9bаÑ\85а",
+       "pageswithprop-submit": "Ð\9aаÑ\80о",
        "pageswithprop-prophidden-long": "деха йозан хӀуман маьӀна хьулйина ($1)",
        "pageswithprop-prophidden-binary": "шалха маьӀна долу хӀума хьулйина ($1)",
        "doubleredirects": "Шалха дIасахьажийнарш",
        "listusers": "Декъашхойн могӀам",
        "listusers-editsonly": "Цхаъ мукъане а хийцам бина декъашхой гайта",
        "listusers-creationsort": "Кхоьллина хене хьаьжна нисъяр",
-       "listusers-desc": "Харжа къезиг хиларца",
+       "listusers-desc": "Харжа кӀезиг хиларца",
        "usereditcount": "$1 {{PLURAL:$1|нисдар|нисдарш}}",
        "usercreated": "{{GENDER:$3|дӀавазвелла|дӀаязелла}} $1 $2",
        "newpages": "Керла агӀонаш",
        "deletedcontributions-title": "ДӀабаьккхина къинхьегам",
        "sp-deletedcontributions-contribs": "къинхьегам",
        "linksearch": "Арахьара хьажораг",
-       "linksearch-pat": "Ð\9bаÑ\85а кеп:",
+       "linksearch-pat": "Ð\9bеÑ\85аÑ\80на кеп:",
        "linksearch-ns": "ЦӀерийн ана:",
        "linksearch-ok": "Лаха",
        "linksearch-text": "Лело мега хӀоттош йолу символаш, масала, <code>*.wikipedia.org</code>.\nЛакхара даржан домен мукъа хила еза , масала<code>*.org</code><br />\nЛовш йолу {{PLURAL:$2|1=протокол|протоколаш}}: <code>$1</code> (Iад йитарца http://, протокол бакъалла язъен яцахь).",
        "blocklist-by": "Цунна блоктоьхана куьйгалхо",
        "blocklist-params": "Блоктохаран параметраш",
        "blocklist-reason": "Бахьна:",
-       "ipblocklist-submit": "Лаха",
+       "ipblocklist-submit": "Лахар",
        "ipblocklist-localblock": "Локальни блоктохар",
        "ipblocklist-otherblocks": "{{PLURAL:$1|Кхин блоктохар|Кхин блоктохарш}}",
        "infiniteblock": "хан чаккхе йоцуш",
        "movepagetext": "Бухахь йолу форманца агӀон цӀе хийцало. Цул совнах цуьна хийцаман тептар кхоьчу метте доккха. Хьалхалера цӀарахь хиръю керла кхоьллина агӀонан хьажораг.\n\nХьовсалаш [[Special:DoubleRedirects|шалха]] а [[Special:BrokenRedirects|йохна хьажоргаш]] юй техь аьлла.\n\nШу жоьпехь ду хьажоргаш нийса некъ гойтуш хиларан.\n\nТидам бе хьалхалера агӀон цӀе ‘’’хийцалур яц’’’ иштта цӀе йолу агӀо йолуш елахь. Юкъардаккхар: йолуш йолу агӀо кхоьчухьа хьажораг елахь, я еса елахь а, цуьна хийцаме истори яцахь а.\n\nИ бохург ду шун агӀонан цӀе юха а хьалха хилларгчунтӀе хийца йиш ю, амма йолуш йолу агӀо дӀаяккха йиш яц.\n\n'''ДӀАХЬЕДАР!'''\n\nЦӀе хийцар бахьнехь гӀаръяьлла агӀонашна дукха дагахь боцу хийцамаш хила тарло. Цундела цӀе хийцале шеш хила тарлучу тӀехьонашах кхета аьлла тешна хила.",
        "movepagetext-noredirectfixer": "Бухахь йолу форманца агӀон цӀе хийцало. Цул совнах цуьна хийцаман тептар кхоьчу метте доккха. Хьалхалера цӀарахь хиръю керла кхоьллина агӀонан хьажораг.\n\nХьовсалаш [[Special:DoubleRedirects|шалха]] а [[Special:BrokenRedirects|йохна хьажоргаш]] юй техь аьлла.\n\nШу жоьпехь ду хьажоргаш нийса некъ гойтуш хиларан.\n\nТидам бе хьалхалера агӀон цӀе ‘’’хийцалур яц’’’ иштта цӀе йолу агӀо йолуш елахь. Юкъардаккхар: йолуш йолу агӀо кхоьчухьа хьажораг елахь, я еса елахь а, цуьна хийцаме истори яцахь а.\n\nИ бохург ду шун агӀонан цӀе юха а хьалха хилларгчунтӀе хийца йиш ю, амма йолуш йолу агӀо дӀаяккха йиш яц.\n\n'''ДӀАХЬЕДАР!'''\n\nЦӀе хийцар бахьнехь гӀаръяьлла агӀонашна дукха дагахь боцу хийцамаш хила тарло. Цундела цӀе хийцале шеш хила тарлучу тӀехьонашах кхета аьлла тешна хила.",
        "movepagetalktext": "ТӀе хӀоьттина йолу дийцаре агӀо ишта цӀе хийцина хира ю, '''цхьа йолу ханчохь, маца:'''\n\n*Йаьсса йоцу дийцаре агӀо йолуш ю оцу цӀарца йа\n*Ахьа къастаман харжам цабиняхь а къастам хӀотточехь.\n\nИшта чу ханчохь, ахьа дехьа яккха йезар ю йа куьйга хӀоттайар, нагахь иза хьашт йалахь.",
-       "movearticle": "ЦӀе хийца хӀокху агӀон",
+       "movearticle": "ЦӀе хийца агӀон",
        "moveuserpage-warning": "'''Тергам бе.''' Хьо декъашхочун агӀона цӀе хийца гӀерта. Дехар до, тергам бе, декъашхочун агӀона цӀе бен хийца лур яц, декъашхочун дӀаяздаран цӀе хийца лур яц.",
        "movecategorypage-warning": "<strong>ДӀахьедар:</strong> Хьо категорин агӀон цӀе хийца гӀерта. Дехар до, терго йе, хӀокху агӀона бен цӀе хуьйцур яц, шира чу категори чура массо агӀонаш керла категори чу йохур <em>яц</em>.",
        "movenologintext": "АгӀона цӀе хийца [[Special:UserLogin|системин чугӀо]].",
        "newimages-legend": "Литтар",
        "newimages-showbots": "Гайта боташ чуяьхна файлаш",
        "noimages": "Суьрташ дац.",
-       "ilsubmit": "Лаха",
+       "ilsubmit": "Лахар",
        "bydate": "терахьашца",
        "sp-newimages-showfrom": "Гайта керла файлаш $2, $1 тӀера дуьйна",
        "seconds-abbrev": "$1 оцу",
        "redirect-legend": "Файлан я агӀона тӀера дӀасхьажор",
        "redirect-summary": "ХӀара агӀо лело йиш ю файлан я агӀона тӀера дӀасхьажош.",
        "redirect-submit": "Дехьа гӀо",
-       "redirect-lookup": "Лаха:",
+       "redirect-lookup": "Лахар:",
        "redirect-value": "МаьӀна:",
        "redirect-user": "Декъашхочун ID",
        "redirect-page": "АгӀона ID",
        "fileduplicatesearch-summary": "Лаха цхьатера йолу файлаш хэш-кодаца.",
        "fileduplicatesearch-legend": "Цхьатера ерш лахар",
        "fileduplicatesearch-filename": "Файлан цӀе:",
-       "fileduplicatesearch-submit": "Лаха",
+       "fileduplicatesearch-submit": "Лахар",
        "fileduplicatesearch-info": "$1 × $2 пиксель<br />Файлан барам: $3<br />MIME-тайп: $4",
        "fileduplicatesearch-result-1": "«$1» файлах тера хӀума яц.",
        "fileduplicatesearch-noresults": "ЦӀе «$1» йолуш файл цакарий.",
index a670048..b0200ec 100644 (file)
        "tog-shownumberswatching": "Anzahl der beobachtenden Benutzer anzeigen",
        "tog-oldsig": "Vorhandene Signatur:",
        "tog-fancysig": "Signatur als Wikitext behandeln (ohne automatische Verlinkung)",
-       "tog-uselivepreview": "Vorschau sofort anzeigen (experimentell)",
+       "tog-uselivepreview": "Vorschau sofort anzeigen",
        "tog-forceeditsummary": "Warnen, sofern beim Speichern die Zusammenfassung fehlt",
        "tog-watchlisthideown": "Eigene Bearbeitungen in der Beobachtungsliste ausblenden",
        "tog-watchlisthidebots": "Bearbeitungen durch Bots in der Beobachtungsliste ausblenden",
        "anoneditwarning": "<strong>Warnung:</strong> Du bist nicht angemeldet. Deine IP-Adresse wird öffentlich sichtbar, falls du Bearbeitungen durchführst. Wenn du dich <strong>[$1 anmeldest]</strong> oder <strong>[$2 ein Benutzerkonto erstellst]</strong>, werden deine Bearbeitungen zusammen mit anderen Beiträgen deinem Benutzernamen zugeordnet.",
        "anonpreviewwarning": "''Du bist nicht angemeldet. Beim Speichern wird deine IP-Adresse in der Versionsgeschichte aufgezeichnet.''",
        "missingsummary": "'''Hinweis:''' Du hast keine Zusammenfassung angegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Änderung ohne Zusammenfassung übernommen.",
+       "selfredirect": "<strong>Warnung:</strong> Du erstellst eine Weiterleitung auf den gleichen Artikel.\nWenn du erneut auf „{{int:savearticle}}“ klickst, wird die Weiterleitung erstellt.",
        "missingcommenttext": "Dein Abschnitt enthält keinen Text.",
        "missingcommentheader": "'''Achtung:''' Du hast kein Betreff/Überschrift eingegeben. Wenn du erneut auf „{{int:savearticle}}“ klickst, wird deine Bearbeitung ohne Überschrift gespeichert.",
        "summary-preview": "Vorschau der Zusammenfassungszeile:",
        "ninterwikis": "{{PLURAL:$1|Ein Interwikilink|$1 Interwikilinks}}",
        "nlinks": "{{PLURAL:$1|1 Link|$1 Links}}",
        "nmembers": "{{PLURAL:$1|1 Eintrag|$1 Einträge}}",
-       "nmemberschanged": "$1 → {{PLURAL:$2|Ein Mitglied|$2 Mitglieder}}",
+       "nmemberschanged": "$1 → $2 {{PLURAL:$2|Mitglied|Mitglieder}}",
        "nrevisions": "{{PLURAL:$1|1 Bearbeitung|$1 Bearbeitungen}}",
        "nviews": "{{PLURAL:$1|1 Abfrage|$1 Abfragen}}",
        "nimagelinks": "Verwendet auf {{PLURAL:$1|einer Seite|$1 Seiten}}",
index 0f4bca7..82e3fdf 100644 (file)
@@ -33,7 +33,7 @@
        "tog-shownumberswatching": "Fà vèder al nómer ed j utèint che gh'àn la pàgina sòta uservasiòun",
        "tog-oldsig": "La fîrma 'd adèsa",
        "tog-fancysig": "Trâta la fîrma cme wikitèst (sèinsa colegamèint avtomâtich)",
-       "tog-uselivepreview": "Permèt la funsiòun \"Live preview\" (guêrda préma 'd salvêr dal vîv - in sperimèint)",
+       "tog-uselivepreview": "Permèt la funsiòun \"Live preview\" (guêrda préma 'd salvêr in dirèta)",
        "tog-forceeditsummary": "Dmânda s'l'è vèira che al câmp argumèint l' é vōd",
        "tog-watchlisthideown": "Lōga al mé mudéfichi int i  tgnû 'd ôc specêl",
        "tog-watchlisthidebots": "Lōga al mudéfichi di bot int i tgnû 'd ôc specêl",
        "anoneditwarning": "<strong>Atèinti:</strong> An n'é mìa stê fât l'ingrès. S' ét farê dal mudéfichi al tó indirés IP al srà vést da tót. Se <strong>[$1 và dèinter]</strong> o <strong>[$2 fà 'n' utèinsa]</strong>, al tô mudéfichi a srân sgnêdi al tó nòm utèint, insèm a êter benefési.",
        "anonpreviewwarning": "\"An n'é mìa stê fât l'ingrès. Mèinter es sêlva la pàgina, l'indirés IP al srà sgnê int la stòria 'd la pàgina.\"",
        "missingsummary": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda cun al mutîv vōd.",
+       "selfredirect": "<strong>Ateinti:</strong>t'é drē fêr un rinvéi a l'istèsa vōş. S'ét fê cléch incòra in sém a \"{{int:savearticle}}\", al rinvéi al gnirà fât",
        "missingcommenttext": "Scréver un cumèint ché sòta.",
        "missingcommentheader": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv/al tétol de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa tétol.",
        "summary-preview": "Guêrda préma sûnt:",
        "right-protect": "Câmbia i livē 'd prutesiòun e mudéfica 'l pàgini prutèti in ripetisiòun",
        "right-editprotected": "Mudéfica 'l pàgini prutèti cun \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "Mudéfica 'l pàgini prutèti cun \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Mudéfica al mudèl ed còl ché dèinter int 'na pàgina.",
        "right-editinterface": "Mudéfica al colegamèint tra sistēma e utèint",
        "right-editusercssjs": "Mudéfica i file CSS e JS 'd êter utèint",
        "right-editusercss": "Mudéfica i file CSS 'd êter utèint",
        "right-editmyprivateinfo": "Câmbia 'l tō infurmasiòun personêli (per eşèimpi: indirés ed pôsta eletrônica, nòm vèira)",
        "right-editmyoptions": "Câmbia al tō preferèinsi",
        "right-rollback": "Scanşèla a la şvêlta al mudéfichi ed l'ûltèint ch'l'à mudifichê 'na pàgina pariculêra",
+       "right-markbotedits": "Sègna al mudéfichi da turnêr a mèter cme préma cme fâti da 'na mâchina in avtomâtich",
+       "right-noratelimit": "An n'é mìa ublighê al lémit 'd asiòun",
+       "right-import": "Côpia dal pàgini da 'd j êter wiki",
        "newuserlogpage": "Utèint nōv",
        "action-read": "lēzer cla pàgina ché",
        "action-edit": "Mudifichêr cla pàgina ché",
index 883dbd7..9b60639 100644 (file)
        "permalink": "Σταθερός σύνδεσμος",
        "print": "Εκτύπωση",
        "view": "Προβολή",
-       "view-foreign": "Î\94είÏ\84ε στο $1",
+       "view-foreign": "ΠÏ\81οβολή στο $1",
        "edit": "Επεξεργασία",
        "edit-local": "Επεξεργασία τοπικής περιγραφής",
        "create": "Δημιουργία",
-       "create-local": "ΠÏ\81οÏ\83θέÏ\83Ï\84ε Ï\84οÏ\80ική Ï\80εÏ\81ιγÏ\81αÏ\86ή",
+       "create-local": "ΠÏ\81οÏ\83θήκη Ï\84οÏ\80ικήÏ\82 Ï\80εÏ\81ιγÏ\81αÏ\86ήÏ\82",
        "editthispage": "Επεξεργασία αυτής της σελίδας",
        "create-this-page": "Δημιουργία αυτής της σελίδας",
        "delete": "Διαγραφή",
        "filepage-nofile-link": "Δεν υπάρχει τέτοιο αρχείο, αλλἀ μπορείτε να [$1 το επιφορτώσετε].",
        "uploadnewversion-linktext": "Φορτώστε μια νέα έκδοση αυτού του αρχείου",
        "shared-repo-from": "από το $1",
-       "shared-repo": "ένα ÎºÎ¿Î¹Î½Ï\8c ÎµÎ½Î±Ï\80οθεÏ\84ήÏ\81ιο",
+       "shared-repo": "κοινό εναποθετήριο",
        "shared-repo-name-wikimediacommons": "Wikimedia Commons",
        "upload-disallowed-here": "Δεν μπορείτε να αντικαταστήσετε αυτό το αρχείο.",
        "filerevert": "Επαναφορά $1",
index 75bdb11..52e3a78 100644 (file)
@@ -28,7 +28,7 @@
        "tog-shownumberswatching": "Show the number of watching users",
        "tog-oldsig": "Existing signature:",
        "tog-fancysig": "Treat signature as wikitext (without an automatic link)",
-       "tog-uselivepreview": "Use live preview (experimental)",
+       "tog-uselivepreview": "Use live preview",
        "tog-forceeditsummary": "Prompt me when entering a blank edit summary",
        "tog-watchlisthideown": "Hide my edits from the watchlist",
        "tog-watchlisthidebots": "Hide bot edits from the watchlist",
        "anoneditwarning": "<strong>Warning:</strong> You are not logged in. Your IP address will be publicly visible if you make any edits. If you <strong>[$1 log in]</strong> or <strong>[$2 create an account]</strong>, your edits will be attributed to your username, along with other benefits.",
        "anonpreviewwarning": "<em>You are not logged in. Saving will record your IP address in this page's edit history.</em>",
        "missingsummary": "<strong>Reminder:</strong> You have not provided an edit summary.\nIf you click \"{{int:savearticle}}\" again, your edit will be saved without one.",
+       "selfredirect": "<strong>Warning:</strong> You are creating redirect to the same article.\nIf you click \"{{int:savearticle}}\" again, the redirect will be created.",
        "missingcommenttext": "Please enter a comment below.",
        "missingcommentheader": "<strong>Reminder:</strong> You have not provided a subject/headline for this comment.\nIf you click \"{{int:savearticle}}\" again, your edit will be saved without one.",
        "summary-preview": "Summary preview:",
index b1a6743..38f9981 100644 (file)
        "tog-shownumberswatching": "Mostrar el número de usuarios que la vigilan",
        "tog-oldsig": "Firma actual:",
        "tog-fancysig": "Tratar la firma como wikitexto (sin un enlace automático)",
-       "tog-uselivepreview": "Usar previsualización dinámica (experimental)",
+       "tog-uselivepreview": "Usar previsualización dinámica",
        "tog-forceeditsummary": "Avisarme cuando grabe la página sin introducir un resumen de edición",
        "tog-watchlisthideown": "Ocultar mis ediciones en la lista de seguimiento",
        "tog-watchlisthidebots": "Ocultar las ediciones de bots en la lista de seguimiento",
        "right-protect": "Cambiar niveles de protección y editar páginas protegidas en cascada",
        "right-editprotected": "Editar páginas protegidas como «{{int:protect-level-sysop}}»",
        "right-editsemiprotected": "Editar páginas protegidas como «{{int:protect-level-autoconfirmed}}»",
+       "right-editcontentmodel": "Editar el modelo de contenido de una página",
        "right-editinterface": "Editar la interfaz de usuario",
        "right-editusercssjs": "Editar las páginas de CSS y JavaScript de otros usuarios",
        "right-editusercss": "Editar las páginas de CSS de otros usuarios",
        "action-viewmywatchlist": "Ver tu lista de seguimiento",
        "action-viewmyprivateinfo": "ver tu información privada",
        "action-editmyprivateinfo": "Editar tu información privada",
+       "action-editcontentmodel": "editar el modelo de contenido de una página",
        "nchanges": "$1 {{PLURAL:$1|cambio|cambios}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde la última visita}}",
        "enhancedrc-history": "historial",
        "unknown_extension_tag": "Etiqueta desconocida «$1»",
        "duplicate-defaultsort": "'''Atención:''' La clave de ordenamiento predeterminada «$2» anula la clave de ordenamiento anterior «$1».",
        "duplicate-displaytitle": "<strong>Advertencia:</strong> El título visualizado \"$2\" sobreescribe al anterior \"$1\".",
+       "invalid-indicator-name": "<strong>Error:</strong> el atributo <code>name</code> de los indicadores de estado de página no debe estar vacío.",
        "version": "Versión",
        "version-extensions": "Extensiones instaladas",
        "version-skins": "Temas instalados",
index fe227ff..7adc540 100644 (file)
        "virus-badscanner": "Viga konfiguratsioonis: tundmatu viirusetõrje: ''$1''",
        "virus-scanfailed": "skaneerimine ebaõnnestus (veakood $1)",
        "virus-unknownscanner": "tundmatu viirusetõrje:",
-       "logouttext": "'''Oled nüüd välja loginud.'''\n\nPane tähele, et seni kuni sa pole oma võrgulehitseja puhvrit tühjendanud, võidakse mõni lehekülg endiselt kuvada nii nagu oleksid ikka sisse logitud.",
+       "logouttext": "<strong>Oled nüüd välja loginud.</strong>\n\nPane tähele, et seni, kuni sa pole veebilehitseja puhvrit tühjendanud, võidakse mõni lehekülg endiselt kuvada nii nagu oleksid ikka sisse logitud.",
        "welcomeuser": "Tere tulemast, $1!",
        "welcomecreation-msg": "Sinu konto on loodud.\nÄra unusta seada oma {{GRAMMAR:genitive|{{SITENAME}}}} [[Special:Preferences|eelistusi]].",
        "yourname": "Kasutajanimi:",
        "userlogin": "Sisselogimine või kasutajakonto loomine",
        "userloginnocreate": "Sisselogimine",
        "logout": "Logi välja",
-       "userlogout": "Logi välja",
+       "userlogout": "Väljalogimine",
        "notloggedin": "Sisse logimata",
        "userlogin-noaccount": "Kas sul pole kontot?",
        "userlogin-joinproject": "Ühine projektiga {{SITENAME}}",
index 03ca92b..eaea3f6 100644 (file)
@@ -40,7 +40,9 @@
                        "Omid.koli",
                        "Alirezaaa",
                        "Mogoeilor",
-                       "Hosseinblue"
+                       "Hosseinblue",
+                       "فلورانس",
+                       "Saeidpourbabak"
                ]
        },
        "tog-underline": "خط کشیدن زیر پیوندها:",
        "filerenameerror": "نشد پروندهٔ «$1» به «$2» تغییر نام یابد.",
        "filedeleteerror": "نشد پروندهٔ «$1» حذف شود.",
        "directorycreateerror": "نشد مسیر $1 را ایجاد کرد.",
+       "directoryreadonlyerror": "دایرکتوری \"$1\" فقط خواندنی است.",
+       "directorynotreadableerror": "دایرکتوری \"$1\" قابل خواندن نیست.",
        "filenotfound": "پروندهٔ «$1» یافت نشد.",
        "unexpected": "مقدار غیرمنتظره: «$1»=«$2».",
        "formerror": "خطا: نمی‌توان فرم را فرستاد.",
        "right-protect": "تغییر میزان محافظت صفحات و ویرایش صفحات محافظت‌شده آبشاری",
        "right-editprotected": "ویرایش صفحه‌های محافظت‌شده به‌عنوان «{{int:protect-level-sysop}}»",
        "right-editsemiprotected": "ویرایش صفحه حفاظت‌شده به عنوان \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "ویرایش مدل محتوای یک صفحه",
        "right-editinterface": "ویرایش واسط کاربری",
        "right-editusercssjs": "ویرایش صفحه‌های CSS و JS دیگر کاربرها",
        "right-editusercss": "ویرایش صفحه‌های CSS دیگر کاربرها",
        "action-viewmywatchlist": "فهرست پیگیری‌های خود را ببینید",
        "action-viewmyprivateinfo": "اطلاعات خصوصی خود را ببینید",
        "action-editmyprivateinfo": "اطلاعات خصوصی خود را ویرایش کنید",
+       "action-editcontentmodel": "ویرایش مدل محتوای یک صفحه",
        "nchanges": "$1 تغییر",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|از آخرین بازدید}}",
        "enhancedrc-history": "تاریخچه",
        "move-page": "انتقال $1",
        "move-page-legend": "انتقال صفحه",
        "movepagetext": "با استفاده از فرم زیر نام صفحه تغییر خواهد کرد، و تمام تاریخچه‌اش به نام جدید منتقل خواهد شد.\nعنوان قدیمی تبدیل به یک صفحهٔ تغییرمسیر به عنوان جدید خواهد شد.\nشما می‌توانید تغییرمسیرهایی که به عنوان اصلی اشاره دارند را به صورت خودکار به‌روزرسانی کنید.\nپیوندهای که به عنوان صفحهٔ قدیمی وجود دارند، تغییر نخواهند کرد؛ حتماً تغییرمسیرهای [[Special:DoubleRedirects|دوتایی]] یا [[Special:BrokenRedirects|خراب]] را بررسی کنید.\n'''شما''' مسئول اطمینان از این هستید که پیوندها هنوز به همان‌جایی که قرار است بروند.\n\nتوجه کنید که اگر از قبل صفحه‌ای در عنوان جدید وجود داشته باشد صفحه منتقل '''نخواهد شد'''،\nمگر این آخرین ویرایش تغییرمسیر باشد و در  تاریخچهٔ ویرایشی نداشته باشد.\nاین یعنی اگر اشتباه کردید می‌توانید صفحه را به همان جایی که از آن منتقل شده بود برگردانید، و این که نمی‌توانید روی صفحات موجود بنویسید.\n\n'''هشدار!'''\nانتقال صفحات به نام جدید ممکن است تغییر اساسی و غیرمنتظره‌ای برای صفحات محبوب باشد؛\nلطفاً مطمئن شوید که قبل از انتقال دادن صفحه، عواقب این کار را درک می‌کنید.",
-       "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پیوندها به جایی اشاره می‌کنند که قرار است بروند.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n'''هشدار!'''\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
+       "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پس از انتقال، پیوندها به عنوان پیشین به جایی منتهی می‌شوند که باید.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید یک صفحه را به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n'''هشدار!'''\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
        "movepagetalktext": "صفحهٔ بحث مربوط، اگر وجود داشته باشد، بطور خودکار همراه با مقالهٔ اصلی منتقل خواهد شد '''مگر اینکه''' :\n* در حال انتقال صفحه از این فضای نام به فضای نام دیگری باشید،\n* یک صفحهٔ بحث غیرخالی تحت این نام جدید وجود داشته باشد، یا\n* جعبهٔ زیر را تیک نزده باشید.\n\nدر این حالات، باید صفحه را بطور دستی انتقال داده و یا محتویات دو صفحه را با ویرایش ادغام کنید.",
        "movearticle": "انتقال صفحه:",
        "moveuserpage-warning": "'''هشدار:''' شما در حال انتقال دادن یک صفحهٔ کاربر هستید. توجه داشته باشید که تنها صفحه منتقل می‌شود و نام کاربر تغییر '''نمی‌یابد'''.",
        "specialpages-group-wiki": "داده و ابزارها",
        "specialpages-group-redirects": "صفحه‌های ویژهٔ تغییرمسیر دهنده",
        "specialpages-group-spam": "ابزارهای هرزنگاری",
+       "specialpages-group-developer": "ابزارهای توسعه‌دهندگان",
        "blankpage": "صفحهٔ خالی",
        "intentionallyblankpage": "این صفحه به طور عمدی خالی گذاشته شده است.",
        "external_image_whitelist": " #این سطر را همان‌گونه که هست رها کنید<pre>\n#عبارت‌های باقاعده (regex) را در زیر قرار دهید (فقط بخشی که بین // قرار می‌گیرد)\n#آن‌ها با نشانی اینترنتی تصاویر خارجی پیوند داده شده تطبیق داده می‌شوند\n#مواردی که مطابق باشند به صورت تصویر نمایش می‌یابند، و در غیر این صورت تنها یک پیوند به تصویر نمایش می‌یابد\n#سطرهایی که با # آغاز شوند به عنوان توضیحات در نظر گرفته می‌شوند\n#این سطرها به کوچکی و بزرگی حروف حساس هستند\n\n#عبارت‌های باقاعده (regex)  را زیر این سطر قرار دهید. این سطر را همان‌گونه که هست رها کنید</pre>",
        "expand_templates_generate_xml": "نمایش درخت تجزیهٔ XML",
        "expand_templates_generate_rawhtml": "نمایش اچ‌تی‌ام‌ال خام",
        "expand_templates_preview": "پیش‌نمایش",
+       "expand_templates_preview_fail_html": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
+       "expand_templates_preview_fail_html_anon": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
        "pagelanguage": "صفحه انتخاب زبان",
        "pagelang-name": "صفحه",
        "pagelang-language": "زبان",
index 9a78cc6..79a45ea 100644 (file)
@@ -41,7 +41,8 @@
                        "아라",
                        "Syreeni",
                        "MrTapsa",
-                       "SMAUG"
+                       "SMAUG",
+                       "SuperPete"
                ]
        },
        "tog-underline": "Linkkien alleviivaus:",
@@ -69,7 +70,7 @@
        "tog-shownumberswatching": "Näytä sivua tarkkailevien käyttäjien määrä",
        "tog-oldsig": "Nykyinen allekirjoitus:",
        "tog-fancysig": "Muotoilematon allekirjoitus ilman automaattista linkkiä",
-       "tog-uselivepreview": "Käytä välitöntä esikatselua (kokeellinen)",
+       "tog-uselivepreview": "Käytä välitöntä esikatselua",
        "tog-forceeditsummary": "Huomauta minua, jos en ole kirjoittanut yhteenvetoa",
        "tog-watchlisthideown": "Piilota omat muokkaukset tarkkailulistalta",
        "tog-watchlisthidebots": "Piilota bottien muokkaukset tarkkailulistalta",
        "anoneditwarning": "<strong>Varoitus:</strong> Et ole kirjautunut sisään. IP-osoitteesi näkyy julkisesti kaikille, jos muokkaat. Jos <strong>[$1 kirjaudut sisään]</strong> tai <strong>[$2 luot tunnuksen]</strong>, muokkauksesi kirjataan käyttäjätunnuksesi tekemiksi ja samalla saat käyttöösi hyödyllisiä välineitä.",
        "anonpreviewwarning": "''Et ole kirjautunut sisään. Tallentaminen kirjaa IP-osoitteesi tämän sivun muutoshistoriaan.''",
        "missingsummary": "Et ole antanut yhteenvetoa. Jos valitset Tallenna uudelleen, niin muokkauksesi tallennetaan ilman yhteenvetoa.",
+       "selfredirect": "<strong>Varoitus:</strong> Olet tekemässä uudelleenohjausta samaan artikkeliin. Jos painat toimintoa \"{{int:savearticle}}\" uudestaan, tämä ohjaussivu luodaan.",
        "missingcommenttext": "Kirjoita viesti alle.",
        "missingcommentheader": "Et ole antanut otsikkoa kommentillesi. Napsauta ”{{int:savearticle}}”, jos et halua antaa otsikkoa.",
        "summary-preview": "Yhteenvedon esikatselu:",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
        "duplicate-args-category": "Sivut, jotka käyttävät kaksinkertaisia argumentteja mallinekutsuissa",
+       "duplicate-args-category-desc": "Tämä sivu sisältää sellaisia mallinekutsuja, jotka käyttävät kaksi kertaa samaa argumenttia kuten <nowiki>{{foo|bar=1|bar=2}}</nowiki></code> taikka <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Tällä sivulla on liian monta hitaiden laajennusfunktioiden kutsua.\nKutsuja pitäisi olla alle $2 {{PLURAL:$2|kappale|kappaletta}}, mutta nyt niitä on $1 {{PLURAL:$1|kappale|kappaletta}}.",
        "expensive-parserfunction-category": "Sivut, joissa on liian monta vaativaa jäsenninfunktiota",
        "post-expand-template-inclusion-warning": "'''Varoitus:''' Sisällytettyjen mallineiden koko on liian suuri.\nJoitakin mallineita ei ole sisällytetty.",
        "right-protect": "Muuttaa suojaustasoja ja muokata tarttuvasti suojattuja sivuja",
        "right-editprotected": "Muokata sivuja, jotka on suojattu tasolle \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "Muokata sivuja, jotka on suojattu tasolle \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Muokata sivun sisältömallia (content model)",
        "right-editinterface": "Muokata käyttöliittymätekstejä",
        "right-editusercssjs": "Muokata toisten käyttäjien CSS- ja JavaScript-tiedostoja",
        "right-editusercss": "Muokata toisten käyttäjien CSS-tiedostoja",
        "action-viewmywatchlist": "tarkastella tarkkailulistaasi",
        "action-viewmyprivateinfo": "katsoa omia yksityisiä tietojasi",
        "action-editmyprivateinfo": "muokata omia yksityisiä tietojasi",
+       "action-editcontentmodel": "muokata sivun sisältömallia",
        "nchanges": "$1 {{PLURAL:$1|muutos|muutosta}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|viimeisen käynnin jälkeen}}",
        "enhancedrc-history": "historia",
        "specialpages-group-wiki": "Tiedot ja työkalut",
        "specialpages-group-redirects": "Ohjaavat toimintosivut",
        "specialpages-group-spam": "Roskalinkkien (spam) työkalut",
+       "specialpages-group-developer": "Kehittäjien työkalut",
        "blankpage": "Tyhjä sivu",
        "intentionallyblankpage": "Tämä sivu on tarkoituksellisesti tyhjä.",
        "external_image_whitelist": " #Älä muuta tätä riviä lainkaan.<pre>\n#Laita säännöllisten lausekkeiden palaset (vain osa, joka menee //-merkkien väliin) alle\n#Niitä verrataan ulkoisten (suoralinkitettyjen) kuvien URLeihin\n#Ne jotka sopivat, näytetään kuvina, muutoin kuviin näytetään vain linkit\n#Rivit, jotka alkavat #-merkillä ovat kommentteja\n#Tämä on riippumaton kirjainkoosta\n\n#Laita kaikki säännöllisten lausekkeiden palaset tämän rivit yläpuolelle. Älä muuta tätä riviä lainkaan</pre>",
        "expand_templates_generate_xml": "Näytä XML-jäsennyspuu",
        "expand_templates_generate_rawhtml": "Näytä raaka HTML",
        "expand_templates_preview": "Esikatselu",
+       "expand_templates_preview_fail_html": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska istunnon tiedot ovat kadonneet, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, yritä uudestaan.</strong>\nJos esikatselu ei vieläkään toimi, kokeile [[Special:UserLogout|kirjautua ulos]] ja sen jälkeen kirjaudu uudestaan sisään.",
+       "expand_templates_preview_fail_html_anon": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska et ole kirjautunut sisään, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, [[Special:UserLogin|kirjaudu sisään]] ja yritä uudestaan.</strong>",
        "pagelanguage": "Sivun kielen valinta",
        "pagelang-name": "Sivu",
        "pagelang-language": "Kieli",
        "log-name-pagelang": "Kielenvaihtoloki",
        "log-description-pagelang": "Tämä on loki, johon merkitään muutokset sivujen kieliasetuksissa.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|muutti}} sivun kieltä sivulla $3 kielestä $4 kieleksi $5.",
-       "default-skin-not-found": "Hupsista! Oletuksena tuleva ulkoasu sinun wikillesi, joka on määritelty koodissa <code dir=\"ltr\">$wgDefaultSkin</code> muotoon <code>$1</code>, ei ole saatavilla.\n\n\n<strong>Alla on ohjeita englanniksi:</strong>\n\n\nYour installation seems to include the following skins. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable them and choose the default.\n\n$2\n\n; If you have just installed MediaWiki: \n: You probably installed from git, or directly from the source code using some other method. This is expected. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by: :* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it. \n:* Cloning one of the <code>mediawiki/skins/*</code> repositories via git into the <code>skins/</code> directory of your MediaWiki installation. \n: Doing this should not interfere with your git repository if you're a MediaWiki developer.\n\n\n; If you have just upgraded MediaWiki: \n: MediaWiki 1.24 and newer no longer automatically enables installed skins (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). You can paste the following lines into <code>LocalSettings.php</code> to enable all currently installed skins:\n\n<pre>$3</pre>\n\n; If you have just modified <code>LocalSettings.php</code>: \n: Double-check the skin names for typos.",
-       "default-skin-not-found-no-skins": "Hupsista! Oletusulkoasua sinun wikillesi ei ole saatavilla. Se on määritelty ulkoasuksi <code>$1</code> kohteessa <code>$wgDefaultSkin</code>.\n\nSinulla ei ole lainkaan asennettuja ulkoasuja. (You have no installed skins.)\n\nAlla on lisäohjeita englanniksi:\n\n\n; If you have just installed or upgraded MediaWiki: \n\n: You probably installed from git, or directly from the source code using some other method. This is expected. MediaWiki 1.24 and newer doesn't include any skins in the main repository. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by: \n\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it. \n\n:* Cloning one of the <code>mediawiki/skins/*</code> repositories via git into the <code dir=\"ltr\">skins/</code> directory of your MediaWiki installation. \n\n: Doing this should not interfere with your git repository if you're a MediaWiki developer. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable skins and choose the default.",
+       "default-skin-not-found": "Hupsista! Oletuksena tuleva ulkoasu sinun wikillesi, joka on määritelty koodissa <code dir=\"ltr\">$wgDefaultSkin</code> muotoon <code>$1</code>, ei ole saatavilla.\n\n\n<strong>Alla on ohjeita englanniksi:</strong>\n\n\nYour installation seems to include the following skins. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable them and choose the default.\n\n$2\n\n; If you have just installed MediaWiki: \n: You probably installed from git, or directly from the source code using some other method. This is expected. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by: :* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it. \n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* Cloning one of the <code>mediawiki/skins/*</code> repositories via git into the <code>skins/</code> directory of your MediaWiki installation. \n: Doing this should not interfere with your git repository if you're a MediaWiki developer.\n\n\n; If you have just upgraded MediaWiki: \n: MediaWiki 1.24 and newer no longer automatically enables installed skins (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). You can paste the following lines into <code>LocalSettings.php</code> to enable all currently installed skins:\n\n<pre>$3</pre>\n\n; If you have just modified <code>LocalSettings.php</code>: \n: Double-check the skin names for typos.",
+       "default-skin-not-found-no-skins": "Hupsista! Oletusulkoasua sinun wikillesi ei ole saatavilla. Se on määritelty ulkoasuksi <code>$1</code> kohteessa <code>$wgDefaultSkin</code>.\n\nSinulla ei ole lainkaan asennettuja ulkoasuja. (You have no installed skins.)\n\nAlla on lisäohjeita englanniksi:\n\n\n; If you have just installed or upgraded MediaWiki: \n\n: You probably installed from git, or directly from the source code using some other method. This is expected. MediaWiki 1.24 and newer doesn't include any skins in the main repository. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by: \n\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it. \n\n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n\n:* Cloning one of the <code>mediawiki/skins/*</code> repositories via git into the <code dir=\"ltr\">skins/</code> directory of your MediaWiki installation. \n\n: Doing this should not interfere with your git repository if you're a MediaWiki developer. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable skins and choose the default.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (käytössä)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''ei käytössä''')",
        "mediastatistics": "Median tilastotiedot",
index 3f65920..3a09b82 100644 (file)
                        "SnowedEarth",
                        "Orikrin1998",
                        "Automatik",
-                       "Elodark"
+                       "Elodark",
+                       "Macofe"
                ]
        },
        "tog-underline": "Souligner les liens :",
        "tog-shownumberswatching": "Afficher le nombre d'utilisateurs qui suivent la page",
        "tog-oldsig": "Signature existante :",
        "tog-fancysig": "Traiter la signature comme du wikitexte (sans lien automatique)",
-       "tog-uselivepreview": "Utiliser l’aperçu rapide (expérimental)",
+       "tog-uselivepreview": "Utiliser l’aperçu rapide",
        "tog-forceeditsummary": "M'avertir lorsque je n'ai pas spécifié de résumé de modification",
        "tog-watchlisthideown": "Masquer mes propres modifications dans la liste de suivi",
        "tog-watchlisthidebots": "Masquer les modifications faites par des robots dans la liste de suivi",
index fe11a5c..329f902 100644 (file)
        "wlheader-enotif": "Tha brathan-naidheachd air a' phost-d an comas.",
        "wlheader-showupdated": "Tha clò <strong>trom</strong> air duilleagan a chaidh atharrachadh on turas mu dheireadh a thadhail thu orra.",
        "wlnote": "Chì thu gu h-ìosal {{PLURAL:$1|a' $1 mhùthadh|an $1 mhùthadh|na $1 mùthaidhean|am $1 mùthadh}} mu dheireadh san {{PLURAL:$2|$2 uair a thìde|$2 uair a thìde|$2 uairean a thìde|$2 uair a thìde}} mu dheireadh, mar a bha e $3, $4.",
-       "wlshowlast": "Seall na $1 uairean a thìde mu dheireadh $2 làithean mu dheireadh",
+       "wlshowlast": "Seall na $1 uairean a thìde mu dheireadh $2 làithean mu dheireadh $3",
        "watchlist-options": "Roghainnean mo chlàir-faire",
        "watching": "'Ga chur air a' chlàr-fhaire...",
        "unwatching": "A' toirt far a' chlàir-fhaire...",
        "exif-gpsdifferential": "Ceartachadh diofarail GPS",
        "exif-coordinate-format": "$1° $2′ $3″ $4",
        "exif-jpegfilecomment": "Beachd faidhle JPEG",
-       "exif-keywords": "Facalan-luirg",
+       "exif-keywords": "Faclan-luirg",
        "exif-worldregioncreated": "An roinn-dùthcha san deach an dealbh a thogail",
        "exif-countrycreated": "An dùthaich san deach an dealbh a thogail",
        "exif-countrycodecreated": "Còd na dùthcha san deach an dealbh a thogail",
index a19f7fd..0fc292d 100644 (file)
@@ -55,7 +55,7 @@
        "tog-shownumberswatching": "הצגת מספר המשתמשים העוקבים",
        "tog-oldsig": "החתימה הנוכחית:",
        "tog-fancysig": "התייחסות לחתימה כקוד ויקי (ללא קישור אוטומטי)",
-       "tog-uselivepreview": "שימוש בתצוגה מקדימה מהירה (ניסיוני)",
+       "tog-uselivepreview": "שימוש בתצוגה מקדימה מהירה",
        "tog-forceeditsummary": "הצגת אזהרה בעת הכנסת תקציר עריכה ריק",
        "tog-watchlisthideown": "הסתרת העריכות שלי ברשימת המעקב",
        "tog-watchlisthidebots": "הסתרת עריכות של בוטים ברשימת המעקב",
        "passwordreset-capture-help": "אם תסמנו תיבה זו, הדואר האלקטרוני (יחד עם הסיסמה הזמנית) יוצג לכם במקביל לשליחתו למשתמש.",
        "passwordreset-email": "כתובת דוא\"ל:",
        "passwordreset-emailtitle": "פרטי חשבון ב{{grammar:תחילית|{{SITENAME}}}}",
-       "passwordreset-emailtext-ip": "מישהו (ככל הנראה אתם, מכתובת ה־IP מספר $1) ביקש איפוס של\nהסיסמה שלכם ב{{grammar:תחילית|{{SITENAME}}}} ($4). {{PLURAL:$3|חשבון המשתמש הבא|חשבונות המשתמש הבאים}}\nשייכים לכתובת הדואר האלקטרוני הזו:\n\n$2\n\n{{PLURAL:$3|סיסמה זמנית זו|סיסמאות זמניות אלה}} יפקעו תוך {{PLURAL:$5|יום|יומיים|$5 ימים}}.\nעליכם להיכנס ולבחור סיסמה חדשה עכשיו. אם מישהו אחר ביצע בקשה זו, או שנזכרתם בסיסמתכם\nהמקורית ואינכם רוצים עוד לשנות אותה, באפשרותכם להתעלם מהודעה זו ולהמשיך להשתמש בסיסמה\nהישנה.",
-       "passwordreset-emailtext-user": "המשתמש $1 ב{{GRAMMAR:תחילית|{{SITENAME}}}} ביקש איפוס של הסיסמה שלכם ב{{GRAMMAR:תחילית|{{SITENAME}}}}\n($4). {{PLURAL:$3|חשבון המשתמש הבא|חשבונות המשתמש הבאים}} שייכים לכתובת הדואר האלקטרוני הזו:\n\n$2\n\n{{PLURAL:$3|סיסמה זמנית זו|סיסמאות זמניות אלה}} יפקעו תוך {{PLURAL:$5|יום|יומיים|$5 ימים}}.\nעליכם להיכנס ולבחור סיסמה חדשה עכשיו. אם מישהו אחר ביצע בקשה זו, או שנזכרתם בסיסמתכם\nהמקורית ואינכם רוצים עוד לשנות אותה, באפשרותכם להתעלם מהודעה זו ולהמשיך להשתמש בסיסמה\nהישנה.",
+       "passwordreset-emailtext-ip": "מישהו (ככל הנראה אתם, מכתובת ה־IP מספר $1) ביקש איפוס של\nהסיסמה שלכם ב{{grammar:תחילית|{{SITENAME}}}} ($4). {{PLURAL:$3|חשבון המשתמש הבא|חשבונות המשתמש הבאים}}\nשייכים לכתובת הדואר האלקטרוני הזו:\n\n$2\n\n{{PLURAL:$3|סיסמה זמנית זו תפקע|סיסמאות זמניות אלה יפקעו}} תוך {{PLURAL:$5|יום|יומיים|$5 ימים}}.\nעליכם להיכנס ולבחור סיסמה חדשה עכשיו. אם מישהו אחר ביצע בקשה זו, או שנזכרתם בסיסמתכם\nהמקורית ואינכם רוצים עוד לשנות אותה, באפשרותכם להתעלם מהודעה זו ולהמשיך להשתמש בסיסמה\nהישנה.",
+       "passwordreset-emailtext-user": "ה{{GENDER:$1|משתמש|משתמשת}} $1 ב{{GRAMMAR:תחילית|{{SITENAME}}}} {{GENDER:$1|ביקש|ביקשה}} איפוס של הסיסמה שלכם ב{{GRAMMAR:תחילית|{{SITENAME}}}}\n($4). {{PLURAL:$3|חשבון המשתמש הבא שייך|חשבונות המשתמש הבאים שייכים}} לכתובת הדואר האלקטרוני הזו:\n\n$2\n\n{{PLURAL:$3|סיסמה זמנית זו תפקע|סיסמאות זמניות אלה יפקעו}} תוך {{PLURAL:$5|יום|יומיים|$5 ימים}}.\nעליכם להיכנס ולבחור סיסמה חדשה עכשיו. אם מישהו אחר ביצע בקשה זו, או שנזכרתם בסיסמתכם\nהמקורית ואינכם רוצים עוד לשנות אותה, באפשרותכם להתעלם מהודעה זו ולהמשיך להשתמש בסיסמה\nהישנה.",
        "passwordreset-emailelement": "שם משתמש: $1\nסיסמה זמנית: $2",
        "passwordreset-emailsent": "נשלח דואר אלקטרוני לאיפוס הסיסמה.",
        "passwordreset-emailsent-capture": "נשלח דואר אלקטרוני לאיפוס הסיסמה, והוא מוצג להלן.",
        "anoneditwarning": "<strong>אזהרה:</strong> אינכם מחוברים לחשבון. כתובת ה־IP שלכם תוצג בפומבי אם תבצעו עריכות כלשהן. אם <strong>[$1 תיכנסו לחשבון]</strong> או <strong>[$2 תיצרו חשבון]</strong>, העריכות שלכם תיוחסנה לשם המשתמש שלכם ותקבלו גם יתרונות אחרים.",
        "anonpreviewwarning": "''אינכם מחוברים לחשבון. שמירה תגרום לכתובת ה־IP שלכם להירשם בהיסטוריית העריכות של הדף.''",
        "missingsummary": "<strong>תזכורת:</strong> לא הזנת תקציר עריכה.\nלחיצה חוזרת על הכפתור \"{{int:savearticle}}\" תגרום לעריכה שלך להישמר בלעדיו.",
+       "selfredirect": "<strong>אזהרה:</strong> ניסית ליצור הפניה מדף זה לעצמו.\nלחיצה חוזרת על הכפתור \"{{int:savearticle}}\" תגרום להפניה להיווצר.",
        "missingcommenttext": "יש להקליד את ההודעה למטה.",
        "missingcommentheader": "<strong>תזכורת:</strong> לא הזנת נושא/כותרת להודעה זו.\nלחיצה חוזרת על הכפתור \"{{int:savearticle}}\" תגרום לעריכה שלך להישמר ללא נושא/כותרת.",
        "summary-preview": "תצוגה מקדימה של התקציר:",
        "userinvalidcssjstitle": "'''אזהרה:''' העיצוב \"$1\" אינו קיים.\nדפי .css ו־.js מותאמים אישית משתמשים בכותרת עם אותיות קטנות – למשל, {{ns:user}}:דוגמה/vector.css ולא {{ns:user}}:דוגמה/Vector.css.",
        "updated": "(מעודכן)",
        "note": "'''הערה:'''",
-       "previewnote": "'''זכרו שזו רק תצוגה מקדימה.'''\nהשינויים שלכם טרם נשמרו!",
+       "previewnote": "<strong>זִכרו שזו רק תצוגה מקדימה.</strong>\nהשינויים שלכם טרם נשמרו!",
        "continue-editing": "מעבר לאזור העריכה",
        "previewconflict": "תצוגה מקדימה זו מציגה כיצד ייראה הטקסט בחלון העריכה העליון, אם תבחרו לשמור אותו.",
        "session_fail_preview": "'''לא ניתן לבצע את עריכתכם עקב אובדן מידע הכניסה.'''\nאנא נסו שוב.\nאם זה לא עוזר, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית.",
index 23768c1..f29b156 100644 (file)
        "revdelete-selected-file": "{{PLURAL:$1|Označena inačica|Označene inačice}} datoteke [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Odabrani zapis u evidenciji|Odabrani zapisi u evidenciji}}:",
        "revdelete-text-text": "Izbrisane izmjene će i dalje biti vidljive u povijesti stranice, ali dijelovi sadržaja neće biti vidljivi javno.",
-       "logdelete-text": "Izbrisane izmjene i dalje će biti vidljive u zapisnicima, ali dijelovi njihova sadržaja biti će nedostupni za javnost.",
+       "logdelete-text": "Izbrisane izmjene i dalje će biti vidljive u evidencijama, ali dijelovi njihova sadržaja biti će nedostupni za javnost.",
        "revdelete-text-others": "Ostali administratori na projektu {{SITENAME}} će moći vidjeti i vratiti izbrisani sadržaj na isti način, osim ako nisu postavljena dodatna ograničenja.",
        "revdelete-confirm": "Molimo potvrdite da namjeravate ovo učiniti, da razumijete posljedice i da to činite u skladu s [[{{MediaWiki:Policy-url}}|pravilima]].",
        "revdelete-suppress-text": "Sklanjanje uređivanja treba raditi '''iznimno''' u slijedećih par slučajeva:\n* Privatne informacije neprilične javnom mediju tipa\n*: ''kućna adresa i broj telefona, JMBG ili OIB, itd.''",
        "autoblocker": "Automatski ste blokirani jer je Vašu IP adresu nedavno koristio \"[[User:$1|$1]]\" koji je blokiran zbog: \"$2\".",
        "blocklogpage": "Evidencija blokiranja",
        "blocklog-showlog": "Ovaj suradnik je ranije blokiran.\nEvidencija blokiranja je prikazan ispod kao napomena:",
-       "blocklog-showsuppresslog": "Ovaj suradnik je ranije blokiran i skriven.\nZapisnik skrivanja je prikazan ispod kao napomena:",
+       "blocklog-showsuppresslog": "Ovaj suradnik je ranije blokiran i skriven.\nEvidencija skrivanja je prikazana ispod kao napomena:",
        "blocklogentry": "Blokiran je \"[[$1]]\" na rok $2 $3.",
        "reblock-logentry": "promijenjene postavke blokiranja za [[$1]] na rok od $2 $3",
        "blocklogtext": "Ovo je evidencija blokiranja i deblokiranja.\nNa popisu nema automatski blokiranih IP adresa.\nZa popis trenutačnih zabrana i blokiranja vidi [[Special:BlockList|popis blokiranja]].",
index e81c941..f864fd1 100644 (file)
        "viewsourcetext": "Megtekintheted és másolhatod a lap forrását:",
        "viewyourtext": "Megtekintheted és kimásolhatod a '''saját szerkesztéseidet''' az alábbi lapra:",
        "protectedinterface": "Ez a lap a szoftver felületéhez szolgáltat szöveget, és a visszaélések elkerülése miatt le van zárva.",
-       "editinginterface": "'''Vigyázat:''' egy olyan lapot szerkesztesz, ami a MediaWiki szoftver felületéhez tartozik. A lap megváltoztatása hatással lesz a kinézetre, ahogy más szerkesztők látják a lapot. Fordításra inkább használd a MediaWiki fordítására indított kezdeményezést, a [//translatewiki.net/wiki/Main_Page?setlang=hu translatewiki.net-et].",
+       "editinginterface": "<strong>Vigyázat:</strong> egy olyan lapot szerkesztesz, ami a MediaWiki szoftver felületéhez tartozik. A lap megváltoztatása hatással lesz a kinézetre, ahogy más szerkesztők látják a lapot.",
        "cascadeprotected": "Ez a lap szerkesztés elleni védelemmel lett ellátva, mert a következő {{PLURAL:$1|lapon|lapokon}} be van kapcsolva a „kaszkádolt” védelem:\n$2",
        "namespaceprotected": "Nincs jogosultságod a(z) '''$1''' névtérben található lapok szerkesztésére.",
        "customcssprotected": "Nem szerkesztheted ezt a CSS-lapot, mert egy másik felhasználó személyes beállításait tartalmazza.",
        "wlheader-enotif": "Az e-mailen keresztül történő értesítés engedélyezve.",
        "wlheader-showupdated": "Azok a lapok, amelyek megváltoztak, mióta utoljára megnézted őket, '''vastagítva''' láthatók.",
        "wlnote": "Alább {{PLURAL:$1|az utolsó változás|az utolsó <strong>$1</strong> változás}} látható az elmúlt {{PLURAL:$2|órában|<strong>$2</strong> órában}}, $3 $4-kor.",
-       "wlshowlast": "Az elmúlt $1 órában | $2 napon |  történt változtatások legyenek láthatóak",
+       "wlshowlast": "Az elmúlt $1 órában | $2 napon történt változtatások legyenek láthatóak",
        "watchlist-options": "A figyelőlista beállításai",
        "watching": "Figyelés...",
        "unwatching": "Figyelés befejezése...",
        "autoblockid": "$1. autoblokk",
        "block": "Felhasználó blokkolása",
        "unblock": "Felhasználó blokkolásának feloldása",
-       "blockip": "Blokkolás",
+       "blockip": "{{GENDER:$1|Felhasználó}} blokkolása",
        "blockip-legend": "Felhasználó blokkolása",
        "blockiptext": "Az alábbi űrlap segítségével megvonhatod egy szerkesztő vagy IP-cím szerkesztési jogait.\nÜgyelj rá, hogy az intézkedésed mindig legyen tekintettel a vonatkozó [[{{MediaWiki:Policy-url}}|irányelvekre]].\nAdd meg a blokkolás okát is (például idézd a blokkolandó személy által vandalizált lapokat).",
        "ipaddressorusername": "IP-cím vagy felhasználói név",
index 3cf8e3e..7aed2ff 100644 (file)
        "right-protect": "Agsukat kadagiti agpang ti salaknib ken agurnos kadagiti nasalakniban ti sariap a panid",
        "right-editprotected": "Agurnos kadagiti panid a nasalakniban a kas \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "Agurnos kadagiti panid a nasalakniban a kas \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Urnosen ti modelo ti linaon iti panid",
        "right-editinterface": "Agurnos iti interface ti agar-aramat",
        "right-editusercssjs": "Agurnos kadagiti papales ti CSS ken JavaScript dagiti sabali nga agar-aramat",
        "right-editusercss": "Agurnos kadagiti papeles ti CSS dagiti sabali nga agar-aramat",
        "action-viewmywatchlist": "agkita iti bukodmo a listaan ti bambantayan",
        "action-viewmyprivateinfo": "agkita iti bukodmo a pribado a pakaammo",
        "action-editmyprivateinfo": "agurnos iti bukodmo a pribado a pakaammo",
+       "action-editcontentmodel": "urnosen ti modelo ti linaon iti panid",
        "nchanges": "$1 {{PLURAL:$1|sinukatan|dagiti sinukatan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|manipud ti naudi a panagsarungkar}}",
        "enhancedrc-history": "pakasaritaan",
        "specialpages-group-wiki": "Datos ken ramramit",
        "specialpages-group-redirects": "Panangibaw-ing kadagiti espesial a panid",
        "specialpages-group-spam": "Ramramit ti spam",
+       "specialpages-group-developer": "Ramramit dagiti agraramid",
        "blankpage": "Blanko a panid",
        "intentionallyblankpage": "Daytoy a panid  ket naigagara a blanko.",
        "external_image_whitelist": " #Baybayan daytoy a linia a kastoy<pre>\n#Ikabil ti \"regular expression fragments\" (idiay laeng paset nga ikabil ti tengnga ti  //) dita baba\n#Dagitoy ipada na ti URLs ti ruar (ti napudot a naikapet) imahen \n#Dagiti agpada ket agparang nga  imahen, ket no saan ti panilpo ti imahen ti agparang laeng\n#Dagiti linia nga umuna iti # ket maipabalin a komentario\n#Daytoy ket \"sensetibo ti kadakkel ti letra\"\n\n#Ikabil dagita \"regex fragment\" ti ngato daytoy a linia. Baybay-an a kastoy daytoy a linia</pre>",
index 3ed9f04..00faf9d 100644 (file)
@@ -76,7 +76,8 @@
                        "Taxandru",
                        "C.R.",
                        "Elitre",
-                       "Laurentius"
+                       "Laurentius",
+                       "Macofe"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "tog-shownumberswatching": "Mostra il numero di utenti che hanno la pagina in osservazione",
        "tog-oldsig": "Firma attuale:",
        "tog-fancysig": "Gestisci la firma come wikitesto (senza collegamento automatico)",
-       "tog-uselivepreview": "Abilita la funzione ''Live preview'' (anteprima in diretta - sperimentale)",
+       "tog-uselivepreview": "Abilita la funzione ''Live preview'' (anteprima in diretta)",
        "tog-forceeditsummary": "Chiedi conferma se il campo oggetto è vuoto",
        "tog-watchlisthideown": "Nascondi le mie modifiche negli osservati speciali",
        "tog-watchlisthidebots": "Nascondi le modifiche dei bot negli osservati speciali",
        "anoneditwarning": "<strong>Attenzione:</strong> Accesso non effettuato. Se effettuerai delle modifiche il tuo indirizzo IP sarà visibile pubblicamente. Se <strong>[$1 accedi]</strong> o <strong>[$2 crei un'utenza]</strong>, le tue modifiche saranno attribuite al tuo nome utente, insieme ad altri benefici.",
        "anonpreviewwarning": "''Non è stato eseguito il login. Salvando la pagina, il proprio indirizzo IP sarà registrato nella cronologia.''",
        "missingsummary": "'''Attenzione:''' non è stato specificato l'oggetto di questa modifica. Premendo di nuovo \"{{int:savearticle}}\" la modifica verrà salvata con l'oggetto vuoto.",
+       "selfredirect": "<strong>Attenzione:</strong> stai creando un redirect alla medesima voce.\nSe fai clic nuovamente su \"{{int:savearticle}}\", il redirect sarà creato.",
        "missingcommenttext": "Inserire un commento qui sotto.",
        "missingcommentheader": "'''Attenzione:''' non è stata specificato l'oggetto/l'intestazione di questo commento. Premendo di nuovo \"{{int:savearticle}}\" la modifica verrà salvata senza intestazione.",
        "summary-preview": "Anteprima dell'oggetto:",
index e95eeea..f6184c0 100644 (file)
        "nonunicodebrowser": "<strong>警告: ご使用中のブラウザーは Unicode に未対応です。</strong>\n安全にページを編集する回避策を表示しています: 編集ボックス内の非 ASCII 文字を 16 進数コードで表現しています。",
        "editingold": "<strong>警告: このページの古い版を編集しています。</strong>\n保存すると、この版以降になされた変更がすべて失われます。",
        "yourdiff": "差分",
-       "copyrightwarning": "{{SITENAME}}ã\81¸ã\81®æ\8a\95稿ã\81¯ã\80\81ã\81\99ã\81¹ã\81¦$2 (詳細ã\81¯$1ã\82\92å\8f\82ç\85§) ã\81®ã\82\82ã\81¨ã\81§å\85¬é\96\8bã\81\97ã\81\9fã\81¨è¦\8bã\81ªã\81\95ã\82\8cã\82\8bã\81\93ã\81¨ã\81«ã\81\94注æ\84\8fã\81\8fã\81 ã\81\95ã\81\84ã\80\82\nã\81\82ã\81ªã\81\9fã\81\8cæ\8a\95稿ã\81\97ã\81\9fã\82\82ã\81®ã\82\92ã\80\81ä»\96人ã\81«ã\82\88ã\81£ã\81¦é\81 æ\85®ã\81ªã\81\8fç·¨é\9b\86ã\81\97ã\80\81ã\81\9dã\82\8cã\82\92è\87ªç\94±ã\81«é\85\8då¸\83ã\81\99ã\82\8bã\81®ã\82\92æ\9c\9bã\81¾ã\81ªã\81\84å ´å\90\88ã\81¯ã\80\81ã\81\93ã\81\93ã\81«ã\81¯æ\8a\95稿ã\81\97ã\81ªã\81\84ã\81§ã\81\8fã\81 ã\81\95ã\81\84ã\80\82<br />\nã\81¾ã\81\9fã\80\81æ\8a\95稿ã\81\99ã\82\8bã\81®ã\81¯ã\80\81ã\81\82ã\81ªã\81\9fã\81\8cæ\9b¸ã\81\84ã\81\9fã\82\82ã\81®ã\81\8bã\80\81ã\83\91ã\83\96ã\83ªã\83\83ã\82¯ ã\83\89ã\83¡ã\82¤ã\83³ã\81¾ã\81\9fã\81¯ã\81\9dã\82\8cã\81«é¡\9eã\81\99ã\82\8bã\83\95ã\83ªã\83¼ã\81ªè³\87æ\96\99ã\81\8bã\82\89ã\81®è¤\87製ã\81§ã\81\82ã\82\8bã\81\93ã\81¨ã\82\92ç´\84æ\9d\9fã\81\97ã\81¦ã\81\8fã\81 ã\81\95ã\81\84ã\80\82\n<strong>è\91\97ä½\9c権ä¿\9dè­·ã\81\95ã\82\8cã\81¦ã\81\84ã\82\8bä½\9cå\93\81ã\82\92ã\80\81許諾ã\81ªã\81\97ã\81«æ\8a\95稿ã\81\97ã\81ªã\81\84ã\81§ã\81\8fã\81 ã\81\95ã\81\84!</strong>",
-       "copyrightwarning2": "{{SITENAME}}へのすべての投稿は、他の利用者によって編集、変更、除去される場合があります。\nあなたの投稿を、他人が遠慮なく編集するのを望まない場合は、ここには投稿しないでください。<br />\nまた、投稿するのは、あなたが書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください (詳細は$1を参照)。\n<strong>著作権保護されている作品を、許諾なしに投稿してはいけません!</strong>",
+       "copyrightwarning": "{{SITENAME}}ã\81¸ã\81®æ\8a\95稿ã\81¯ã\81\99ã\81¹ã\81¦ã\80\81$2 ï¼\88詳細ã\81¯$1ã\82\92å\8f\82ç\85§ï¼\89ã\81®ã\82\82ã\81¨ã\81§å\85¬é\96\8bã\81\97ã\81\9fã\81¨è¦\8bã\81ªã\81\95ã\82\8cã\82\8bã\81\93ã\81¨ã\81«ã\81\94注æ\84\8fã\81\8fã\81 ã\81\95ã\81\84ã\80\82\nè\87ªå\88\86ã\81\8cæ\9b¸ã\81\84ã\81\9fã\82\82ã\81®ã\81\8cä»\96ã\81®äººã\81«å®¹èµ¦ã\81ªã\81\8fç·¨é\9b\86ã\81\95ã\82\8cã\80\81è\87ªç\94±ã\81«é\85\8då¸\83ã\81\95ã\82\8cã\82\8bã\81®ã\82\92æ\9c\9bã\81¾ã\81ªã\81\84å ´å\90\88ã\81¯ã\80\81ã\81\93ã\81\93ã\81«æ\8a\95稿ã\81\97ã\81ªã\81\84ã\81§ã\81\8fã\81 ã\81\95ã\81\84ã\80\82<br />\nã\81¾ã\81\9fã\80\81æ\8a\95稿ã\81\99ã\82\8bã\81®ã\81¯ã\80\81è\87ªå\88\86ã\81§æ\9b¸ã\81\84ã\81\9fã\82\82ã\81®ã\81\8bã\80\81ã\83\91ã\83\96ã\83ªã\83\83ã\82¯ ã\83\89ã\83¡ã\82¤ã\83³ã\81¾ã\81\9fã\81¯ã\81\9dã\82\8cã\81«é¡\9eã\81\99ã\82\8bã\83\95ã\83ªã\83¼ã\81ªè³\87æ\96\99ã\81\8bã\82\89ã\81®è¤\87製ã\81§ã\81\82ã\82\8bã\81\93ã\81¨ã\82\92ç´\84æ\9d\9fã\81\97ã\81¦ã\81\8fã\81 ã\81\95ã\81\84ã\80\82\n<strong>è\91\97ä½\9c権ä¿\9dè­·ã\81\95ã\82\8cã\81¦ã\81\84ã\82\8bä½\9cå\93\81ã\81¯ã\80\81許諾ã\81ªã\81\97ã\81«æ\8a\95稿ã\81\97ã\81ªã\81\84ã\81§ã\81\8fã\81 ã\81\95ã\81\84ï¼\81</strong>",
+       "copyrightwarning2": "{{SITENAME}}への投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。\n自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。<br />\nまた、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は$1を参照)。\n<strong>著作権保護されている作品は、許諾なしに投稿しないでください!</strong>",
        "longpageerror": "<strong>エラー: 投稿された文章は {{PLURAL:$1|$1 KB}} の長さがあります。これは投稿できる最大の長さ {{PLURAL:$2|$2 KB}} を超えています。</strong>\nこの編集内容は保存できません。",
        "readonlywarning": "<strong>警告: データベースがメンテナンスのためロックされており、現在は編集内容を保存できません。</strong>\n必要であれば文章をコピー&amp;ペーストしてテキストファイルとして保存し、後ほど保存をやり直してください。\n\nデータベースをロックした管理者による説明は以下の通りです: $1",
        "protectedpagewarning": "<strong>警告: このページは保護されているため、管理者権限を持つ利用者のみが編集できます。</strong>\n参考として以下に最後の記録を表示します:",
        "specialpages-group-wiki": "データとツール",
        "specialpages-group-redirects": "転送される特別ページ",
        "specialpages-group-spam": "スパム対策ツール",
+       "specialpages-group-developer": "開発者用ツール",
        "blankpage": "白紙ページ",
        "intentionallyblankpage": "このページは意図的に白紙にされています。",
        "external_image_whitelist": "  #この行はこのままにしておいてください<pre>\n#この下に正規表現 (//の間に入る記述) を置いてください\n#外部の (ホットリンクされている) 画像の URL と一致するか検査されます\n#一致する場合は画像として、一致しない場合は画像へのリンクとして表示されます\n#行の頭に # を付けるとコメントとして扱われます\n#大文字と小文字は区別されません\n\n#正規表現はすべてこの行の上に置いてください。この行はこのままにしておいてください</pre>",
index fe9b550..282de51 100644 (file)
@@ -43,7 +43,7 @@
        "tog-shownumberswatching": "D'Zuel vun de Benotzer déi dës Säit iwwerwaache weisen",
        "tog-oldsig": "Aktuell Ënnerschrëft:",
        "tog-fancysig": "Ënnerschrëft als Wiki-Text behandelen (Ouni automatesche Link)",
-       "tog-uselivepreview": "Live-Preview benotzen (experimentell)",
+       "tog-uselivepreview": "Live-Preview benotzen",
        "tog-forceeditsummary": "Warnen, wa beim Späicheren de Resumé feelt",
        "tog-watchlisthideown": "Meng Ännerungen op menger Iwwerwaachungslëscht verstoppen",
        "tog-watchlisthidebots": "Ännerunge vu Botten op menger Iwwerwaachungslëscht verstoppen",
        "anoneditwarning": "<strong>Opgepasst:</strong> Dir sidd net ageloggt. Dowéinst gëtt amplaz vun engem Benotzernumm Är IP Adress ëffentlech gewise wann Dir Ännerunge maacht. Wann Dir <strong>[$1 Iech aloggt]</strong> oder <strong>[$2 e Bnotzerkont opmaachen]</strong>, Är Ännerunge ginn dann Ärem Benotzerkont zougedeelt, genee wéi aner Avantagen.",
        "anonpreviewwarning": "''Dir sidd net ageloggt. Wann Dir ofspäichert gëtt Är IP-Adress an der Lëscht vun de Versioune vun dëser Säit enregistréiert.''",
        "missingsummary": "'''Erënnerung:''' Dir hutt kee Resumé aginn.\nWann Dir nacheemol op \"{{int:savearticle}}\" klickt, gëtt Är Ännerung ouni Resumé ofgespäichert.",
+       "selfredirect": "<strong>Opgepasst:</strong> Dir maacht eng Viruleedung op deeselwechten Artikel.\nWann Dir nach eng Kéier op \"{{int:savearticle}}\" klickt, da gëtt d'Viruleedung ugeluecht.",
        "missingcommenttext": "Gitt w.e.g. eng Bemierkung an.",
        "missingcommentheader": "'''Denkt drun:''' Dir hutt keen Titel/Sujet fir dës Bemierkung aginn.\nWann Dir nach en Kéier op \"{{int:savearticle}}\" klickt da gëtt Är Ännerung ouni Titel gespäichert.",
        "summary-preview": "Resumé kucken ouni ofzespäicheren:",
index f4c1fc4..e95fa6e 100644 (file)
@@ -43,7 +43,7 @@
        "tog-shownumberswatching": "Прикажи го бројот на корисници кои набљудуваат",
        "tog-oldsig": "Постоечки потпис:",
        "tog-fancysig": "Сметај го потписот за викитекст (без автоматска врска)",
-       "tog-uselivepreview": "Користи преглед во живо (експериментално)",
+       "tog-uselivepreview": "Користи преглед во живо",
        "tog-forceeditsummary": "Извести ме кога нема опис на промените",
        "tog-watchlisthideown": "Скриј мои уредувања од списокот на набљудувања",
        "tog-watchlisthidebots": "Скриј ботовски уредувања од списокот на набљудувања",
        "anoneditwarning": "<strong>Предупредување:</strong> Не сте најавени. Вашата IP-адреса ќе биде јавно видлива ако уредувате. Ако <strong>[$1 се најавите]</strong> или <strong>[$2 направите сметка]</strong>, тогаш уредувањата ќе се припишуваат на вашето корисничко име, покрај другите погодности.",
        "anonpreviewwarning": "''Не сте најавени. Ако ја зачувате, Вашата IP-адреса ќе биде заведена во историјата на уредување на страницата.''",
        "missingsummary": "'''Потсетник:''' Не внесовте опис на измените. Ако притиснете Зачувај повторно, вашите измени ќе се зачуваат без опис.",
+       "selfredirect": "<strong>Предупредување:</strong> Создавате пренасочување кон истата статија.\nАко стиснете на „{{int:savearticle}}“ повторно, тогаш пренасочувањето ќе се создаде.",
        "missingcommenttext": "Ве молиме внесете коментар подолу.",
        "missingcommentheader": "'''Потсетување:''' Не внесовте наслов за овој коментар.\nАко повторно стиснете на „{{int:savearticle}}“, уредувањето ќе биде зачувано без наслов.",
        "summary-preview": "Изглед на описот:",
        "hijri-calendar-m10": "Шавал",
        "hijri-calendar-m11": "Ду ел-Кида",
        "hijri-calendar-m12": "Ду ел-Хиџа",
-       "hebrew-calendar-m1": "Тишри",
-       "hebrew-calendar-m2": "Хешван",
-       "hebrew-calendar-m3": "Ð\9aислев",
-       "hebrew-calendar-m4": "Тебет",
-       "hebrew-calendar-m5": "Шебат",
-       "hebrew-calendar-m6": "Ð\90дар",
-       "hebrew-calendar-m6a": "Ð\90дар I",
-       "hebrew-calendar-m6b": "Ð\90дар II",
-       "hebrew-calendar-m7": "Ð\9dисан",
-       "hebrew-calendar-m8": "Ð\98јар",
-       "hebrew-calendar-m9": "Сиван",
-       "hebrew-calendar-m10": "Тамуз",
-       "hebrew-calendar-m11": "Ð\90в",
-       "hebrew-calendar-m12": "Ð\95лул",
-       "hebrew-calendar-m1-gen": "Тишри",
-       "hebrew-calendar-m2-gen": "Хешван",
-       "hebrew-calendar-m3-gen": "Ð\9aислев",
-       "hebrew-calendar-m4-gen": "Тебет",
-       "hebrew-calendar-m5-gen": "Шебат",
-       "hebrew-calendar-m6-gen": "Ð\90дар",
-       "hebrew-calendar-m6a-gen": "Ð\90дар I",
-       "hebrew-calendar-m6b-gen": "Ð\90дар II",
-       "hebrew-calendar-m7-gen": "Ð\9dисан",
-       "hebrew-calendar-m8-gen": "Ð\98јар",
-       "hebrew-calendar-m9-gen": "Сиван",
-       "hebrew-calendar-m10-gen": "Тамуз",
-       "hebrew-calendar-m11-gen": "Ð\90в",
-       "hebrew-calendar-m12-gen": "Ð\95лул",
+       "hebrew-calendar-m1": "тишри",
+       "hebrew-calendar-m2": "хешван",
+       "hebrew-calendar-m3": "кислев",
+       "hebrew-calendar-m4": "тевет",
+       "hebrew-calendar-m5": "шват",
+       "hebrew-calendar-m6": "адар",
+       "hebrew-calendar-m6a": "адар I",
+       "hebrew-calendar-m6b": "адар II",
+       "hebrew-calendar-m7": "нисан",
+       "hebrew-calendar-m8": "ијар",
+       "hebrew-calendar-m9": "сиван",
+       "hebrew-calendar-m10": "тамуз",
+       "hebrew-calendar-m11": "ав",
+       "hebrew-calendar-m12": "елул",
+       "hebrew-calendar-m1-gen": "тишри",
+       "hebrew-calendar-m2-gen": "хешван",
+       "hebrew-calendar-m3-gen": "кислев",
+       "hebrew-calendar-m4-gen": "тевет",
+       "hebrew-calendar-m5-gen": "шват",
+       "hebrew-calendar-m6-gen": "адар",
+       "hebrew-calendar-m6a-gen": "адар I",
+       "hebrew-calendar-m6b-gen": "адар II",
+       "hebrew-calendar-m7-gen": "нисан",
+       "hebrew-calendar-m8-gen": "ијар",
+       "hebrew-calendar-m9-gen": "сиван",
+       "hebrew-calendar-m10-gen": "тамуз",
+       "hebrew-calendar-m11-gen": "ав",
+       "hebrew-calendar-m12-gen": "елул",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|разговор]])",
        "unknown_extension_tag": "Непозната ознака на додатокот „$1“",
        "duplicate-defaultsort": "Предупредување: Основниот клуч за подредување „$2“ го поништува претходниот основен клуч за подредување „$1“.",
index d18c67e..bbe6a1c 100644 (file)
@@ -38,7 +38,7 @@
        "tog-shownumberswatching": "Fa' vedé 'o nummero d'utente che teneno 'a paggena cuntrullata",
        "tog-oldsig": "Firma 'e mmo:",
        "tog-fancysig": "Piglia 'a firma comme fosse nu wikitesto (senza fà link automatico)",
-       "tog-uselivepreview": "Abilita 'o \"Live preview\" (sperimentale)",
+       "tog-uselivepreview": "Abbìa 'o \"Live preview\"",
        "tog-forceeditsummary": "Chiere a mme quanno se sta azzeccanno nu campo oggetto abbacante",
        "tog-watchlisthideown": "Annascunne 'e cagnamiente d' 'a lista 'e cuntrollo mia",
        "tog-watchlisthidebots": "Annasconne 'e cagnamiènte d' 'e bot ncopp'a l'elenco 'e cuntrollo",
        "userlogin-createanother": "Cria n'at'account",
        "createacct-emailrequired": "Indirizzo email",
        "createacct-emailoptional": "Indirizzo 'e posta elettronica (ozzionale)",
-       "createacct-email-ph": "Scrive 'o nderizzo mail tuo",
-       "createacct-another-email-ph": "Scrive nderizzo mail",
+       "createacct-email-ph": "Scrivite 'o nderizzo mail vuosto",
+       "createacct-another-email-ph": "Scrivite nderizzo mail",
        "createaccountmail": "Usa na password qualunque temporanea e manna sta password a l'indirizzo 'e posta e-mail specificato",
        "createacct-realname": "Nomme riale (ozzionale)",
        "createaccountreason": "Mutivo:",
        "createacct-reason": "Mutivo",
        "createacct-reason-ph": "Pecché staje crianno n'at'utenza",
        "createacct-captcha": "Cuntrollo 'e sicurezza",
-       "createacct-imgcaptcha-ph": "Scrive 'o testo ca vire ncoppa",
+       "createacct-imgcaptcha-ph": "Scrivite 'o testo ca vedite ncoppa",
        "createacct-submit": "Cria 'a toja utenza",
        "createacct-another-submit": "Cria 'n atro account",
        "createacct-benefit-heading": "{{SITENAME}} è fatta 'e perzone comme te.",
        "emailnotauthenticated": "'O ndirizzo 'e posta elettronica nun è stat'ancora cunfermato.\nNun se mannarranno mmasciate e-mail p' ' funzione ccà abbascio.",
        "noemailprefs": "Avite 'a specificà nu ndirizzo e-mail pe ll'attivà sti funzione.",
        "emailconfirmlink": "Cunferma 'o nderizzo mail d' 'o tujo.",
-       "invalidemailaddress": "'O nderizzo e-mail scritto nun se può accettà pecché nun tene nu furmatto buono.\nScrive n'ata vota nu nderizzo bbuono o abbacanta 'a casella.",
+       "invalidemailaddress": "'O nderizzo e-mail scritto nun se può accettà pecché nun tene nu furmatto buono.\nScrivite n'ata vota nu nderizzo bbuono o abbacantate 'a casciulella.",
        "cannotchangeemail": "'E ccunte mail nun se ponno cagnà dint'a sta wiki.",
        "emaildisabled": "Chistu sito nun può mannà mmasciate e-mail.",
        "accountcreated": "Cunto criato",
        "anoneditwarning": "'''Attenzione:''' Nun avite fatto l'acciesso. 'A cronologgia d' 'a vosta sarrà visibbele pubbrecamente si facite cocche cagnamiento. Si <strong>[$1 tràse]</strong> o <strong>[$2 crìe nu cunto]</strong>, 'e cagnamiente vuoste ve sarranno attribbuite a vvuje, nzieme a n'ati migliuramente.",
        "anonpreviewwarning": "''Nun avite fatto 'o login. Sarvann' 'a paggena, l'indirizzo IP d' 'o vuosto sarrà riggistrato dint'a cronologgia.''",
        "missingsummary": "'''Attenziò:''' nun s'è specificato l'oggetto 'e stu cagnamiento. Clicann' 'a \"{{int:savearticle}}\" n'ata vota 'o cagnamiento sarrà sarvato cu l'oggetto abbacante.",
+       "selfredirect": "<strong>Attenziò:</strong> State crianno nu redirect a 'o stesso articolo.\nSi cliccate \"{{int:savearticle}}\" n'ata vota, si criarrà 'o redirect.",
        "missingcommenttext": "Pe' piacere scrivete nu commento ccà abbascio.",
        "missingcommentheader": "'''Attenziò:''' nun s'è specificato l'oggetto/titolo 'e stu commento. Clicann' 'a \"{{int:savearticle}}\" n'ata vota 'o cagnamiento sarrà sarvato c' 'o titolo abbacante.",
        "summary-preview": "Anteprimma'e l'oggetto:",
index a074da5..fa7a9d3 100644 (file)
@@ -60,7 +60,8 @@
                        "Calak",
                        "Arg",
                        "NCoppens",
-                       "Josse.Cottenier"
+                       "Josse.Cottenier",
+                       "Macofe"
                ]
        },
        "tog-underline": "Koppelingen onderstrepen:",
@@ -88,7 +89,7 @@
        "tog-shownumberswatching": "Het aantal gebruikers weergeven dat deze pagina volgt",
        "tog-oldsig": "Bestaande ondertekening:",
        "tog-fancysig": "Als wikitekst behandelen (zonder automatische koppeling)",
-       "tog-uselivepreview": "\"Live voorvertoning\" gebruiken (experimenteel)",
+       "tog-uselivepreview": "\"Live voorvertoning\" gebruiken",
        "tog-forceeditsummary": "Een melding geven bij een lege bewerkingssamenvatting",
        "tog-watchlisthideown": "Eigen bewerkingen op mijn volglijst verbergen",
        "tog-watchlisthidebots": "Botbewerkingen op mijn volglijst verbergen",
        "specialpages-group-wiki": "Gegevens en -hulpmiddelen",
        "specialpages-group-redirects": "Doorverwijzende speciale pagina's",
        "specialpages-group-spam": "Spamhulpmiddelen",
+       "specialpages-group-developer": "Hulpmiddelen voor ontwikkelaars",
        "blankpage": "Lege pagina",
        "intentionallyblankpage": "Deze pagina is bewust leeg gelaten en wordt gebruikt voor benchmarks, enzovoort.",
        "external_image_whitelist": " #Laat deze regel onveranderd<pre>\n#Zet hieronder reguliere expressiefragmenten (alleen het deel dat tussen de // staat)\n#Deze worden gehouden tegen de URL's van externe (gehotlinkte) afbeeldingen\n#Als de reguliere expressie van toegang is, wordt een afbeelding weergegeven, anders wordt alleen een koppeling weergegeven\n#Regels die beginnen met \"#\" worden als opmerking behandeld\n#Regels in de witte lijst zijn niet hoofdlettergevoelig.\n\n#Zet alle reguliere expressiefragmenten boven deze regel. Laat deze regel onveranderd</pre>",
index 23bb892..1a5e64e 100644 (file)
@@ -71,7 +71,8 @@
                        "Michał Sobkowski",
                        "Py64",
                        "Nanaki",
-                       "Alan ffm"
+                       "Alan ffm",
+                       "Macofe"
                ]
        },
        "tog-underline": "Podkreślenie linków:",
        "tog-shownumberswatching": "Pokaż liczbę użytkowników obserwujących stronę",
        "tog-oldsig": "Twój obecny podpis:",
        "tog-fancysig": "Traktuj podpis jako wikikod (nie linkuj automatycznie całości)",
-       "tog-uselivepreview": "Używaj dynamicznego podglądu (eksperymentalny)",
+       "tog-uselivepreview": "Używaj dynamicznego podglądu",
        "tog-forceeditsummary": "Informuj o niewypełnieniu opisu zmian",
        "tog-watchlisthideown": "Ukryj moje edycje na liście obserwowanych",
        "tog-watchlisthidebots": "Ukryj edycje botów na liście obserwowanych",
index fc640b9..21a976a 100644 (file)
@@ -91,7 +91,7 @@
        "tog-shownumberswatching": "Mostrar o número de utilizadores a vigiar",
        "tog-oldsig": "Assinatura atual:",
        "tog-fancysig": "Tratar assinatura como texto wiki (sem hiperligações automáticas)",
-       "tog-uselivepreview": "Usar a antevisão ao vivo (experimental)",
+       "tog-uselivepreview": "Usar a antevisão ao vivo",
        "tog-forceeditsummary": "Avisar-me se deixar o resumo da edição vazio",
        "tog-watchlisthideown": "Esconder as minhas edições ao listar mudanças às páginas vigiadas",
        "tog-watchlisthidebots": "Esconder edições de robôs ao listar mudanças às páginas vigiadas",
        "specialpages-group-wiki": "Dados e ferramentas",
        "specialpages-group-redirects": "Redirecionar páginas especiais",
        "specialpages-group-spam": "Ferramentas anti-spam",
+       "specialpages-group-developer": "Ferramentas de desenvolvimento",
        "blankpage": "Página em branco",
        "intentionallyblankpage": "Esta página foi intencionalmente deixada em branco",
        "external_image_whitelist": " # Deixe esta linha exatamente como ela está<pre>\n# Coloque fragmentos de expressões regulares (apenas a parte entre //) abaixo\n# Estas serão comparadas com as URL das imagens externas (com ligação direta)\n# As que corresponderem serão apresentadas como imagens, caso contrário apenas será apresentado um link para a imagem\n# As linhas que começam com um símbolo de cardinal (#) são tratadas como comentários\n# Esta lista não distingue maiúsculas de minúsculas\n\n# Coloque todos os fragmentos de expressões regulares (regex) acima desta linha. Deixe esta linha exatamente como ela está</pre>",
index 0a59925..2082104 100644 (file)
        "tog-shownumberswatching": "Toggle option used in [[Special:Preferences]], in the section for recent changes. When this option is activated, the entries in recent changes includes the number of users who watch pages. {{Gender}}",
        "tog-oldsig": "Used in [[Special:Preferences]], tab User profile. {{Gender}}",
        "tog-fancysig": "In user preferences under the signature box.  {{Gender}}",
-       "tog-uselivepreview": "{{Gender}}\nToggle option used in [[Special:Preferences]].\n\nLive preview is an experimental feature (unavailable by default) to use edit preview without loading the page again.",
+       "tog-uselivepreview": "{{Gender}}\nToggle option used in [[Special:Preferences]].\n\nLive preview is a feature to use edit preview without loading the page again.",
        "tog-forceeditsummary": "Toggle option used in [[Special:Preferences]] to force an edit ''{{msg-mw|summary}}''. {{Gender}}",
        "tog-watchlisthideown": "[[Special:Preferences]], tab 'Watchlist'. Offers user to hide own edits from watchlist. {{Gender}}",
        "tog-watchlisthidebots": "[[Special:Preferences]], tab 'Watchlist'. Offers user to hide bot edits from watchlist. {{Gender}}",
        "anoneditwarning": "Shown when editing a page anonymously.\n\nParameters:\n* $1 – A link to log in, <nowiki>{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}</nowiki>\n* $2 – A link to sign up, <nowiki>{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}</nowiki>\n\nSee also:\n* {{msg-mw|mobile-frontend-editor-anoneditwarning}}",
        "anonpreviewwarning": "See also:\n* {{msg-mw|Anoneditwarning}}",
        "missingsummary": "The text \"edit summary\" is in {{msg-mw|Summary}}.\n\nSee also:\n* {{msg-mw|Missingcommentheader}}\n* {{msg-mw|Savearticle}}",
+       "selfredirect": "Notice displayed once after the user tries to create a redirect to the same article.",
        "missingcommenttext": "This message is shown, when the textbox by a new-section is empty.",
        "missingcommentheader": "Edit summary that is shown if you enable \"Prompt me when entering a blank summary\" and add a new section without headline to a talk page.\n\nSee also:\n* {{msg-mw|Missingsummary}}\n* {{msg-mw|Savearticle}}",
        "summary-preview": "Preview of the edit summary, shown under the edit summary itself.\nShould match: {{msg-mw|summary}}.",
index db6accf..ff387d9 100644 (file)
@@ -51,7 +51,7 @@
        "tog-shownumberswatching": "Arată numărul utilizatorilor care urmăresc",
        "tog-oldsig": "Semnătură actuală:",
        "tog-fancysig": "Tratează semnătura ca wikitext (fără o legătură automată)",
-       "tog-uselivepreview": "Folosește previzualizarea în timp real (experimental)",
+       "tog-uselivepreview": "Folosește previzualizarea în timp real",
        "tog-forceeditsummary": "Avertizează-mă când uit să descriu modificările",
        "tog-watchlisthideown": "Ascunde modificările mele la lista mea de urmărire",
        "tog-watchlisthidebots": "Ascunde modificările boților la lista mea de urmărire",
        "anoneditwarning": "<strong>Atenție:</strong> Nu v-ați autentificat. Adresa dumneavoastră IP va fi vizibilă în mod public dacă efectuați modificări. Dacă vă <strong>[$1 autentificați]</strong> sau vă <strong>[$2 creați un cont]</strong>, modificările dumneavoastră vor fi asociate numelui de utilizator, pe lângă alte beneficii.",
        "anonpreviewwarning": "''Nu v-ați autentificat. Dacă salvați pagina adresa dumneavoastră IP va fi înregistrată în istoric.''",
        "missingsummary": "'''Atenție:''' Nu ați completat caseta „descriere modificări”. Dacă apăsați din nou butonul „salvează pagina” modificările vor fi salvate fără descriere.",
+       "selfredirect": "<strong>Atenție:</strong> Sunteți pe cale să creați o redirecționare către același articol.\nDacă apăsați din nou pe „{{int:savearticle}}”, redirecționarea va fi creată.",
        "missingcommenttext": "Vă rugăm să introduceți un comentariu.",
        "missingcommentheader": "'''Atenție,''' nu ați pus titlu sau subiect la acest comentariu.\nDacă dați din nou clic pe „{{int:savearticle}}” modificarea va fi salvată fără titlu.",
        "summary-preview": "Previzualizare descriere:",
index 3bd9d93..ff5906d 100644 (file)
@@ -34,6 +34,7 @@
        "tog-watchdefault": "Eik pages n files that Ah eedit til ma watchleet",
        "tog-watchmoves": "Eik pages n files that Ah muiv til ma watchleet",
        "tog-watchdeletion": "Eik pages n files that Ah get rid o til ma watchleet",
+       "tog-watchrollback": "Eik pages whaur Ah'v performed ae rowback tae ma watchleet",
        "tog-minordefault": "Mairk aa eedits \"smaa\" bi defaut",
        "tog-previewontop": "Shaw luikower afore eedit kist n naw efter it",
        "tog-previewonfirst": "Shaw luikower oan firstwhile eidit",
@@ -63,6 +64,7 @@
        "underline-default": "Skin or brouser defaut",
        "editfont-style": "Eidit area font style:",
        "editfont-default": "Brouser defaut",
+       "editfont-monospace": "Monospaced font",
        "editfont-sansserif": "Sans-serif font",
        "editfont-serif": "Serif font",
        "sunday": "Sunday",
        "permalink": "Permanent airtin",
        "print": "Prent",
        "view": "See",
+       "view-foreign": "See oan $1",
        "edit": "Eedit",
        "edit-local": "Eedit local description",
        "create": "Ceaut",
+       "create-local": "Eik local descreeption",
        "editthispage": "Eedit this page",
        "create-this-page": "Creaut this page",
        "delete": "Delyte",
        "otherlanguages": "In ither leids",
        "redirectedfrom": "(Reguidit fae $1)",
        "redirectpagesub": "Reguidal page",
+       "redirectto": "Reguidit tae:",
        "lastmodifiedat": "This page wis hintmaist chynged oan $2, $1.",
        "viewcount": "This page haes been accesst $1 {{PLURAL:$1|yince|$1 times}}.",
        "protectedpage": "Protectit page",
        "hidetoc": "skauk",
        "collapsible-collapse": "Collapse.",
        "collapsible-expand": "Mak mair muckle",
+       "confirmable-confirm": "Ar {{GENDER:$1|ye}} sair?",
+       "confirmable-yes": "Ay",
+       "confirmable-no": "Na",
        "thisisdeleted": "See or restore $1?",
        "viewdeleted": "See $1?",
        "restorelink": "{{PLURAL:$1|yin delytit eidit|$1 delytit eidits}}",
        "filerenameerror": "Cuidna rename file \"$1\" til \"$2\".",
        "filedeleteerror": "Cuidna delyte file \"$1\".",
        "directorycreateerror": "Couldna creat directerie \"$1\".",
+       "directoryreadonlyerror": "Directerie \"$1\" is read-yinlie.",
+       "directorynotreadableerror": "Directerie \"$1\" is no readable.",
        "filenotfound": "Coudna fynd file \"$1\".",
        "unexpected": "Vailyie isnae expectit: \"$1\"=\"$2\".",
        "formerror": "Mistak: cuidna haun in form",
        "viewsourcetext": "Ye can leuk at n copie the soorce o this page:",
        "viewyourtext": "Ye can see n copie the soorce o <strong>yer eedits</strong> til this page:",
        "protectedinterface": "This page provides interface tex fer the saffware oan this wiki, n is protected fer tae hinder abuise.\nTae eik or chynge owersets fer aw wikis, please uise [//translatewiki.net/ translatewiki.net], the MediaWiki localisation waurk.",
-       "editinginterface": "<strong>Warnishment:</strong> Ye'r eeditin ae page that is uised tae provide interface tex fer the saffware.\nChynges til this page will affect the kithin o the uiser interface fer ither uisers oan this wiki.\nTae eik or chynge owersets fer aw wikis, please uise [//translatewiki.net/ translatewiki.net], the MediaWiki localisation waurk.",
+       "editinginterface": "<strong>Warnishment:</strong> Ye'r eeditin ae page that is uised tae provide interface tex fer the saffware.\nChynges til this page will affect the kithin o the uiser interface fer ither uisers oan this wiki.",
+       "translateinterface": "Tae eik or chynge owersets fer aw wikis, please uise [//translatewiki.net/ translatewiki.net], the MediaWiki localisation wairk.",
        "cascadeprotected": "This page haes been protectit fae eiditin, cause it is inclædit in the follaein {{PLURAL:$1|page|pages}}, that ar protectit wi the \"cascadin\" optie turnit oan:\n$2",
        "namespaceprotected": "Ye dinna hae permeession tae edit pages in the '''$1''' namespace.",
        "customcssprotected": "Ye dinna hae permeession tae eidit this CSS page cause it contains anither uiser's personal settings.",
        "createaccount-text": "Somebodie cræftit aen accoont fer yer wab-mail address oan {{SITENAME}} ($4) named \"$2\", wi passwaird \"$3\".\nYe shid log in n chynge yer passwaird nou.\n\nYe can ignore this message, gif this accoont wis cræftit bi mistak.",
        "login-throttled": "Ye'v makit ower monie recynt login attempts.\nPlease wait $1 afore giein it anither gae.",
        "login-abort-generic": "Yer login wisna successful - Aborted",
+       "login-migrated-generic": "Yer accoont's been migratit, n yer uisername nae langer exeests oan this wiki.",
        "loginlanguagelabel": "Leid: $1",
        "suspicious-userlogout": "Yer request tae log oot wis denied cause it luiks like it wis sent bi ae broken brouser or caching proxy.",
        "createacct-another-realname-tip": "Real name is aen optie.\nGif ye chuise tae provide it, this will be uised fer giein the uiser attreebution fer their wark.",
        "passwordreset-disabled": "Passwaird resets hae been disabled oan this wiki.",
        "passwordreset-emaildisabled": "Wab-mail features hae been disabled oan this wiki.",
        "passwordreset-username": "Uisername:",
+       "passwordreset-domain": "Domain:",
        "passwordreset-capture": "See the ootcomin e-mail?",
        "passwordreset-capture-help": "Gif ye check this kist, the e-mail (wi the temperie passwaird) will be shawn til ye n be sent til the uiser ava.",
        "passwordreset-email": "Wab-mail address:",
        "preview": "Luikower",
        "showpreview": "Shaw luikower",
        "showdiff": "Shaw chynges",
+       "blankarticle": "<strong>Wairnishment:</strong> The page that ye'r creautin is blank.\nGif ye clap \"{{int:savearticle}}\" again, the page will be creautit wioot oniething oan it.",
        "anoneditwarning": "<strong>Warnishment:</strong> Ye'r no loggit in. Yer IP address will be publeeclie veesible gif ye mak onie eedits. Gif ye <strong>[$1 log in]</strong> or <strong>[$2 creaute aen accoont]</strong>, yer eedits will be attreebutit tae yer uisername, aes weel aes ither benefits.",
        "anonpreviewwarning": "<em>Ye'r no loggit in. Hainin will record yer IP address in this page's eedit histerie.</em>",
        "missingsummary": "<strong>Mynd:</strong> Ye'v naw gien aen eedit owerview. Gif ye clap oan \"{{int:savearticle}}\" again, yer eedit will be haint wioot ane.",
        "edit-gone-missing": "Coudna update the page.\nIt appears tae hae been delytit.",
        "edit-conflict": "Eedit confleect.",
        "edit-no-change": "Yer eedit wis ignored cause nae chynge wis makit til the tex.",
+       "postedit-confirmation-created": "The page haes been creautit.",
+       "postedit-confirmation-restored": "The page haes been restored.",
        "postedit-confirmation-saved": "Yer eedit wis hained.",
        "edit-already-exists": "Coudna mak ae new page.\nIt awreadie exists.",
        "defaultmessagetext": "Defaut message tex",
        "editpage-notsupportedcontentformat-text": "The content format $1 isna supported bi the content model $2.",
        "content-model-wikitext": "wikitex",
        "content-model-text": "plain tex",
+       "content-model-javascript": "JavaScript",
+       "content-model-css": "CSS",
+       "duplicate-args-category": "Pages uisin dupleecate arguments in template caws",
+       "duplicate-args-category-desc": "The page contains template caws that uise dupleecates o arguments, lik <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "<strong>Warnishment:</strong> This page contains ower moni expensive parser function caws.\n\nIt shid hae less than $2 {{PLURAL:$2|caw|caws}}, thaur {{PLURAL:$1|is nou $1 caw|ar noo $1 caws}}.",
        "expensive-parserfunction-category": "Pages wi ower moni expensive parser function caws",
        "post-expand-template-inclusion-warning": "<strong>Warnishment Template incluid size is owermuckle. \nSome templates will na be incluidit.",
        "parser-template-recursion-depth-warning": "Template recursion depth limit owershote ($1)",
        "language-converter-depth-warning": "Leid converter depth limit owershote ($1)",
        "node-count-exceeded-category": "Pages whaur node-coont is owershote",
+       "node-count-exceeded-category-desc": "The page exceeds the mucklest node coont.",
        "node-count-exceeded-warning": "Page owershot the node coont",
        "expansion-depth-exceeded-category": "Pages whaur expansion depth is owershote",
+       "expansion-depth-exceeded-category-desc": "The page exceeds the mucklest expansion depth.",
        "expansion-depth-exceeded-warning": "Page owershote the expansion depth",
        "parser-unstrip-loop-warning": "Unstrip luip detected",
        "parser-unstrip-recursion-limit": "Unstrip recursion limit owershote ($1)",
        "rev-deleted-event": "(log action remuived)",
        "rev-deleted-user-contribs": "[uisername or IP address remuived - eidit skauk't fae contreebutions]",
        "rev-deleted-text-permission": "This page reveesion haes been <strong>delytit</strong>.\nDetails can be foond in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} delytion log].",
+       "rev-suppressed-text-permission": "This page reveesion haes been <strong>suppressed</strong>.\nTae fynd oot why the [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} suppression log].",
        "rev-deleted-text-unhide": "This page luikower haes been <strong>delytit</strong>.\nDetails can be foond in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} delytion log].\nYe can still [$1 see this luikower] gif ye wish tae proceed.",
        "rev-suppressed-text-unhide": "This page luikower haes been <strong>suppressed</strong>.\nDetails can be foond in the [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} suppression log].\nYe can still [$1 see this luikower] gif ye wish tae proceed.",
        "rev-deleted-text-view": "This page luikower haes been <strong>delytit</strong>.\nYe can see it; the details can be foond in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} delytion log].",
        "search-result-category-size": "{{PLURAL:$1|1 memmer|$1 memmers}} ({{PLURAL:$2|1 subcategerie|$2 subcategeries}}, {{PLURAL:$3|1 file|$3 files}})",
        "search-redirect": "(reguide $1)",
        "search-section": "(section $1)",
+       "search-category": "(categerie $1)",
        "search-file-match": "(matches file content.)",
        "search-suggest": "Did ye mean: $1",
        "search-interwiki-caption": "Sister projec's",
        "searchall": "aw",
        "showingresults": "Shawin ablo up tae {{PLURAL:$1|'''1''' ootcome|'''$1''' ootcomes}} stertin wi #'''$2'''.",
        "showingresultsinrange": "Shawin ablo up til {{PLURAL:$1|<strong>1</strong> ootcome|<strong>$1</strong> ootcome}} in range #<strong>$2</strong> til #<strong>$3</strong>.",
+       "search-showingresults": "{{PLURAL:$4|Ootcome <strong>$1</strong> o <strong>$3</strong>|Ootcomes <strong>$1 - $2</strong> o <strong>$3</strong>}}",
        "search-nonefound": "Thaur were naw ootcomes matchin the speiring.",
        "powersearch-legend": "Advanced rake",
        "powersearch-ns": "Rake in namespaces:",
        "restoreprefs": "Restore aw defaut settins (in aw sections)",
        "prefs-editing": "Eeditin",
        "rows": "Raws:",
+       "columns": "Columns:",
        "searchresultshead": "Rake ootcome settins",
        "stub-threshold": "Threeshaud fer <a href=\"#\" class=\"stub\">stub airtin</a> formattin (bytes):",
        "stub-threshold-disabled": "Disablt",
        "prefs-help-recentchangescount": "This includes recent chynges, page histories, n logs.",
        "prefs-help-watchlist-token2": "This is the hidlins key til the wab feed o yer watchleet. Onibodie wha kens this can read yer watchleet, sae dinna shair it. Gif ye need to, [[Special:ResetTokens|Ye can reset it]].",
        "savedprefs": "Yer preferences haes been hained.",
+       "timezonelegend": "Time zone:",
+       "localtime": "Local time:",
        "timezoneuseserverdefault": "Uise wiki defaut ($1)",
        "timezoneuseoffset": "Ither (speceefie affset)",
        "servertime": "Server time the nou",
        "timezoneregion-atlantic": "Atlaunteec Ocean",
        "timezoneregion-australia": "Australie",
        "timezoneregion-europe": "Europ",
+       "timezoneregion-indian": "Indian Ocean",
        "timezoneregion-pacific": "Paceefic Ocean",
        "allowemail": "Allou email fae ither uisers",
        "prefs-searchoptions": "Rake",
+       "prefs-namespaces": "Namespaces",
        "default": "defaut",
        "prefs-files": "Files",
        "prefs-custom-css": "Custom CSS",
        "gender-female": "She eedits wiki pages",
        "prefs-help-gender": "Settin this preference is aen optie.\nThe saffware uises its value tae address ye n tae mention ye til ithers uisin the appropriate grammatical gender.\nThis information will be publeec.",
        "email": "E-mail",
-       "prefs-help-realname": "Real name is aen optie.\nGif ye chuise tae provide it, this will be uised fer giein ye attreebution fer yer wark.",
+       "prefs-help-realname": "Real name is aen optie.\nGif ye chuise tae provide it, this will be uised fer giein ye attreebution fer yer wirk.",
        "prefs-help-email": "Wab-mail is optional, bit is needed fer passwaird resets, shid ye ferget yer passwaird.",
        "prefs-help-email-others": "Ye can chuise tae let ithers contact ye bi wab-mail through ae link oan yer uiser or tauk page.\nYer wab-mail address isna revealed whan ither uisers contact ye.",
        "prefs-help-email-required": "Yer e-mail address is needit.",
+       "prefs-info": "Baseec information",
        "prefs-i18n": "Internaitionalisation",
        "prefs-signature": "Signatur",
+       "prefs-dateformat": "Date format",
        "prefs-timeoffset": "Time affset",
        "prefs-advancedediting": "General opties",
        "prefs-editor": "Eediter",
        "prefs-advancedwatchlist": "Advanced opties",
        "prefs-displayrc": "Displey opties",
        "prefs-displaywatchlist": "Displey opties",
+       "prefs-tokenwatchlist": "Token",
        "prefs-diffs": "Diffs",
        "prefs-help-prefershttps": "This preeferance will tak effect oan yer nex login.",
+       "prefswarning-warning": "Ye'v makit chynges tae yer preferances that'v no been hained yet.\nGif ye leave this page wioot clapin \"$1\" than yer preferances 'll no be updatit.",
        "prefs-tabs-navigation-hint": "Tip: Ye can uise the cair n richt arrae keys tae naveegate atween the tabs in the tabs leet.",
        "email-address-validity-valid": "Wab-mail address appears tae be valid",
        "email-address-validity-invalid": "Enter ae valid wab-mail address",
        "group-autoconfirmed": "Autæconfirmed uisers",
        "group-bot": "Bots",
        "group-sysop": "Admeenistraters",
+       "group-bureaucrat": "Bureaucrats",
        "group-suppress": "Owersichts",
        "group-all": "(aw)",
        "group-user-member": "{{GENDER:$1|uiser}}",
        "group-autoconfirmed-member": "{{GENDER:$1|autæconfirmed uiser}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|admeenistrater}}",
+       "group-bureaucrat-member": "{{GENDER:$1|bureaucrat}}",
        "group-suppress-member": "{{GENDER:$1|owersicht}}",
        "grouppage-user": "{{ns:project}}:Uisers",
        "grouppage-autoconfirmed": "{{ns:project}}:Autæconfirmed uisers",
+       "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Admeenistraters",
+       "grouppage-bureaucrat": "{{ns:project}}:Bureaucrats",
        "grouppage-suppress": "{{ns:project}}:Owersicht",
+       "right-read": "Read pages",
        "right-edit": "Eedit pages",
        "right-createpage": "Cræft pages (that arna tauk pages)",
        "right-createtalk": "Cræft discussion pages",
        "right-move": "Muiv pages",
        "right-move-subpages": "Muiv pages wi thair subpages",
        "right-move-rootuserpages": "Muiv ruit uiser pages",
+       "right-move-categorypages": "Muiv categerie pages",
        "right-movefile": "Muiv files",
        "right-suppressredirect": "Na cræft reguidals fae soorce pages whan muivin pages",
        "right-upload": "Uplaid files",
        "right-browsearchive": "Rake delytit pages",
        "right-undelete": "Ondelyte ae page",
        "right-suppressrevision": "See, skauk n onskauk speceefic reveesions o pages fae onie uiser",
+       "right-viewsuppressed": "See owerluiks that'r skaukt fae onie uiser",
        "right-suppressionlog": "see preevate logs",
        "right-block": "Block ither uisers fae eeditin",
        "right-blockemail": "Block ae uiser fae sendin wab-mail",
        "right-protect": "Chynge protection levels n eedit cascade-protected pages",
        "right-editprotected": "Eedit pages protected aes \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "Eedit pages protected aes \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Eedit the content model o ae page",
        "right-editinterface": "Eedit the uiser interface",
        "right-editusercssjs": "Eedit ither uisers' CSS n JavaScript files",
        "right-editusercss": "Eedit ither uisers' CSS files",
        "newuserlogpagetext": "This is ae log o uiser cræftins.",
        "rightslog": "Uiser richts log",
        "rightslogtext": "This is a log o chynges tae uiser richts.",
+       "action-read": "read this page",
        "action-edit": "eedit this page",
        "action-createpage": "cræft pages",
        "action-createtalk": "cræft discussion pages",
        "action-createaccount": "cræft this uiser accoont",
+       "action-history": "see the histerie o this page",
        "action-minoredit": "maurk this eedit aes smaa",
        "action-move": "muiv this page",
        "action-move-subpages": "mui this page, n its subpages",
        "action-move-rootuserpages": "muiv ruit uiser pages",
+       "action-move-categorypages": "muiv categerie pages",
        "action-movefile": "muiv this file",
        "action-upload": "uplaid this file",
        "action-reupload": "owerwrite this exeestin file",
        "action-viewmywatchlist": "see yer watchleet",
        "action-viewmyprivateinfo": "see yer preevate information",
        "action-editmyprivateinfo": "eedit yer preevate information",
+       "action-editcontentmodel": "eedit the content model o ae page",
        "nchanges": "$1 {{PLURAL:$1|chynge|chynges}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sin laist veesit}}",
        "enhancedrc-history": "histeri",
        "recentchanges-label-bot": "This eedit wis performed bi ae bot",
        "recentchanges-label-unpatrolled": "This eedit haes no bin patrolled yet",
        "recentchanges-label-plusminus": "The page size chynged bi this nummer o bytes",
+       "recentchanges-legend-heading": "'''Legend:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (see [[Special:NewPages|leet o new pages]] n aw)",
        "rcnotefrom": "Ablo {{PLURAL:$5|is the chynge|ar the chynges}} sin <strong>$3, $4</strong> (up tae <strong>$1</strong> shawn).",
        "rclistfrom": "Shaw new chynges stertin fae $3 $2",
        "rc_categories": "Limit til categeries (separate wi \"|\")",
        "rc_categories_any": "Onie",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} efter chynge",
+       "newsectionsummary": "/* $1 */ new section",
        "rc-enhanced-expand": "Shaw details",
        "rc-enhanced-hide": "Skauk details",
        "rc-old-title": "oreeginlie cræftit aes \"$1\"",
        "upload-recreate-warning": "'''Warnishment: Ae file bi that name haes been delytit or muived.'''\n\nThe delytion n muiv log fer this page ar gien here fer conveeneeance:",
        "uploadtext": "Uise the form ablo tae uplaid files.\nTae see or rake aforegaun uplaided files gang til the [[Special:FileList|leet o uplaided files]], (re)uplaids ar loggit in the [[Special:Log/upload|uplaid log]] aes weel, n delytions in the [[Special:Log/delete|delytion log]].\n\nTae incluid ae file in ae page, uise aen airtin in yin o the follaein forms:\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code></strong> tae uise the ful version o the file\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|alt tex]]</nowiki></code></strong> tae uise ae 200 pixel wide rendeetion in ae kist in the cair margin wi \"alt tex\" aes descreeption\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code></strong> fer linkin directlie til the file wioot displeyin the file.",
        "upload-permitted": "Permitit file types: $1.",
+       "upload-preferred": "Preferred file types: $1.",
        "upload-prohibited": "Proheebited file types: $1.",
        "uploadlogpage": "Uplaid log",
        "uploadlogpagetext": "Ablo is ae leet o the maist recynt file uplaids.\nSee the [[Special:NewFiles|gallerie o new files]] fer ae mair veesual luikower.",
+       "filename": "Filename",
        "filedesc": "Ootline",
        "fileuploadsummary": "Ootline:",
        "filereuploadsummary": "File chynges:",
        "license-nopreview": "(Luikower naw available)",
        "upload_source_url": "(yer chosen file fae ae valid, publeeclie accessible URL)",
        "upload_source_file": "(yer chosen file fae yer computer)",
+       "listfiles-delete": "delyte",
        "listfiles-summary": "This speecial page shaws aw uplaided files.",
        "listfiles_search_for": "Rake fer media name:",
        "imgfile": "file",
        "listfiles": "The file leet",
        "listfiles_thumb": "Thummnail",
+       "listfiles_date": "The Date",
        "listfiles_name": "Name",
        "listfiles_user": "Uiser",
        "listfiles_size": "Size",
        "listfiles_description": "Descreeption",
+       "listfiles_count": "Versions",
        "listfiles-show-all": "Incluide auld versions o eemages",
        "listfiles-latestversion": "The Nou version",
        "listfiles-latestversion-yes": "Ay",
        "filehist-nothumb": "Naw thummnail",
        "filehist-user": "Uiser",
        "filehist-dimensions": "Dimensions",
+       "filehist-filesize": "The File size",
        "filehist-comment": "Comment",
        "imagelinks": "File uisage",
        "linkstoimage": "The follaein {{PLURAL:$1|page airts|$1 pages airt}} tae this file:",
        "randomincategory": "Random page in categerie",
        "randomincategory-invalidcategory": "\"$1\" isna ae valid categerie name.",
        "randomincategory-nopages": "Thaur's naw pages in the [[:Category:$1|$1]] categerie.",
+       "randomincategory-category": "Categerie:",
+       "randomincategory-legend": "Random page in categerie",
        "randomredirect": "Random reguidal",
        "randomredirect-nopages": "Thaur's naw reguidals in the namespace \"$1\".",
        "statistics": "Stateestics",
        "statistics-header-edits": "Eidit stateestics",
        "statistics-header-users": "Uiser stateestics",
        "statistics-header-hooks": "Ither stateestics",
+       "statistics-articles": "Content pages",
        "statistics-pages": "Pages",
        "statistics-pages-desc": "Aw pages in the wiki, incluidin tauk pages, reguidals, etc.",
        "statistics-files": "Uplaided files",
        "fewestrevisions": "Pages wi the fewest reeveesions",
        "nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
        "ncategories": "$1 {{PLURAL:$1|categerie|categeries}}",
+       "ninterwikis": "$1 {{PLURAL:$1|interwiki|interwikis}}",
        "nlinks": "$1 {{PLURAL:$1|airtin|airtins}}",
        "nmembers": "$1 {{PLURAL:$1|memmer|memmers}}",
        "nmemberschanged": "$1 → $2 {{PLURAL:$2|memmer|memmers}}",
        "wantedpages-badtitle": "Onvalid title in ootcome set: $1",
        "wantedfiles": "Wantit files",
        "wantedfiletext-cat": "The follaein files ar uised but dinna exeest. Files fae foreign repositeries micht be leetit despite exeestin. Onie sic false poseeteeves will be <del>struck oot</del>. Addeetionallie, pages that embed files that dinna exeest ar leetit in [[:$1]].",
+       "wantedfiletext-cat-noforeign": "The follaein files ar uised but dinna exeest. Mair than that, pages that embed files that dinna exeest ar leetit in [[:$1]].",
        "wantedfiletext-nocat": "The follaein files ar uised but dinna exeest. Files fae foreign repositeries micht be leetit despite exeestin. Onie sic false poseeteeves will be <del>struck oot</del>.",
+       "wantedfiletext-nocat-noforeign": "The folleain files ar uised but dinna exeest.",
        "wantedtemplates": "Wantit templates",
        "mostlinked": "Maist airtit-tae pages",
        "mostlinkedcategories": "Maist airtit-tae categeries",
        "prefixindex": "Aw pages wi prefix",
        "prefixindex-namespace": "Aw pages wi preefix ($1 namespace)",
        "prefixindex-strip": "Strip preefix in leet",
+       "shortpages": "Short pages",
        "longpages": "Lang pages",
        "deadendpages": "Deid-end pages",
        "deadendpagestext": "The follaein pages dinna link til ither pages in {{SITENAME}}.",
        "pager-older-n": "{{PLURAL:$1|aulder 1|aulder $1}}",
        "suppress": "Owersicht",
        "querypage-disabled": "This speecial page is disablit fer performance raisons.",
+       "apihelp": "API help",
+       "apihelp-no-such-module": "Module \"$1\" wis no foond.",
        "booksources": "Buik soorces",
        "booksources-search-legend": "Rake fer buik soorces",
+       "booksources-search": "Rake",
        "booksources-text": "Ablo is ae leet o airtins til ither steids that sell new n uised buiks, n micht hae further information aneat buiks that ye'r seekin ava:",
        "booksources-invalid-isbn": "The gien ISBN disna seem tae be valid; check fer mistaks copiein fae the oreeginal soorce.",
        "specialloguserlabel": "Performer:",
        "listgrouprights-removegroup-self": "Remuiv {{PLURAL:$2|groop|groops}} fae yer accoont: $1",
        "listgrouprights-addgroup-self-all": "Eik aw groops til yer accoont",
        "listgrouprights-removegroup-self-all": "Remuiv aw groops fae yer accoont",
+       "listgrouprights-namespaceprotection-header": "Namespace restreections",
+       "listgrouprights-namespaceprotection-namespace": "Namespace",
+       "listgrouprights-namespaceprotection-restrictedto": "Richt(s) allooing ae uiser tae eedit",
        "trackingcategories": "Keepin track o categeries",
        "trackingcategories-summary": "This page leets the trackin categeries that ar autæmateecallie populatit bi the MediaWiki saffware. Thair names can be chynged bi alterin the reelavant system messages in the {{ns:8}} namespace.",
        "trackingcategories-msg": "The Trackin Categerie",
        "emailto": "Til:",
        "emailsubject": "Aneat:",
        "emailmessage": "Message:",
+       "emailsend": "Send",
        "emailccme": "Wab-mail me ae copie o ma message.",
        "emailccsubject": "Copie o yer message til $1: $2",
        "emailsent": "Wab-mail sent",
        "watchnologin": "Nae loggit in",
        "addwatch": "Eik til watchleet",
        "addedwatchtext": "The page \"[[:$1]]\" haes been added til yer [[Special:Watchlist|watchleet]].\nFutur chynges til this page n its associated tauk page will be leeted thaur.",
+       "addedwatchtext-short": "The page \"$1\" haes been eikit tae yer watchleet.",
        "removewatch": "Remuiv fae watchleet",
        "removedwatchtext": "The page \"[[:$1]]\" haes been remuied fae [[Special:Watchlist|yer watchleet]].",
+       "removedwatchtext-short": "The page \"$1\" haes been remuived fae yer watchleet.",
        "watch": "Watch",
        "watchthispage": "Watch this page",
        "unwatch": "Onwatch",
        "wlheader-enotif": "Wab-mail annooncemant is enabled.",
        "wlheader-showupdated": "Pages that hae been chynged sin ye last veesitit thaim ar shawn in '''baud'''.",
        "wlnote": "Ablo {{PLURAL:$1|is the laist chynge|ae the laist <strong>$1</strong> chynges}} in the laist {{PLURAL:$2|hoor|<strong>$2</strong> hoors}}, aes o $3, $4.",
-       "wlshowlast": "Shaw hainmaist $1 hoors $2 days",
+       "wlshowlast": "Shaw the hainmaist $1 hoors $2 days",
        "watchlist-options": "Watchleet opties",
        "watching": "Watchin...",
        "unwatching": "Onwatchin...",
        "created": "cræftit",
        "changed": "chynged",
        "deletepage": "Delyte page",
+       "confirm": "Confirm",
        "excontent": "content wis: '$1'",
        "excontentauthor": "content wis: '$1' (n the ae contreebuter wis '[[Special:Contributions/$2|$2]]')",
        "exbeforeblank": "content afore blankin wis: '$1'",
        "delete-edit-reasonlist": "Eedit delytion raisons",
        "delete-toobig": "This page haes ae muckle eedit histerie, ower $1 {{PLURAL:$1|reveesion|reveesions}}.\nDelytion o sic pages haes been restrictit tae stap accidental disruption o {{SITENAME}}.",
        "delete-warning-toobig": "This page haes ae muckle eedit histerie, ower $1 {{PLURAL:$1|reveesion|reveesions}}.\nDelytin it micht disrupt database operations o {{SITENAME}};\nproceed wi caution.",
+       "deleteprotected": "Ye canna delyte this page cause it's been fended.",
        "deleting-backlinks-warning": "'''Warnishment:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Ither pages]] airt til or transcluide the page ye'r aboot tae delyte.",
        "rollback": "Row back eedits",
        "rollback_short": "Rowback",
        "revertpage": "Reverted eidits bi [[Special:Contributions/$2|$2]] ([[User talk:$2|tauk]]) til laist reveesion bi [[User:$1|$1]]",
        "revertpage-nouser": "Reverted eedits bi ae skaukt uiser til laist revesion bi {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Reverted eedits b $1;\nchynged back til the laist reveesion bi $2.",
+       "sessionfailure-title": "Session failure",
        "sessionfailure": "Thaur seems tae be ae proablem wi yer login session;\nthis action haes been canceled aes ae precaution again session hijackin.\nGang back til the preeveeoos page, relaid that page n than gie it anither gae.",
        "protectlogpage": "Fend log",
        "protectlogtext": "Ablow is ae leet o chynges til page protections.\nSee the [[Special:ProtectedPages|protected pages leet]] fer the leet o currently operational page protections.",
        "protect-title": "Chynge protection level fer \"$1\"",
        "protect-title-notallowed": "See protection level o \"$1\"",
        "prot_1movedto2": "[[$1]] muivit tae [[$2]]",
+       "protect-badnamespace-title": "No-fendable namespace",
        "protect-badnamespace-text": "Pages in this namespace canna be protected.",
        "protect-norestrictiontypes-text": "This page canna be protected aes thaur's naw restreection types available.",
+       "protect-norestrictiontypes-title": "No-fendable page",
+       "protect-legend": "Confirm fendin",
        "protectcomment": "Raison:",
        "protectexpiry": "Expires:",
        "protect_expiry_invalid": "Expirie time is onvalit.",
        "protect-othertime": "Ither time:",
        "protect-othertime-op": "ither time",
        "protect-existing-expiry": "Exeestin expirie time: $3, $2",
+       "protect-existing-expiry-infinity": "Exeestin expirie time: infeenit",
        "protect-otherreason": "Ither/addeetional raison:",
        "protect-otherreason-op": "Ither raison",
        "protect-dropdown": "*Commyn protection raisons\n** Excesseeve vandaleesm\n** Excesseeve spammin\n** Coonter-producteeve eedit warrin\n** Hei traffeec page",
        "restriction-level": "Restreection level:",
        "minimum-size": "Smaaest size",
        "maximum-size": "Mucklest size:",
+       "pagesize": "(bytes)",
        "restriction-edit": "Eidit",
        "restriction-move": "Muiv",
        "restriction-create": "Creaut",
        "undelete-revision": "Deleted reveesion o $1 (aes o $4, at $5) bi $3:",
        "undeleterevision-missing": "Onvalid or missin reveesion.\nYe micht hae ae bad link, or the reveesion micht hae been restored or remuived fae the archive.",
        "undelete-nodiff": "Naw preeveeoos reveesion foond.",
+       "undeletebtn": "Restore",
        "undeletelink": "see/restore",
        "undeleteviewlink": "see",
+       "undeleteinvert": "Invert the selection",
        "undeletecomment": "Raison:",
        "undeletedrevisions": "{{PLURAL:$1|1 reveesion|$1 reveesions}} restored",
        "undeletedrevisions-files": "{{PLURAL:$1|1 reveesion|$1 reveesions}} n {{PLURAL:$2|1 file|$2 files}} restored",
+       "undeletedfiles": "{{PLURAL:$1|1 file|$1 files}} restored",
        "cannotundelete": "Ondelyte failed:\n$1",
        "undeletedpage": "<strong>$1 haes been restored</strong>\n\nConsult the [[Special:Log/delete|delytion log]] fer ae record o recynt delytions n restorations.",
        "undelete-header": "See [[Special:Log/delete|the delytion log]] fer the recentlie delytit pages.",
        "namespace": "Namespace:",
        "invert": "Invert selection",
        "tooltip-invert": "Check this kist tae skauk chynges til pages wiin the selectit namespace (n the associatit namespace gif checked)",
+       "namespace_association": "Associatit namespace",
        "tooltip-namespace_association": "Check this kist foreby tae incluid the tauk or subject namespace associatit wi the selectit namespace",
        "blanknamespace": "(Main)",
        "contributions": "{{GENDER:$1|Uiser}} contributions",
        "contributions-title": "Uiser contreebutions fer $1",
        "mycontris": "Ma contreebutions",
        "contribsub2": "Fer {{GENDER:$3|$1}} ($2)",
+       "contributions-userdoesnotexist": "Uiser accoont \"$1\" is no registerit.",
        "nocontribs": "Nae chynges wis funnd matchin thir criteria.",
        "uctop": "(current)",
        "month": "Fae month (n afore):",
        "ipbwatchuser": "Watch this uiser's uiser n tauk pages",
        "ipb-disableusertalk": "Stap this uiser fae eeditin thair ain tauk page while blockit",
        "ipb-change-block": "Re-block the uiser wi thir settins",
+       "ipb-confirm": "Confirm the block",
        "badipaddress": "That IP address is nae guid",
        "blockipsuccesssub": "Block succeedit",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] haes been blockit.\n<br />See [[Special:BlockList|block leet]] tae review blocks.",
        "unblocked": "[[User:$1|$1]] haes been onblockit.",
        "unblocked-range": "$1 haes been onblockit.",
        "unblocked-id": "Block $1 haes been remuived.",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]] haes been onblockt.",
        "blocklist": "Blockit uisers",
        "ipblocklist": "Blockit uisers",
        "ipblocklist-legend": "Fynd ae blockit uiser",
        "blocklist-tempblocks": "Skauk temparie blocks",
        "blocklist-addressblocks": "Skauk single IP blocks",
        "blocklist-rangeblocks": "Skauk range blocks",
+       "blocklist-timestamp": "Timestamp",
        "blocklist-target": "Tairget",
        "blocklist-expiry": "Dies",
        "blocklist-by": "Blockin admeen",
        "blocklist-params": "Block boonds",
        "blocklist-reason": "Raison",
        "ipblocklist-submit": "Rake",
+       "ipblocklist-localblock": "Local block",
        "ipblocklist-otherblocks": "Ither {{PLURAL:$1|block|blocks}}",
        "infiniteblock": "infeenite",
        "expiringblock": "dies oan $1 at $2",
        "cant-see-hidden-user": "The uiser that ye'r attemptin tae block haes awreadie been blockit n skaukt.\nAes ye dinna hae the skaukuiser richt, ye canna see or eedit the uiser's block.",
        "ipbblocked": "Ye canna block or onblock ither uisers cause ye yersel is blockit.",
        "ipbnounblockself": "Yer na permitit tae onblock yersel.",
+       "lockdb": "Lock database",
        "unlockdb": "Lowse database",
        "lockdbtext": "Lockin the database will suspend the abeelitie o aw uisers tae eedit pages, chynge thair preeferences, eedit thair watchleets, n ither things needin chynges in the database. Please confirm that this is whit ye'r etlin tae dae, n that ye'll lowse the database whan yer maintenance is dun.",
        "unlockdbtext": "Lowsin the database will gie back the abeelitie fer aa uisers tae eidit pages, chynge their preeferences, eidit their watchleets, an ither things needin chynges in the database. Please confirm that this is whit ye ettle tae dae.",
        "lockconfirm": "Ai, Ah reellie want tae lock the database.",
        "unlockconfirm": "Ai, Ah reellie want tae lowse the database.",
+       "lockbtn": "Lock database",
        "unlockbtn": "Lowse database",
        "locknoconfirm": "Ye didna tick the confirmation kist.",
        "lockdbsuccesssub": "Database lock fine",
        "movepagetalktext": "The associated tauk page will be autaematiclie muived alang wi it <strong>onless:</strong>\n*A no-tuim tauk page awreadie exeests unner the new name, or\n*Ye oncheck the kist ablo.\n\nIn thae cases, ye will hae tae muiv or merge the page manuallie gif ye sae desire.",
        "movearticle": "Muiv page:",
        "moveuserpage-warning": "<strong>Warnishment:</strong> Ye'r aboot tae muiv ae uiser page. Please tak tent that yinlie the page will be muivd n the uiser will <em>naw</em> be renamed.",
+       "movecategorypage-warning": "<strong>Wairnishment:</strong> Ye'r aboot tae muiv ae categerie page. Please mynd that yinlie the page'll be muived n onie pages in the auld categerie will <em>no</em> be recategerised intae the new categerie.",
        "movenologintext": "Ye maun be a registert uiser n [[Special:UserLogin|loggit in]] tae muiv ae page.",
        "movenotallowed": "Ye dinna hae permeession tae muiv pages.",
        "movenotallowedfile": "Ye dinna hae permeession tae muiv files.",
        "cant-move-user-page": "Ye dinna hae permeession tae muiv uiser pages (aside fae subpages).",
        "cant-move-to-user-page": "Ye dinna hae permeession tae muiv ae page til ae uiser page (except til ae uiser subpage).",
+       "cant-move-category-page": "Ye dinna hae permeession tae muiv categerie pages.",
+       "cant-move-to-category-page": "Ye dinna hae permeession tae muiv ae page tae ae categerie page.",
        "newtitle": "Til new teitle",
        "move-watch": "Watch soorce page n tairget page",
        "movepagebtn": "Muiv page",
        "movepage-max-pages": "The mmucklest o $1 {{PLURAL:$1|page|pages}} haes been muived n naw mair will be muived autæmateeclie.",
        "movelogpage": "Muiv log",
        "movelogpagetext": "Ae leet o aw page muives is ablo.",
+       "movesubpage": "{{PLURAL:$1|Subpage|Subpages}}",
        "movesubpagetext": "This page haes $1 {{PLURAL:$1|subpage|subpages}} shawn ablo.",
        "movenosubpage": "This page haes naw subpages.",
        "movereason": "Raison:",
        "exportcuronly": "Inclæde juist the nou reveesion, naw the ful histerie",
        "exportnohistory": "----\n<strong>Mynd:</strong> Exportin the ful histerie o pages throogh this form haes been disabled cause o performance raisons.",
        "exportlistauthors": "Incluid ae ful leet o contreebuters fer ilka page",
+       "export-submit": "Export",
        "export-addcattext": "Eik pages fae categerie:",
+       "export-addcat": "Eik",
        "export-addnstext": "Eik pages fae namespace:",
+       "export-addns": "Eik",
        "export-download": "Hain aes file",
        "export-templates": "Incluid templates",
        "export-pagelinks": "Incluid linkt pages til ae depth o:",
        "allmessagescurrent": "Message tex the nou",
        "allmessagestext": "This is ae leet o seestem messages available in the MediaWiki namespace.\nPlease veesit [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] n [//translatewiki.net translatewiki.net] gif ye wish tae contreebute tae the generic MediaWiki localisation.",
        "allmessagesnotsupportedDB": "This page canna be uised cause <strong>$wgUseDatabaseMessages</strong> haes been disablt.",
+       "allmessages-filter-legend": "Filter",
        "allmessages-filter": "Filter b custymization state:",
        "allmessages-filter-unmodified": "Onmodified",
        "allmessages-filter-all": "Aw",
        "thumbnail_gd-library": "Oncompleate GD librie confeeguration: Missin function $1",
        "thumbnail_image-missing": "File seems tae be missin: $1",
        "thumbnail_image-failure-limit": "Thaur hae been ower monie recynt failed attempts ($1 or mair) tae render this thummnail. Please ettle again later.",
+       "import": "Import pages",
+       "importinterwiki": "Transwiki import",
        "import-interwiki-text": "Select ae wiki n page title tae import.\nReveesion dates n eediters' names will be preserved.\nAw transwiki import actions ar loggit at the [[Special:Log/import|import log]].",
+       "import-interwiki-sourcewiki": "The Soorce wiki:",
+       "import-interwiki-sourcepage": "The Soorce page:",
        "import-interwiki-history": "Copie aw histerie reveesions fer this page",
        "import-interwiki-templates": "Incluid aw templates",
+       "import-interwiki-submit": "Import",
        "import-interwiki-namespace": "Desteenation namespace:",
        "import-interwiki-rootpage": "Desteenation ruit page (aen optie):",
+       "import-upload-filename": "Filename:",
+       "import-comment": "Comment:",
        "importtext": "Please export the file fae the soorce wiki uising the [[Special:Export|export utilitie]].\nHain it til yer computer n uplaid it here.",
        "importstart": "Importin pages...",
        "import-revision-count": "$1 {{PLURAL:$1|reveesion|reveesions}}",
        "importnopages": "Naw pages tae import.",
        "imported-log-entries": "Imported $1 {{PLURAL:$1|log entrie|log entries}}.",
+       "importfailed": "The Import failed: <nowiki>$1</nowiki>",
        "importunknownsource": "Onkent import soorce type",
        "importcantopen": "Coudna apen import file",
+       "importbadinterwiki": "Bad interwiki airtin",
        "importsuccess": "Importit fine!",
        "importnosources": "Nae transwiki import soorces haes been defined n direct histerie uplaids is disabled.",
        "importnofile": "Naw import file wis uplaided.",
        "importuploaderrorsize": "Uplaid o import file failed.\nThe file is muckler than the permitit uplaid size.",
        "importuploaderrorpartial": "Uplaid o import file failed.\nThe file wis yinlie pairtlie uplaided.",
        "importuploaderrortemp": "Uplaid o import file failed.\nAe temparie fauder is missin.",
+       "import-parse-failure": "XML import parse failure",
        "import-noarticle": "Naw page tae import!",
        "import-nonewrevisions": "Nae reveesions imported (aw were either awreadie present, or skipt cause o mistaks).",
+       "xml-error-string": "$1 oan line $2, col $3 (byte $4): $5",
        "import-upload": "Uplaid XML data",
        "import-token-mismatch": "Loss o session data.\nPlease gie it anither gae.",
        "import-invalid-interwiki": "Canna import fae the speceefied wiki.",
        "import-options-wrong": "Wrang {{PLURAL:$2|optie|opties}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Gien ruit page is aen onvalit title.",
        "import-rootpage-nosubpage": "Namespace \"$1\" o the ruit page disna permit subpages.",
+       "importlogpage": "The Import log",
        "importlogpagetext": "Admeenistrateeve imports o pages wi eedit histerie fae ither wikis.",
        "import-logentry-upload": "imported [[$1]] bi file uplaid",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|reveesion|reveesions}} importit",
+       "import-logentry-interwiki": "transwikied $1",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|reveesion|reveesions}} importit fae $2",
        "javascripttest": "JavaScript testin",
        "javascripttest-title": "Rinnin $1 tests",
        "javascripttest-pagetext-frameworks": "Please chuise yin o the follaein testin framewairks: $1",
        "javascripttest-pagetext-skins": "Chuise ae skin tae rin the tests wi:",
        "javascripttest-qunit-intro": "See [$1 testin documentation] oan mediawiki.org.",
+       "javascripttest-qunit-heading": "MediaWiki JavaScript QUnit test suite",
        "tooltip-pt-userpage": "Yer uiser page",
        "tooltip-pt-anonuserpage": "The uiser page fer the IP address that ye'r eeditin aes",
        "tooltip-pt-mytalk": "Yer tauk page",
        "tooltip-pt-mycontris": "Leet o yer contreebutions",
        "tooltip-pt-login": "It's ae guid idea tae log in, but ye dinna hae tae.",
        "tooltip-pt-logout": "Log oot",
+       "tooltip-pt-createaccount": "We encoorage ye tae creaute aen accoont n log in; houever, it's no strictllie nesisair",
        "tooltip-ca-talk": "Discussion aneat the content page",
        "tooltip-ca-edit": "Ye can eedit this page. Please uise the luikower button afore hainin",
        "tooltip-ca-addsection": "Stairt ae new section",
        "tooltip-feed-atom": "Atom feed fer this page",
        "tooltip-t-contributions": "See ae leet o this uiser's contreebutions",
        "tooltip-t-emailuser": "Send ae wab-mail til this uiser",
+       "tooltip-t-info": "Mair information aneat this page",
        "tooltip-t-upload": "Uplaid files",
        "tooltip-t-specialpages": "Ae leet o aw byordinar pages",
        "tooltip-t-print": "Prentable version o this page",
        "anonusers": "{{SITENAME}} anonymoos {{PLURAL:$2|uiser|uisers}} $1",
        "creditspage": "Page creeedits",
        "nocredits": "Thaur's nae creedit info available fer this page.",
+       "spamprotectiontitle": "Spam protection filter",
        "spamprotectiontext": "The tex ye wished tae save wis blockit bi the spam filter.\nThis is maistlikly caused bi aen airtin til ae blaickleeted external site.",
        "spamprotectionmatch": "The follaein tex is whit triggered wir spam filter: $1",
+       "spambot_username": "MediaWiki spam cleanup",
        "spam_reverting": "Revertin til the laist reveesion na containin links til $1",
        "spam_blanking": "Aw reveesions contained links til $1, blankin",
        "spam_deleting": "Aw reveesions contained links til $1, delytin",
        "simpleantispam-label": "Anti-spam check.\nDiv <strong>NAW</strong> ful this in!",
        "pageinfo-title": "Information fer \"$1\"",
        "pageinfo-not-current": "Sairrie, it's na possible tae provide this information fer auld reveesions.",
+       "pageinfo-header-basic": "Baseec information",
        "pageinfo-header-edits": "Eedit histerie",
+       "pageinfo-header-restrictions": "Page fendin",
+       "pageinfo-header-properties": "Page properties",
        "pageinfo-display-title": "Displey title",
        "pageinfo-default-sort": "Defaut sort key",
+       "pageinfo-length": "Page langth (in bytes)",
+       "pageinfo-article-id": "The Page ID",
        "pageinfo-language": "Page content leid",
+       "pageinfo-content-model": "The Page content model",
        "pageinfo-robot-policy": "Indexin bi robots",
        "pageinfo-robot-index": "Permitit",
        "pageinfo-robot-noindex": "Na permitit",
        "pageinfo-hidden-categories": "Skaukt {{PLURAL:$1|categerie|categeries}} ($1)",
        "pageinfo-templates": "Transcluided {{PLURAL:$1|template|templates}} ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|Page|Pages}} transcluided oan ($1)",
+       "pageinfo-toolboxlink": "Page information",
        "pageinfo-redirectsto": "Reguidals til",
+       "pageinfo-redirectsto-info": "info",
        "pageinfo-contentpage": "Coonted aes ae content page",
        "pageinfo-contentpage-yes": "Ay",
        "pageinfo-protect-cascading": "Protections ar cascadin fae here",
        "mediawarning": "<strong>Warnishment:</strong> This file type micht contain maleecious code.\nBi executin it, yer system micht be compromised.",
        "imagemaxsize": "Eemage size leemit:<br /><em>(fer file descreeption pages)</em>",
        "thumbsize": "Thummnail size:",
+       "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|page|pages}}",
+       "file-info": "file size: $1, MIME type: $2",
        "file-info-size": "$1 × $2 pixels, file size: $3, MIME type: $4",
+       "file-info-size-pages": "$1 × $2 pixels, file size: $3, MIME type: $4, $5 {{PLURAL:$5|page|pages}}",
        "file-nohires": "Nae heier resolution available.",
        "svg-long-desc": "SVG file, nominallie $1 × $2 pixels, file size: $3",
        "svg-long-desc-animated": "Animated SVG file, nominallie $1 × $2 pixels, file size: $3",
        "show-big-image": "Oreeginal file",
        "show-big-image-preview": "Size o this luikower: $1.",
        "show-big-image-other": "Ither {{PLURAL:$2|resolution|resolutions}}: $1.",
+       "show-big-image-size": "$1 × $2 pixels",
        "file-info-gif-looped": "luip't",
+       "file-info-gif-frames": "$1 {{PLURAL:$1|frame|frames}}",
        "file-info-png-looped": "luip't",
        "file-info-png-repeat": "pleyed $1 {{PLURAL:$1|time|times}}",
+       "file-info-png-frames": "$1 {{PLURAL:$1|frame|frames}}",
        "file-no-thumb-animation": "<strong>Mynd: Due til techneecal limitations, thummnails o this file will na be animated.</strong>",
        "file-no-thumb-animation-gif": "<strong>Mynd: Due til techneecal limitations, thummnails o hei resolution GIF eemages sic aes this will na be animated.</strong>",
        "newimages": "Gallerie o new files",
        "imagelisttext": "Ablo is a leet o $1 {{PLURAL:$1|eemage|eemages}} sortit $2.",
        "newimages-summary": "This byordinair page shaws the last uplaidit files.",
+       "newimages-legend": "Filter",
        "newimages-label": "Filename (or ae pairt o it):",
+       "newimages-showbots": "Shaw uplaids bi bots",
        "noimages": "Nawthing tae see.",
        "ilsubmit": "Rake",
        "bydate": "bi date",
        "sp-newimages-showfrom": "Shaw new files stairtin fae $2, $1",
        "seconds": "{{PLURAL:$1|$1 seicont|$1 seiconts}}",
+       "minutes": "{{PLURAL:$1|$1 minute|$1 minutes}}",
        "hours": "{{PLURAL:$1|$1 hoor|$1 hoors}}",
+       "days": "{{PLURAL:$1|$1 day|$1 days}}",
+       "weeks": "{{PLURAL:$1|$1 week|$1 weeks}}",
+       "months": "{{PLURAL:$1|$1 month|$1 months}}",
+       "years": "{{PLURAL:$1|$1 year|$1 years}}",
        "ago": "$1 sin",
        "just-now": "richt nou",
        "hours-ago": "$1 {{PLURAL:$1|hoor|hoors}} sin",
        "minutes-ago": "$1 {{PLURAL:$1|minute|minutes}} sin",
        "seconds-ago": "$1 {{PLURAL:$1|seicont|seiconts}} sin",
        "monday-at": "Monenday at $1",
+       "tuesday-at": "Tuesday at $1",
        "wednesday-at": "Wedensday at $1",
+       "thursday-at": "Thursday at $1",
        "friday-at": "Frisday at $1",
+       "saturday-at": "Saturday at $1",
+       "sunday-at": "Sunday at $1",
+       "yesterday-at": "Yesterday at $1",
        "bad_image_list": "The format is aes follaes:\n\nAinlie leet eetems (lines stairtin wi *) ar considered. The foremaist airtin oan ae line maun be aen airtin til aen ill file. Onie subsequent airtins oan the same line ar considered tae be exceptions, i,e., pages whaur the eemage can occur inline.",
        "metadata": "Metadata",
        "metadata-help": "This file contains addeetional information, likelie eikit fae the deegital camera or scanner uised tae cræft or deegitise it. \nGif the file haes bin modeefied fae its oreeginal state, some details micht na fullie reflect the modeefied file.",
        "metadata-expand": "Shaw extendit details",
        "metadata-collapse": "Skauk extendit details",
        "metadata-fields": "Eemage metadata fields leetit in this message will be incluidit oan eemage page displey whan the metadata buird is collaps't. Ithers will be skaukt bi defaut. \n* mak\n* model\n* datetimeoreeginal\n* exposuretime\n* fnummer\n* isospeedratins\n* focallength\n* airtist\n* copiericht\n* eemagedescreeption\n* gpslateetuid\n* gpslangeetuid\n* gpsalteetuid",
+       "exif-imagewidth": "Width",
        "exif-imagelength": "Heicht",
+       "exif-bitspersample": "Bits per component",
+       "exif-compression": "Compression scheme",
        "exif-photometricinterpretation": "Pixel composeetion",
+       "exif-orientation": "Orientation",
        "exif-samplesperpixel": "Nummer o components",
+       "exif-planarconfiguration": "Data arrangement",
        "exif-ycbcrsubsampling": "Subsamplin ratio o Y til C",
        "exif-ycbcrpositioning": "Y n C poseetionin",
+       "exif-xresolution": "Horizontal resolution",
        "exif-yresolution": "Verteecal resolution",
        "exif-stripoffsets": "Eemage data location",
        "exif-rowsperstrip": "Nummer o raws per streep",
        "exif-referenceblackwhite": "Pair o blaick n white referance values",
        "exif-datetime": "File chynge date n time",
        "exif-imagedescription": "Eemage title",
+       "exif-make": "Camera manufacturer",
+       "exif-model": "The Camera model",
        "exif-software": "Saffware uised",
        "exif-artist": "Writer",
        "exif-copyright": "Copiericht hauder",
+       "exif-exifversion": "Exif version",
        "exif-flashpixversion": "Supportit Flashpix version",
        "exif-colorspace": "Colour space",
        "exif-componentsconfiguration": "Meanin o ilka component",
        "exif-subsectime": "DateTime subseiconts",
        "exif-subsectimeoriginal": "DateTimeOreeginal subseiconts",
        "exif-subsectimedigitized": "DateTimeDeegeetized subseiconts",
+       "exif-exposuretime": "Exposure time",
+       "exif-exposuretime-format": "$1 sec ($2)",
        "exif-fnumber": "F Nummer",
+       "exif-exposureprogram": "Exposure Program",
        "exif-spectralsensitivity": "Spectral sensiteevitie",
        "exif-isospeedratings": "ISO speed ratin",
+       "exif-shutterspeedvalue": "APEX shutter speed",
+       "exif-aperturevalue": "APEX aperture",
        "exif-brightnessvalue": "APEX brichtness",
+       "exif-exposurebiasvalue": "APEX exposure bias",
        "exif-maxaperturevalue": "Mucklest launn aperture",
+       "exif-subjectdistance": "Subject distance",
        "exif-meteringmode": "Meterin mode",
        "exif-lightsource": "Licht soorce",
+       "exif-flash": "Flash",
+       "exif-focallength": "Lens focal langth",
        "exif-subjectarea": "Subject airt",
        "exif-flashenergy": "Flash energie",
+       "exif-focalplanexresolution": "Focal plane X resolution",
+       "exif-focalplaneyresolution": "Focal plane Y resolution",
+       "exif-focalplaneresolutionunit": "Focal plane resolution unit",
+       "exif-subjectlocation": "Subject location",
+       "exif-exposureindex": "Exposure index",
        "exif-sensingmethod": "Sensin methyd",
        "exif-filesource": "File soorce",
+       "exif-scenetype": "Scene type",
        "exif-customrendered": "Custym eemage processin",
+       "exif-exposuremode": "Exposure mode",
+       "exif-whitebalance": "White balance",
        "exif-digitalzoomratio": "Deegeetal zuim ratio",
+       "exif-focallengthin35mmfilm": "Focal length in 35 mm film",
+       "exif-scenecapturetype": "Scene captur type",
+       "exif-gaincontrol": "Scene control",
+       "exif-contrast": "Contrast",
+       "exif-saturation": "Saturation",
        "exif-sharpness": "Shairpness",
        "exif-devicesettingdescription": "Device settins descreeption",
        "exif-subjectdistancerange": "Subject deestance range",
        "exif-imageuniqueid": "Uníque eemage ID",
+       "exif-gpsversionid": "GPS tag version",
        "exif-gpslatituderef": "Nort or sooth lateetude",
        "exif-gpslatitude": "Lateetude",
        "exif-gpslongituderef": "Aest or west langeetude",
        "exif-gpstimestamp": "GPS time (atomeec clock)",
        "exif-gpssatellites": "Satellites uised fer measurement",
        "exif-gpsstatus": "Receever status",
+       "exif-gpsmeasuremode": "Measurement mode",
        "exif-gpsdop": "Measurement preeceesion",
+       "exif-gpsspeedref": "Speed unit",
        "exif-gpsspeed": "Speed o GPS receever",
        "exif-gpstrackref": "Referance fer direction o muivement",
        "exif-gpstrack": "Direction o muivement",
        "exif-gpsdestdistance": "Distance til destination",
        "exif-gpsprocessingmethod": "Name o GPS processin methyd",
        "exif-gpsareainformation": "Name o GPS airt",
+       "exif-gpsdatestamp": "GPS date",
        "exif-gpsdifferential": "GPS differantial correction",
+       "exif-jpegfilecomment": "JPEG file comment",
        "exif-keywords": "Keywairds",
        "exif-worldregioncreated": "Region o the Yird that the picture wis taen in",
        "exif-countrycreated": "Kintra that the picture wis taen in",
        "exif-provinceorstatedest": "Provínce or state shawn",
        "exif-citydest": "Ceetie shawn",
        "exif-sublocationdest": "Sublocation o ceetie shawn",
+       "exif-objectname": "Short title",
        "exif-specialinstructions": "Byordiair insructions",
        "exif-headline": "Heidline",
        "exif-credit": "Creedit/Provider",
        "exif-locationdest": "Location depeected",
        "exif-locationdestcode": "Code o location depeected",
        "exif-objectcycle": "Time o day that media is intended fer",
+       "exif-contact": "Contact information",
+       "exif-writer": "Writer",
        "exif-languagecode": "Leid",
+       "exif-iimversion": "IIM version",
        "exif-iimcategory": "Categerie",
        "exif-iimsupplementalcategory": "Supplemental categeries",
        "exif-datetimeexpires": "Dinna uise efter",
        "exif-lens": "Lens uised",
        "exif-serialnumber": "Serial nummer o camera",
        "exif-cameraownername": "Ainer o camera",
+       "exif-label": "Label",
        "exif-datetimemetadata": "Date metadata wis laist modeefied",
        "exif-nickname": "Informal name o eemage",
        "exif-rating": "Ratin (oot o 5)",
        "exif-morepermissionsurl": "Alternative licensin information",
        "exif-attributionurl": "Whan re-uisin this wairk, please link til",
        "exif-preferredattributionname": "Whan re-uisin this wairk, please creedit",
+       "exif-pngfilecomment": "PNG file comment",
+       "exif-disclaimer": "Disclaimer",
        "exif-contentwarning": "Content warnishment",
+       "exif-giffilecomment": "GIF file comment",
        "exif-intellectualgenre": "Type o eetem",
+       "exif-subjectnewscode": "Subject code",
+       "exif-scenecode": "IPTC scene code",
        "exif-event": "Event depected",
        "exif-organisationinimage": "Organization depected",
        "exif-personinimage": "Person depected",
        "exif-copyrighted-true": "Copierichted",
        "exif-copyrighted-false": "Copiericht status na set",
        "exif-unknowndate": "Onkent date",
+       "exif-orientation-1": "Ordinair",
        "exif-orientation-2": "Flipt horizontallie",
        "exif-orientation-3": "Rotatit 180°",
        "exif-orientation-4": "Flipt verticlie",
        "exif-orientation-7": "Rotatit 90° CW n flipt verticlie",
        "exif-orientation-8": "Rotatit 90° CW",
        "exif-planarconfiguration-1": "chunkie format",
+       "exif-planarconfiguration-2": "planar format",
        "exif-colorspace-65535": "Oncalibratit",
        "exif-componentsconfiguration-0": "disna exeest",
        "exif-exposureprogram-0": "Na defined",
+       "exif-exposureprogram-1": "Manual",
+       "exif-exposureprogram-2": "Ordinair program",
        "exif-exposureprogram-3": "Apertur prioritie",
        "exif-exposureprogram-4": "Shutter prioritie",
        "exif-exposureprogram-5": "Cræftie program (biased thewaird the depth o field)",
        "exif-exposureprogram-6": "Action program (biased thewaird fast shutter speed)",
        "exif-exposureprogram-7": "Portrait mode (fer closeup photæs wi the backgroond oot o focus)",
        "exif-exposureprogram-8": "Launnscape mode (fer launnscape photæs wi the backgroonn in focus)",
+       "exif-subjectdistance-value": "$1 meters",
        "exif-meteringmode-0": "Onkent",
+       "exif-meteringmode-1": "Average",
        "exif-meteringmode-2": "Center weichtit average",
+       "exif-meteringmode-3": "Spot",
        "exif-meteringmode-4": "Multí-Spot",
+       "exif-meteringmode-5": "Pattern",
        "exif-meteringmode-6": "Pairtial",
        "exif-meteringmode-255": "Ither",
        "exif-lightsource-0": "Onkent",
        "exif-lightsource-1": "Daylicht",
        "exif-lightsource-2": "Fluorescant",
        "exif-lightsource-3": "Tungsten (incandescant licht)",
+       "exif-lightsource-4": "Flash",
+       "exif-lightsource-9": "Fine weather",
        "exif-lightsource-10": "Cloodie weather",
+       "exif-lightsource-11": "Gloam",
        "exif-lightsource-12": "Daylicht fluorescant (D 5700 – 7100K)",
        "exif-lightsource-13": "Day white fluorescant (N 4600 – 5400K)",
        "exif-lightsource-14": "Cuil white fluorescant (W 3900 – 4500K)",
        "exif-lightsource-17": "Staunart licht A",
        "exif-lightsource-18": "Staunart licht B",
        "exif-lightsource-19": "Staunart licht C",
+       "exif-lightsource-24": "ISO studio tungsten",
        "exif-lightsource-255": "Ither licht soorce",
        "exif-flash-fired-0": "Flash didna fire",
+       "exif-flash-fired-1": "Flash fired",
        "exif-flash-return-0": "naw flash return detection function",
        "exif-flash-return-2": "flash return licht na detectit",
        "exif-flash-return-3": "flash return licht detectit",
        "exif-flash-mode-3": "autæ mode",
        "exif-flash-function-1": "Naw flash function",
        "exif-flash-redeye-1": "reid-ee reduction mode",
+       "exif-focalplaneresolutionunit-2": "inches",
        "exif-sensingmethod-1": "Ondefined",
        "exif-sensingmethod-2": "Yin-chip colour airt senser",
        "exif-sensingmethod-3": "Twa-chip colour airt senser",
        "exif-sensingmethod-8": "Colour sequential linear senser",
        "exif-filesource-3": "Deegeetal still camera",
        "exif-scenetype-1": "Ae directlie photægraphed eemage",
+       "exif-customrendered-0": "Ordinair process",
        "exif-customrendered-1": "Custym process",
        "exif-exposuremode-0": "Autæ exposure",
+       "exif-exposuremode-1": "Manual exposure",
        "exif-exposuremode-2": "Autæ bracket",
        "exif-whitebalance-0": "Autæ white balance",
+       "exif-whitebalance-1": "Manual white balance",
        "exif-scenecapturetype-0": "Staunart",
        "exif-scenecapturetype-1": "Launscape",
+       "exif-scenecapturetype-2": "Portrait",
        "exif-scenecapturetype-3": "Nicht scene",
        "exif-gaincontrol-0": "Nane",
        "exif-gaincontrol-1": "Law gain up",
        "exif-gaincontrol-2": "Hei gain up",
        "exif-gaincontrol-3": "Law gain doon",
        "exif-gaincontrol-4": "Hei gain doon",
+       "exif-contrast-0": "Ordinair",
        "exif-contrast-1": "Saft",
        "exif-contrast-2": "Haurd",
+       "exif-saturation-0": "Ordinair",
        "exif-saturation-1": "Law saturation",
        "exif-saturation-2": "Hei saturation",
+       "exif-sharpness-0": "Ordinair",
        "exif-sharpness-1": "Saff",
        "exif-sharpness-2": "Haurd",
        "exif-subjectdistancerange-0": "Onkent",
+       "exif-subjectdistancerange-1": "Macro",
        "exif-subjectdistancerange-2": "Claise luik at",
        "exif-subjectdistancerange-3": "Distance sechtline",
        "exif-gpslatitude-n": "Nort lateetude",
        "exif-gpslongitude-w": "West langeetude",
        "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|meter|meters}} abuin sea level",
        "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|meter|meters}} ablo sea level",
+       "exif-gpsstatus-a": "Measurement in progress",
        "exif-gpsstatus-v": "Measurement interoperabeelitie",
+       "exif-gpsmeasuremode-2": "2-dimensional measurement",
+       "exif-gpsmeasuremode-3": "3-dimensional measurement",
        "exif-gpsspeed-k": "Kilometers aen hoor",
        "exif-gpsspeed-m": "Miles aen hoor",
+       "exif-gpsspeed-n": "Knots",
+       "exif-gpsdestdistance-k": "Kilometers",
+       "exif-gpsdestdistance-m": "Miles",
        "exif-gpsdestdistance-n": "Nauteecal miles",
        "exif-gpsdop-excellent": "Excellant ($1)",
        "exif-gpsdop-good": "Guid ($1)",
+       "exif-gpsdop-moderate": "Moderate ($1)",
+       "exif-gpsdop-fair": "Fair ($1)",
        "exif-gpsdop-poor": "Puir ($1)",
        "exif-objectcycle-a": "Mornin yinlie",
        "exif-objectcycle-p": "Evenin yinlie",
        "exif-objectcycle-b": "Baith mornin n evenin",
+       "exif-gpsdirection-t": "True direction",
        "exif-gpsdirection-m": "Magneteec direction",
+       "exif-ycbcrpositioning-1": "Centerit",
+       "exif-ycbcrpositioning-2": "Co-steidit",
        "exif-dc-contributor": "Contreebuters:",
        "exif-dc-coverage": "Spatial or tempral scope o media",
+       "exif-dc-date": "Date(s)",
+       "exif-dc-publisher": "Publisher",
        "exif-dc-relation": "Relatit media",
        "exif-dc-rights": "Richts",
        "exif-dc-source": "Soorce media",
        "exif-iimcategory-clj": "Crime n law",
        "exif-iimcategory-dis": "Disasters n accidants",
        "exif-iimcategory-fin": "Economie n business",
+       "exif-iimcategory-edu": "Education",
+       "exif-iimcategory-evn": "Environment",
        "exif-iimcategory-hth": "The Heal",
        "exif-iimcategory-hum": "Fawk interest",
        "exif-iimcategory-lab": "Laber",
        "exif-iimcategory-rel": "Releegion n truent",
        "exif-iimcategory-sci": "Sciance n technologie",
        "exif-iimcategory-soi": "Social eessues",
+       "exif-iimcategory-spo": "Sports",
        "exif-iimcategory-war": "War, conflict n onrest",
+       "exif-iimcategory-wea": "Weather",
+       "exif-urgency-normal": "Ordinair ($1)",
        "exif-urgency-low": "Law ($1)",
        "exif-urgency-high": "Hei ($1)",
        "exif-urgency-other": "Uiser-defined prioritie ($1)",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Clair the cache o this page?",
        "confirm-purge-bottom": "Purgin ae page clears the cache n forces the maist recynt reveesion tae appear.",
+       "confirm-watch-button": "OK",
        "confirm-watch-top": "Eik this page til yer watchleet?",
+       "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Remuiv this page fae yer watchleet?",
+       "quotation-marks": "\"$1\"",
        "imgmultipageprev": "← preeveeoos page",
        "imgmultipagenext": "nex page →",
        "imgmultigo": "Gang!",
        "img-lang-default": "(defaut leid)",
        "img-lang-info": "Render this eemage in $1. $2",
        "img-lang-go": "Gang",
+       "ascending_abbrev": "asc",
+       "descending_abbrev": "desc",
        "table_pager_next": "Page aifter",
        "table_pager_prev": "Page afore",
+       "table_pager_first": "First page",
        "table_pager_last": "Laist page",
        "table_pager_limit": "Shaw $1 eetems per page",
        "table_pager_limit_label": "Eetems per page:",
        "autosumm-replace": "Replacin page wi '$1'",
        "autoredircomment": "Reguidin til [[$1]]",
        "autosumm-new": "Cræftit page wi \"$1\"",
+       "autosumm-newblank": "Creautit blank page",
        "lag-warn-normal": "Chynges newer than $1 {{PLURAL:$1|seicont|seiconts}} micht na be shawn in this leet.",
        "lag-warn-high": "Cause o hei database server lag, chynges newer than $1 {{PLURAL:$1|seicont|seiconts}} micht na be shawn in this leet.",
        "watchlistedit-normal-title": "Eedit watchleet",
        "watchlistedit-raw-title": "Eedit raw watchleet",
        "watchlistedit-raw-legend": "Eedit raw watchleet",
        "watchlistedit-raw-explain": "Titles oan yer watchleet ar shawn ablo, n can be eeditit bi eikin til n remuivin fae the leet;\nyin title per line.\nWhan dun, clap \"{{int:Watchlistedit-raw-submit}}\".\nYe can [[Special:EditWatchlist|uise the staundairt eediter]] n aw.",
+       "watchlistedit-raw-titles": "Titles:",
        "watchlistedit-raw-submit": "Update watchleet",
        "watchlistedit-raw-done": "Yer watchleet haes been updated.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 title wis|$1 titles were}} added:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 title wis|$1 titles were}} remuived:",
+       "watchlistedit-clear-title": "Cleared watchleet",
+       "watchlistedit-clear-legend": "Clear watchleet",
+       "watchlistedit-clear-explain": "Aw o the titles will be remuived fae yer watchleet",
+       "watchlistedit-clear-titles": "Titles:",
+       "watchlistedit-clear-submit": "Clear the watchleet (This is fer aye!)",
+       "watchlistedit-clear-done": "Yer watchleet's been cleared.",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|1 title wis|$1 titles were}} remuived:",
+       "watchlistedit-too-many": "Thaur's ower monie pages tae displey here.",
+       "watchlisttools-clear": "Clear the watchleet",
        "watchlisttools-view": "See reelavant chynges",
        "watchlisttools-edit": "See n eedit watchleet",
        "watchlisttools-raw": "Eedit raw watchleet",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|tauk]])",
        "unknown_extension_tag": "Onkent extension tag \"$1\"",
        "duplicate-defaultsort": "<strong>Warnishment:</strong> Defaut sort key \"$2\" owerrides earlier defaut sort key \"$1\".",
+       "duplicate-displaytitle": "<strong>Warnishment:</strong> Displey title \"$2\" owerrides the earlier displey title \"$1\".",
+       "invalid-indicator-name": "<strong>Mistak:</strong> Page status indicaters' <code>name</code> attreebute maunna be tuim.",
+       "version": "Version",
        "version-extensions": "Instawed extensions",
+       "version-skins": "Instawed skins",
        "version-specialpages": "Byordinar pages",
        "version-parserhooks": "Parser huiks",
        "version-variables": "Vareeables",
+       "version-antispam": "Spam hinderance",
        "version-other": "Ither",
        "version-mediahandlers": "Media haunnlers",
        "version-hooks": "Huiks",
+       "version-parser-extensiontags": "Parser extension tags",
        "version-parser-function-hooks": "Parser function huiks",
        "version-hook-name": "Huik name",
        "version-hook-subscribedby": "Subscribed bi",
        "version-no-ext-name": "[no name]",
+       "version-license": "MediaWiki License",
+       "version-ext-license": "License",
+       "version-ext-colheader-name": "Extension",
+       "version-skin-colheader-name": "Skin",
+       "version-ext-colheader-version": "Version",
+       "version-ext-colheader-license": "License",
        "version-ext-colheader-description": "Descreeption",
        "version-ext-colheader-credits": "Writers",
        "version-license-title": "License fer $1",
        "version-credits-summary": "We'd like tae recognize the follaein fawk fer thair contreebution til [[Special:Version|MediaWiki]].",
        "version-license-info": "MediaWiki is free saffware; ye can reedistreebute it n/or modifie it unner the terms o the GNU General Public License aes publeesht bi the Free Software Foundation; either version 2 o the License, or (bi yer optie) onie later version.\n\nMediaWiki is distreebuted in the hope that it will be uissfu, bit WIOOT ONIE WARRANTIE; wioot even the implied warrantie o MERCHANTABILITIE or FITNESS FER AE PARTEECULAR PURPYSS. See the GNU General Public License fer mair details.\n\nYe shid hae receeved [{{SERVER}}{{SCRIPTPATH}}/COPIEIN ae copie o the GNU General Public License] alang wi this program; gif na, write til the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA or [//www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].",
        "version-software": "Instawed saffware",
+       "version-software-product": "Product",
+       "version-software-version": "Version",
        "version-entrypoints": "Entrie point URLs",
        "version-entrypoints-header-entrypoint": "Entrie point",
+       "version-entrypoints-header-url": "URL",
        "redirect": "Reguidal bi file, uiser, page or reveesion ID",
        "redirect-legend": "Reguidal til ae file or page",
        "redirect-summary": "This byordiair page reguides til ae file (gien the file name), ae page (gien ae reveesion ID or page ID), or ae uiser page (gien ae numereec uiser ID). Uissage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/reveesion/328429]], or [[{{#Special:Redirect}}/uiser/101]].",
        "redirect-submit": "Gang",
        "redirect-lookup": "Luikup:",
+       "redirect-value": "Value:",
        "redirect-user": "Uiser ID",
+       "redirect-page": "Page ID",
        "redirect-revision": "Page reveesion",
+       "redirect-file": "File name",
        "redirect-not-exists": "Value na foond",
        "fileduplicatesearch": "Rake fer dupleecate files",
        "fileduplicatesearch-summary": "Rake fer dupleecate files based oan hash values.",
        "fileduplicatesearch-legend": "Rake fer ae dupleecate",
        "fileduplicatesearch-filename": "Filename:",
        "fileduplicatesearch-submit": "Rake",
+       "fileduplicatesearch-info": "$1 × $2 pixel<br />File size: $3<br />MIME type: $4",
        "fileduplicatesearch-result-1": "The file \"$1\" haes naw identeecal dupleecation.",
        "fileduplicatesearch-result-n": "The file \"$1\" haes {{PLURAL:$2|1 identeecal dupleecation|$2 identeecal dupleecations}}.",
        "fileduplicatesearch-noresults": "Naw file named \"$1\" foond.",
        "specialpages": "Byordinar pages",
+       "specialpages-note-top": "The Legend",
        "specialpages-note": "* Normal byordinair pages.\n* <span class=\"mw-specialpagerestricted\">Restreected byordinair pages.</span>",
+       "specialpages-group-maintenance": "Maintenance reports",
        "specialpages-group-other": "Ither byordinair pages",
        "specialpages-group-login": "Login / cræft accoont",
        "specialpages-group-changes": "Recynt chynges n logs",
        "specialpages-group-wiki": "Data n tuils",
        "specialpages-group-redirects": "Reguidin byordinair pages",
        "specialpages-group-spam": "Spam tuils",
+       "specialpages-group-developer": "Deveeloper tuils",
+       "blankpage": "Blank page",
        "intentionallyblankpage": "This page is intentionlie left blank.",
        "external_image_whitelist": " #Lea this line exactlie aes it is<pre>\n#Put regulair expression fragments (jist the pairt that gaes atween the //) ablo\n#Thir will be matched wi the URLs o ootby (hotairtit) eemages\n#Thae that match will be displeyed aes eemages, itherwise yinlie aen airtin til the eemage will be shawn\n#Lines beginnin wi # ar treated aes comments\n#This is case-onsensiteeve\n\n#Put aw regex fragments abuin this line. Lea this line exactlie aes it is</pre>",
        "tags": "Valit chynge tags",
        "tag-filter": "[[Special:Tags|Tag]] filter:",
        "tag-filter-submit": "Filter",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
+       "tags-title": "Tags",
        "tags-intro": "This page leets the tags that the saffware can maurk aen eedit wi, n thair meanin.",
+       "tags-tag": "Tag name",
        "tags-display-header": "Appearance oan chynge leets",
        "tags-description-header": "Ful descreeption o meanin",
        "tags-active-header": "Acteeve?",
        "tags-active-no": "Naw",
        "tags-edit": "eedit",
        "tags-hitcount": "$1 {{PLURAL:$1|chynge|chynges}}",
+       "comparepages": "Compare pages",
+       "compare-page1": "Page 1",
+       "compare-page2": "Page 2",
        "compare-rev1": "Reveesion 1",
        "compare-rev2": "Reveesion 2",
+       "compare-submit": "Compare",
        "compare-invalid-title": "The title that ye speceefied is onvalit.",
        "compare-title-not-exists": "The title that ye speceefied disna exeest.",
        "compare-revision-not-exists": "The reveesion that ye speceefied disna exeest.",
        "htmlform-no": "Naw",
        "htmlform-yes": "Ay",
        "htmlform-chosen-placeholder": "Select aen optie",
+       "htmlform-cloner-create": "Eik mair",
+       "htmlform-cloner-delete": "Remuiv",
+       "htmlform-cloner-required": "At least the ae value is needit.",
        "sqlite-has-fts": "$1 wi ful-tex rake support",
        "sqlite-no-fts": "$1 wioot ful-tex rake support",
        "logentry-delete-delete": "$1 {{GENDER:$2|delytit}} page $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|restored}} page $3",
        "logentry-delete-event": "$1 {{GENDER:$2|chynged}} veesibeelitie o {{PLURAL:$5|ae log event|$5 log events}} oan $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|chynged}} veesibeelitie o {{PLURAL:$5|ae reveesion|$5 reveesions}} oan page $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|chynged}} veesibeelitie o log events oan $3",
        "logentry-delete-revision-legacy": "$1 {{GENDER:$2|chynged}} veesibeelitie o reveesions oan page $3",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|suppressed}} page $3",
        "logentry-suppress-event": "$1 hidlinswise {{GENDER:$2|chynged}} veesibeelitie o {{PLURAL:$5|ae log event|$5 log events}} oan $3: $4",
        "logentry-suppress-revision": "$1 hidlinswise {{GENDER:$2|chynged}} veesibeelity o {{PLURAL:$5|ae reveesion|$5 reveesions}} oan page $3: $4",
        "logentry-suppress-event-legacy": "$1 hidlinswise {{GENDER:$2|chynged}} veesibeelitie o log events oan $3",
        "revdelete-uname-unhid": "uisername onskaukt",
        "revdelete-restricted": "applied restreections til admeenistraters",
        "revdelete-unrestricted": "remuived restreections fer admeenistraters",
+       "logentry-merge-merge": "$1 {{GENDER:$2|merged}} $3 intae $4 (reveesions up tae $5)",
        "logentry-move-move": "$1 {{GENDER:$2|muived}} page $3 til $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|muived}} page $3 til $4 wioot leain ae reguidal",
        "logentry-move-move_redir": "$1 {{GENDER:$2|muived}} page $3 til $4 ower reguidal",
        "logentry-rights-rights": "$1 {{GENDER:$2|chynged}} groop memmership fer $3 fae $4 til $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|chynged}} groop memmership fer $3",
        "logentry-rights-autopromote": "$1 wis autæmateeclie {{GENDER:$2|promoted}} fae $4 til $5",
+       "logentry-upload-upload": "$1 {{GENDER:$2|uplaidit}} $3",
+       "logentry-upload-overwrite": "$1 {{GENDER:$2|uplaidit}} ae new version o $3",
+       "logentry-upload-revert": "$1 {{GENDER:$2|uplaidit}} $3",
        "rightsnone": "(nane)",
+       "revdelete-summary": "eedit the ootline",
        "feedback-bugornote": "Gif yer readie tae describe ae techneecal proablem in detail please [$1 report ae bug].\nItherwise, ye can uiss the easie form ablo. Yer comment will be eikit til the page \"[$3 $2]\", alang wi yer uisername.",
+       "feedback-subject": "Aneat:",
+       "feedback-message": "Message:",
+       "feedback-cancel": "Cancel",
+       "feedback-submit": "Haund Feedback In",
        "feedback-adding": "Eikin feedback til page...",
        "feedback-error1": "Mistak: Onrecognised ootcome fae API",
        "feedback-error2": "Mistak: Eedit failed",
        "searchsuggest-search": "Rake",
        "searchsuggest-containing": "containin...",
        "api-error-badaccess-groups": "Ye'r na permittit tae uplaid files til this wiki.",
+       "api-error-badtoken": "Inby mistak: Bad token.",
        "api-error-copyuploaddisabled": "Uplaidin bi URL is disabled oan this server.",
        "api-error-duplicate": "Thaur {{PLURAL:$1|is [$2 anither file]|ar [$2 some ither files]}} awreadie oan the site wi the same content.",
        "api-error-duplicate-archive": "Thaur {{PLURAL:$1|wis [$2 anither file]|were [$2 some ither files]}} awreadie oan the site wi the same content, but {{PLURAL:$1|it wis|thay were}} delytit.",
        "api-error-stashfailed": "Internal mistak: Server failed tae store temparie file.",
        "api-error-publishfailed": "Internal mistak: Server failed tae publeesh temparie file.",
        "api-error-stasherror": "Thaur wis ae mistak while uplaidin the file tae stash.",
+       "api-error-stashedfilenotfound": "The stashed file wis no foond whan attemptin tae uplaid it fae the stash.",
+       "api-error-stashpathinvalid": "The path that the stashed file shid hae been foond at wis no valid.",
+       "api-error-stashfilestorage": "Thaur wis ae mistak in storin the file in the stash.",
+       "api-error-stashzerolength": "The server coudna stash the file, cause it haed zero langth.",
+       "api-error-stashnotloggedin": "Ye maun be loggit in tae hain files in the uplaid stash.",
+       "api-error-stashwrongowner": "The file that ye were attemptin tae access in the stash disna belang tae ye.",
+       "api-error-stashnosuchfilekey": "The file key that ye were attemptin tae access in the stash disna exeest.",
        "api-error-timeout": "The server didna respond wiin the expectit time.",
        "api-error-unclassified": "Aen onkent mistake occurred.",
+       "api-error-unknown-code": "Onknawn mistak: \"$1\".",
        "api-error-unknown-error": "Internal mistak: Sommit went wrang whan uplaidin yer file.",
+       "api-error-unknown-warning": "Onknawn warnishment: \"$1\".",
+       "api-error-unknownerror": "Onknawn mistak: \"$1\".",
        "api-error-uploaddisabled": "Uplaidin is disabled oan this wiki.",
        "api-error-verification-error": "This file micht be rotten, or hae the wrang extension.",
        "duration-seconds": "$1 {{PLURAL:$1|seicont|seiconts}}",
+       "duration-minutes": "$1 {{PLURAL:$1|minute|minutes}}",
        "duration-hours": "$1 {{PLURAL:$1|hoor|hoors}}",
+       "duration-days": "$1 {{PLURAL:$1|day|days}}",
+       "duration-weeks": "$1 {{PLURAL:$1|week|weeks}}",
+       "duration-years": "$1 {{PLURAL:$1|year|years}}",
+       "duration-decades": "$1 {{PLURAL:$1|decade|decades}}",
        "duration-centuries": "$1 {{PLURAL:$1|centuair|centuairs}}",
+       "duration-millennia": "$1 {{PLURAL:$1|millennium|millennia}}",
        "rotate-comment": "Eemage rotated bi $1 {{PLURAL:$1|degree|degrees}} clockwise",
        "limitreport-title": "Parser profilin data:",
        "limitreport-cputime": "CPU time uissage",
        "limitreport-ppvisitednodes": "Preprocessor veesitit node coont",
        "limitreport-ppgeneratednodes": "Preprocessor generated node coont",
        "limitreport-postexpandincludesize": "Post-expand incluid size",
+       "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
+       "limitreport-templateargumentsize": "Template argument size",
+       "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
        "limitreport-expansiondepth": "Heiest expansion depth",
        "limitreport-expensivefunctioncount": "Expensive parser function coont",
+       "expandtemplates": "Mak templates muckler",
        "expand_templates_intro": "This byordiair page taks tex n expauns aw templates in it recurseevelie.\nIt foreby expaunds supported parser functions like\n<code><nowiki>{{</nowiki>#language:…}}</code> n vareeables like\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nIn fact, it expauns just aboot awthings in dooble-braces.",
        "expand_templates_title": "Contex title, fer {{FULLPAGENAME}}, etc.:",
+       "expand_templates_input": "The Input tex:",
        "expand_templates_output": "Ootcome",
        "expand_templates_xml_output": "XML ootpit",
        "expand_templates_html_output": "Raw HTML ootpit",
+       "expand_templates_ok": "OK",
+       "expand_templates_remove_comments": "Remuiv comments",
        "expand_templates_remove_nowiki": "Suppress <nowiki> tags in ootcome",
        "expand_templates_generate_xml": "Shaw XML parse tree",
        "expand_templates_generate_rawhtml": "Shaw raw HTML",
-       "expand_templates_preview": "Luikower"
+       "expand_templates_preview": "Luikower",
+       "expand_templates_preview_fail_html": "<em>Cause {{SITENAME}} haes raw HTML enabled n thaur wis ae loss o session data, the luikower haes been skaukt tae help defend again JavaScript attacks.</em>\n\n<strong>Gif this is a legeetimate luikower attempt, please gie it anither shot.</strong>\nGif ye still haae nae joy, than gie [[Special:UserLogout|loggin oot]] n loggin back in ae shot.",
+       "expand_templates_preview_fail_html_anon": "<em>Cause {{SITENAME}} haes raw HTML enabled n ye'r no loggit in, the luikower haes been skaukt tae fend again JavaScript attacks.</em>\n\n<strong>Gif this is ae legeetimate luikower attempt, than please [[Special:UserLogin|log in]] n gie it anither shot.</strong>",
+       "pagelanguage": "Page leid selecter",
+       "pagelang-name": "Page",
+       "pagelang-language": "Leid",
+       "pagelang-use-default": "Uise the defaut leid",
+       "pagelang-select-lang": "Pick yer leid",
+       "right-pagelang": "Chynge page leid",
+       "action-pagelang": "chynge the page leid",
+       "log-name-pagelang": "Chynge leid log",
+       "log-description-pagelang": "This is ae log o chynges in page leids.",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|chynged}} page leid fer $3 fae $4 tae $5.",
+       "default-skin-not-found": "Whoops! The defaut skin fer yer wiki, defined in <code dir=\"ltr\">$wgDefaultSkin</code> aes <code>$1</code>, is no available.\n\nYer instawation seems tae incluid the follaein skins. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] fer information oan hou tae enable thaim n chuise the defaut.\n\n$2\n\n; Gif ye'v juist instawed MediaWiki:\n: Ye proabablie instawed it fae git, or directlie fae the soorce code uisin some ither method. This is expectie. Gie instawin some skins fae [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory] ae shot, bi:\n:* Dounlaidin the [https://www.mediawiki.org/wiki/Download tarball installer], this comes wi several skins n extensions. Ye can than capie n paste the <code>skins/</code> directerie fae this.\n:* Dounlaidin indiveedual skin tarballs frae [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* Clonin one of the <code>mediawiki/skins/*</code> repositries bi wa o git intae the <code dir=\"ltr\">skins/</code> directerie o yer MediaWiki instawation.\n: Daein this shoudna interfere wi yer git repositrie gif ye'r ae MediaWiki deveeloper.\n\n; Gif ye,v juist upgradit MediaWiki:\n: MediaWiki 1.24 n newer nae langer enables instawed skins autæmateeclie (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Ye can paste the follaein lines intae <code>LocalSettings.php</code> tae enable aw nou installed skins:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Gif ye'v juist modified <code>LocalSettings.php</code>:\n: Double-check the skin names fer typos.",
+       "default-skin-not-found-no-skins": "Whoops! The defaut skin fer yer wiki, defined in <code>$wgDefaultSkin</code> aes <code>$1</code>, is no available.\n\nYe'v nae instawed skins.\n\n; Gif ye'v juist instawed or upgradit MediaWiki:\n: Ye probably instawed fae git, or directlie fae the soorce code uisin some ither method. This is expectit. MediaWiki 1.24 n newer disna incluid onie skins in the main repositrie. Gie instawin some skins fae [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory] ae shot, bi:\n:* Dounlaidin the [https://www.mediawiki.org/wiki/Download tarball installer], this comes wi several skins n extensions. Ye can than capie n paste the <code>skins/</code> directerie fae it.\n:* Dounlaidin individual skin tarballs fae [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* Cloning yin o the <code>mediawiki/skins/*</code> repositries bi wa o git intae the <code dir=\"ltr\">skins/</code> directerie o yer MediaWiki instawation.\n: Daein this shoudna interfere wi yer git repositrie gif ye'r ae MediaWiki deveeloper. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] fer information oan hou tae enable skins n chuise the defaut.",
+       "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (enabled)",
+       "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''disablt''')",
+       "mediastatistics": "Media stateestics",
+       "mediastatistics-summary": "Stateestics aneat uplaided file types. This yinlie incluids the maist recent version o ae file. Auld or delytit versions o files ar excluidit.",
+       "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
+       "mediastatistics-table-mimetype": "MIME type",
+       "mediastatistics-table-extensions": "Possible extensions",
+       "mediastatistics-table-count": "Nummer o files",
+       "mediastatistics-table-totalbytes": "Combined size",
+       "mediastatistics-header-unknown": "Onknawn",
+       "mediastatistics-header-bitmap": "Bitmap eemages",
+       "mediastatistics-header-drawing": "Drawins (vecter eemages)",
+       "mediastatistics-header-audio": "Audio",
+       "mediastatistics-header-video": "Videos",
+       "mediastatistics-header-multimedia": "Rich media",
+       "mediastatistics-header-office": "Affice",
+       "mediastatistics-header-text": "Texual",
+       "mediastatistics-header-executable": "Executables",
+       "mediastatistics-header-archive": "Compressed formats",
+       "json-warn-trailing-comma": "$1 trailin {{PLURAL:$1|comma wis|commas were}} remuived fae JSON",
+       "json-error-unknown": "Thaur wis ae proablem wi the JSON. Mistak: $1",
+       "json-error-depth": "The mucklest stack depth haes been exceedit",
+       "json-error-state-mismatch": "Onvalit or malformed JSON",
+       "json-error-ctrl-char": "Control chairacter mistak, possiblie wranglie encoded",
+       "json-error-syntax": "Syntax mistak",
+       "json-error-utf8": "Malformed UTF-8 chairacters, possiblie wranglie encoded",
+       "json-error-recursion": "Yin or mair recurseeve references in the value tae be encoded",
+       "json-error-inf-or-nan": "Yin or mair NAN or INF values in the value tae be encoded",
+       "json-error-unsupported-type": "Ae value o ae type that canna be encoded wis gien"
 }
index 47267a0..bc5d8c5 100644 (file)
@@ -37,7 +37,7 @@
        "tog-shownumberswatching": "Prikaži število uporabnikov, ki spremljajo temo",
        "tog-oldsig": "Trenutni podpis:",
        "tog-fancysig": "Obravnavaj podpis kot wikibesedilo (brez samodejne povezave)",
-       "tog-uselivepreview": "Uporabi hitri predogled (preizkusno)",
+       "tog-uselivepreview": "Uporabi hitri predogled",
        "tog-forceeditsummary": "Ob vpisu praznega povzetka urejanja me opozori",
        "tog-watchlisthideown": "Na spisku nadzorov skrij moja urejanja",
        "tog-watchlisthidebots": "Na spisku nadzorov skrij urejanja botov",
index c56f06a..6f52692 100644 (file)
@@ -88,7 +88,7 @@
        "tog-shownumberswatching": "Visa antalet användare som bevakar",
        "tog-oldsig": "Nuvarande signatur:",
        "tog-fancysig": "Behandla signatur som wikitext (utan en automatisk länk)",
-       "tog-uselivepreview": "Använd direktuppdaterad förhandsgranskning (experimentell)",
+       "tog-uselivepreview": "Använd direktuppdaterad förhandsgranskning",
        "tog-forceeditsummary": "Påminn mig om jag inte fyller i en redigeringskommentar",
        "tog-watchlisthideown": "Dölj mina redigeringar i bevakningslistan",
        "tog-watchlisthidebots": "Visa inte robotredigeringar i bevakningslistan",
        "anoneditwarning": "<strong>Varning:</strong> Du är inte inloggad. Din IP-adress kommer att vara publikt synlig om du gör några redigeringar. Om du <strong>[$1 loggar in]</strong> eller <strong>[$2 skapar ett konto]</strong> kommer dina redigeringar att tillskrivas ditt användarnamn, tillsammans med andra fördelar.",
        "anonpreviewwarning": "''Du är inte inloggad. Om du sparar kommer din IP-adress registreras på denna sidas redigeringshistorik.''",
        "missingsummary": "<strong>Påminnelse:</strong> Du har inte skrivit någon redigeringskommentar.\nOm du klickar på \"{{int:savearticle}}\" igen kommer din redigering att sparas utan en sådan.",
+       "selfredirect": "<strong>Varning:</strong> Du omdirigerar till samma artikel.\nOm du klickar på \"{{int:savearticle}}\" igen kommer omdirigeringen att skapas.",
        "missingcommenttext": "Var god och skriv in en kommentar nedan.",
        "missingcommentheader": "<strong>Påminnelse:</strong> Du har inte skrivit något ämne/rubrik för den här kommentaren.\nOm du trycker på \"{{int:savearticle}}\" igen kommer din redigering sparas utan rubrik.",
        "summary-preview": "Förhandsgranskning av sammanfattning:",
        "right-protect": "Ändra skyddsnivåer och redigera kaskadskyddade sidor",
        "right-editprotected": "Redigera skyddade sidor som \"{{int:protect-level-sysop}}\"",
        "right-editsemiprotected": "Redigera skyddade sidor som \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Ändra innehållsmodellen för en sida",
        "right-editinterface": "Redigera användargränssnittet",
        "right-editusercssjs": "Redigera andra användares CSS- och JS-filer",
        "right-editusercss": "Redigera andra användares CSS-filer",
        "action-viewmywatchlist": "visa din bevakningslista",
        "action-viewmyprivateinfo": "visa din privata information",
        "action-editmyprivateinfo": "redigera din privata information",
+       "action-editcontentmodel": "ändra innehållsmodellen för en sida",
        "nchanges": "$1 {{PLURAL:$1|ändring|ändringar}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sedan senaste besöket}}",
        "enhancedrc-history": "historik",
        "expand_templates_generate_xml": "Visa parseträd som XML",
        "expand_templates_generate_rawhtml": "Visa rå HTML",
        "expand_templates_preview": "Förhandsvisning",
+       "expand_templates_preview_fail_html": "<em>Eftersom {{SITENAME}} har rå HTML aktiverat och det uppstod en förlust av sessionsdata har förhandsgranskningen dolts som en försiktighetsåtgärd för att skydda mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett äkta försök att förhandsgranska sidan, vänligen försök igen.</strong>\nOm det fortfarande inte fungerar, försök att [[Special:UserLogout|logga ut]] och sedan logga in igen.",
+       "expand_templates_preview_fail_html_anon": "<em>Eftersom {{SITENAME}} har rå HTML aktiverat och du inte är inloggad har förhandsgranskningen dolts som en försiktighetsåtgärd för att skydda mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett äkta försök att förhandsgranska sidan, vänligen [[Special:UserLogin|logga in]] och försök igen.</strong>",
        "pagelanguage": "Sidspråksväljare",
        "pagelang-name": "Sida",
        "pagelang-language": "Språk",
        "log-description-pagelang": "Detta är en logg över ändringar i sidspråken.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|ändrade}} sidspråket för $3 från $4 till $5.",
        "default-skin-not-found": "Ojsan! Standardutseendet för din wiki, definierad i <code dir=\"ltr\">$wgDefaultSkin</code> som <code>$1</code>, är inte tillgängligt.\n\nDin installation verkar innehålla följande utseenden. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manualen: Utseendeinställningar] för information om hur du aktiverar dem och hur standard väljs.\n\n$2\n\n; Om du precis installerat MediaWiki:\n: Du installerade troligen från git, eller direkt från källkoden via någon annan metod. Detta är normalt. Försök att installera några utseenden från [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org:s utseendekatalog], genom att:\n:* Ladda ner [https://www.mediawiki.org/wiki/Download tarball-installeraren], som kommer med flera utseenden och tillägg. Du kan klippa och klistra in <code>skins/</code>-katalogen från den.\n:* Ladda ner individuella tarballs med utseenden från [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* Klona ett av <code>mediawiki/skins/*</code>-centralförvaren in i <code dir=\"ltr\">skins/</code>-arkiven i din MediaWiki-installation.\n: Att göra detta borde inte påverka ditt git-centralförvar om du är en MediaWiki-utvecklare. \n\n; Om du precis har uppgraderat MediaWiki:\n: MediaWiki 1.24 och nyare aktiverar ej längre automatiskt installerade utseenden (se [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Automatisk identifiering av utseenden]). Du kan klistra in följande rader i <code>LocalSettings.php</code> för att aktivera alla för närvarande installerade utseenden:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Om du precis har modifierat <code>LocalSettings.php</code>:\n: Dubbelkolla namnen för utseendena för att identifiera stavfel.",
-       "default-skin-not-found-no-skins": "Ojsan! Standardutseendet för din wiki, definierad i <code>$wgDefaultSkin</code> som <code>$1</code>, är inte tillgängligt.\n\nDu har inga installerade utseenden.\n\n; Om du precis installerat eller uppdaterat MediaWiki:\n: Du installerade troligen från git, eller direkt från källkoden via någon annan metod. Detta är att förvänta. MediaWiki 1.24 och nyare inkluderar inte några utseenden i det huvudsakliga centralförvaret. Försök att installera några utseenden från [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org:s utseendekatalog], genom att:\n:* Ladda ner [https://www.mediawiki.org/wiki/Download tarball-installeraren], som kommer med flera utseenden och tillägg. Du kan klipp-och-klistra in <code dir=\"ltr\">skins/</code>-katalogen från den.\n* Klona ett av <code>mediawiki/skins/*</code>-centralförvaren in i <code>skins/</code>-katalogen i din MediaWiki-installation.\n: Att göra detta borde inte påverka ditt git-centralförvar om du är en MediaWiki-utvecklare. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manualen: Utseendeinställningar] för information om hur utseenden aktiveras och hur standardutseendet väljs.",
+       "default-skin-not-found-no-skins": "Ojsan! Standardutseendet för din wiki, definierad i <code>$wgDefaultSkin</code> som <code>$1</code>, är inte tillgängligt.\n\nDu har inga installerade utseenden.\n\n; Om du precis installerat eller uppdaterat MediaWiki:\n: Du installerade troligen från git, eller direkt från källkoden via någon annan metod. Detta är att förvänta. MediaWiki 1.24 och nyare inkluderar inte några utseenden i det huvudsakliga centralförvaret. Försök att installera några utseenden från [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org:s utseendekatalog], genom att:\n:* Ladda ner [https://www.mediawiki.org/wiki/Download tarball-installeraren], som kommer med flera utseenden och tillägg. Du kan klipp-och-klistra in <code dir=\"ltr\">skins/</code>-katalogen från den.\n:* Ladda ner individuella tarballs med utseende från [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* Klona ett av <code>mediawiki/skins/*</code>-centralförvaren in i <code>skins/</code>-katalogen i din MediaWiki-installation.\n: Att göra detta borde inte påverka ditt git-centralförvar om du är en MediaWiki-utvecklare. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manualen: Utseendeinställningar] för information om hur utseenden aktiveras och hur standardutseendet väljs.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (aktiverad)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''inaktiverad''')",
        "mediastatistics": "Mediastatistik",
index 14ebeaf..67346b3 100644 (file)
@@ -47,7 +47,7 @@
        "tog-shownumberswatching": "వీక్షకుల సంఖ్యను చూపు",
        "tog-oldsig": "ప్రస్తుత సంతకం:",
        "tog-fancysig": "సంతకాన్ని వికీపాఠ్యంగా తీసుకో (ఆటోమెటిక్‌ లింకు లేకుండా)",
-       "tog-uselivepreview": "à°µà±\86à°¨à±\81à°µà±\86à°\82à°\9f à°®à±\81à°¨à±\81à°\9cà±\82à°ªà±\81à°¨à±\81 à°µà°¾à°¡à±\81 (à°ªà±\8dà°°à°¯à±\8bà°\97ాతà±\8dà°®à°\95à°\82)",
+       "tog-uselivepreview": "తాà°\9cà°¾ à°®à±\81à°¨à±\81à°\9cà±\82à°ªà±\81à°¨à±\81 à°µà°¾à°¡à±\81",
        "tog-forceeditsummary": "దిద్దుబాటు సారాంశం ఖాళీగా ఉంటే ఆ విషయాన్ని నాకు సూచించు",
        "tog-watchlisthideown": "నా మార్పులను వీక్షణా జాబితాలో చూపించొద్దు",
        "tog-watchlisthidebots": "బాట్లు చేసిన మార్పులను నా వీక్షణా జాబితాలో చూపించొద్దు",
        "hidetoc": "దాచు",
        "collapsible-collapse": "కుదించు",
        "collapsible-expand": "విస్తరించు",
+       "confirmable-yes": "అవును",
+       "confirmable-no": "కాదు",
        "thisisdeleted": "$1ను చూస్తారా, పునఃస్థాపిస్తారా?",
        "viewdeleted": "$1 చూస్తారా?",
        "restorelink": "{{PLURAL:$1|ఒక తొలగించిన మార్పు|$1 తొలగించిన మార్పులు}}",
        "search-result-category-size": "{{PLURAL:$1|1 సభ్యుడు|$1 సభ్యులు}} ({{PLURAL:$2|1 ఉవవర్గం|$2 ఉపవర్గాలు}}, {{PLURAL:$3|1 దస్త్రం|$3 దస్త్రాలు}})",
        "search-redirect": "(దారిమార్పు $1)",
        "search-section": "(విభాగం $1)",
+       "search-category": "(వర్గం $1)",
        "search-file-match": "(ఫైలు విషయంతో సరిపోలుతోంది)",
        "search-suggest": "మీరు అంటున్నది ఇదా: $1",
        "search-interwiki-caption": "సోదర ప్రాజెక్టులు",
        "license-nopreview": "(మునుజూపు అందుబాటులో లేదు)",
        "upload_source_url": " (సార్వజనికంగా అందుబాటులో ఉన్న, సరైన URL)",
        "upload_source_file": " (మీ కంప్యూటర్లో ఒక ఫైలు)",
+       "listfiles-delete": "తొలగించు",
        "listfiles-summary": "ఈ ప్రత్యేక పేజీ, ఎక్కించిన ఫైళ్ళన్నిటినీ చూపిస్తుంది.",
        "listfiles_search_for": "మీడియా పేరుకై వెతుకు:",
        "imgfile": "దస్త్రం",
        "randomincategory": "వర్గంలోని యాదృచ్చిక పేజీ",
        "randomincategory-invalidcategory": "\"$1\" అనేది సరైన పర్గం పేరు కాదు.",
        "randomincategory-nopages": "[[:Category:$1|$1]] వర్గంలో పేజీలేమీ లేవు.",
+       "randomincategory-category": "వర్గం:",
        "randomredirect": "యాదృచ్చిక దారిమార్పు",
        "randomredirect-nopages": "\"$1\" పేరుబరిలో దారిమార్పులేమీ లేవు.",
        "statistics": "గణాంకాలు",
        "querypage-disabled": "పనితీరు కారణాల వలన, ఈ ప్రత్యేకపేజీని అశక్తం చేసాం.",
        "booksources": "పుస్తక మూలాలు",
        "booksources-search-legend": "పుస్తక మూలాల కోసం వెతుకు",
+       "booksources-search": "వెతుకు",
        "booksources-text": "కొత్త, పాత పుస్తకాలు అమ్మే ఇతర సైట్లకు లింకులు కింద ఇచ్చాం. మీరు వెతికే పుస్తకాలకు సంబంధించిన మరింత సమాచారం కూడా అక్కడ దొరకొచ్చు:",
        "booksources-invalid-isbn": "మీరిచ్చిన ISBN సరైనదిగా అనిపించుటలేదు; అసలు మూలాన్నుండి కాపీ చేయడంలో పొరపాట్లున్నాయేమో చూసుకోండి.",
        "specialloguserlabel": "కర్త:",
        "confirm-watch-top": "ఈ పుటను మీ వీక్షణ జాబితాలో చేర్చాలా?",
        "confirm-unwatch-button": "సరే",
        "confirm-unwatch-top": "ఈ పుటను మీ వీక్షణ జాబితా నుండి తొలగించాలా?",
+       "quotation-marks": "“$1”",
        "imgmultipageprev": "← మునుపటి పేజీ",
        "imgmultipagenext": "తరువాతి పేజీ →",
        "imgmultigo": "వెళ్ళు!",
        "autosumm-replace": "పేజీని '$1' తో మారుస్తున్నాం",
        "autoredircomment": "[[$1]]కు దారిమళ్ళించారు",
        "autosumm-new": "'$1' తో కొత్త పేజీని సృష్టించారు",
+       "autosumm-newblank": "ఖాళీ పేజీని సృష్టించారు",
        "lag-warn-normal": "$1 {{PLURAL:$1|క్షణం|క్షణాల}} లోపు జరిగిన మార్పులు ఈ జాబితాలో కనిపించకపోవచ్చు.",
        "lag-warn-high": "అధిక వత్తిడి వలన డేటాబేసు సర్వరు వెనుకబడింది, $1 {{PLURAL:$1|క్షణం|క్షణాల}} కంటే కొత్తవైన మార్పులు ఈ జాబితాలో కనిపించకపోవచ్చు.",
        "watchlistedit-normal-title": "వీక్షణ జాబితాను మార్చు",
        "duplicate-defaultsort": "హెచ్చరిక: డిఫాల్టు పేర్చు కీ \"$2\", గత డిఫాల్టు పేర్చు కీ \"$1\" ని అతిక్రమిస్తుంది.",
        "version": "సంచిక",
        "version-extensions": "స్థాపించిన పొడగింతలు",
-       "version-skins": "అలంకారాలు",
+       "version-skins": "à°¸à±\8dథాపిà°\82à°\9aà°¿à°¨ à°\85à°²à°\82à°\95ారాలà±\81",
        "version-specialpages": "ప్రత్యేక పేజీలు",
        "version-parserhooks": "పార్సరు కొక్కాలు",
        "version-variables": "చరరాశులు",
        "version-hook-name": "కొక్కెం పేరు",
        "version-hook-subscribedby": "ఉపయోగిస్తున్నవి",
        "version-version": "(కూర్పు $1)",
+       "version-no-ext-name": "[పేరు లేదు]",
        "version-license": "MediaWiki లైసెన్సు",
        "version-ext-license": "లైసెన్సు",
        "version-ext-colheader-name": "పొడిగింత",
+       "version-skin-colheader-name": "అలంకారం",
        "version-ext-colheader-version": "కూర్పు",
        "version-ext-colheader-license": "లైసెన్సు",
        "version-ext-colheader-description": "వివరణ",
        "specialpages-group-wiki": "డాటా మరియు పనిముట్లు",
        "specialpages-group-redirects": "ప్రత్యేక పేజీల దారిమార్పులు",
        "specialpages-group-spam": "స్పామ్ పనిముట్లు",
+       "specialpages-group-developer": "వికాసకుల పనిముట్లు",
        "blankpage": "ఖాళీ పేజీ",
        "intentionallyblankpage": "బెంచిమార్కింగు, మొదలగు వాటికై ఈ పేజీని కావాలనే ఖాళీగా వదిలాము.",
        "external_image_whitelist": " #ఈ లైనును ఎలా ఉన్నదో అలాగే వదిలెయ్యండి<pre>\n#regular expression తునకలను (// ల మధ్య ఉండే భాగం)కింద పెట్టండి\n#వీటిని బయటి బొమ్మల URLలతో సరిపోల్చుతాము\n#సరిపోలిన బొమ్మలను చూపిస్తాము, మిగిలినవాటి లింకులను మాత్రమే చూపిస్తాము\n##తో మొదలయ్యే లైనులు వ్యాఖ్యానాలుగా భావించబడతాయి\n#ఇది కేస్-సెన్సిటివ్\n\n#అన్ని తునకలను ఈ లైనుకు పైన ఉంచండి.  ఈ లైనును ఎలా ఉన్నదో అలాగే వదిలెయ్యండి</pre>",
        "expand_templates_remove_nowiki": "ఫలితంలో <nowiki> ట్యాగులను అణచిపెట్టు",
        "expand_templates_generate_xml": "XML పార్స్ ట్రీని చూపించు",
        "expand_templates_generate_rawhtml": "ముడి HTML ను చూపించు",
-       "expand_templates_preview": "మునుజూపు"
+       "expand_templates_preview": "మునుజూపు",
+       "pagelang-name": "పేజీ",
+       "pagelang-language": "భాష",
+       "pagelang-use-default": "అప్రమేయ భాషను వాడు"
 }
index 54b5846..968a02d 100644 (file)
        "exif-iimcategory-wea": "Ob-havo",
        "namespacesall": "Barchasi",
        "monthsall": "barchasi",
+       "confirmrecreate": "Ushbu sahifa siz tahrir qilayotganingizda foydalanuvchi [[User:$1|$1]] ([[User talk:$1|munozara]]) tomonidan quyidagi sababga binoan yoʻqotilgan:\n: <em>$2</em>\nIltimos, sahifani qaytadan yaratmoqchi ekanligingizni tasdiqlang.",
+       "confirmrecreate-noreason": "Ushbu sahifa siz tahrir qilayotganingizda foydalanuvchi [[User:$1|$1]] ([[User talk:$1|munozara]]) tomonidan yoʻqotilgan. Iltimos, sahifani qaytadan yaratmoqchi ekanligingizni tasdiqlang.",
        "unit-pixel": " piksel",
        "imgmultipageprev": "← oldingi sahifa",
        "imgmultipagenext": "keyingi sahifa →",
index fbff85d..c862c3c 100644 (file)
@@ -21,9 +21,9 @@
        "tog-newpageshidepatrolled": "באַהאַלטן פאַטראלירטע בלעטער פון דער ליסטע פון נײַע בלעטער",
        "tog-extendwatchlist": "פארברייטערן די אויפפאסן ליסטע צו צייגן אלע פאסנדע ענדערונגען (אנדערשט: בלויז די לעצטע ענדערונג פון יעדן בלאט)",
        "tog-usenewrc": "גרופירן ענדערונגען לויטן בלאט אין \"לעצטע ענדערונגען\" און אויפֿפאסן ליסטע",
-       "tog-numberheadings": "נומערירן קעפלעך אויטאמאטיש",
+       "tog-numberheadings": "נומערירן קעפּלעך אויטאָמאַטיש",
        "tog-showtoolbar": "ווײַזן רעדאקטירן געצייג-שטאנג",
-       "tog-editondblclick": "רעדאקטירן בלעטער דורך טאפל קליק",
+       "tog-editondblclick": "רעדאַקטירן בלעטער דורך טאָפּל־קליק",
        "tog-editsectiononrightclick": "באמעגלעכן אפטייל רעדאקטירן דורכן רעכטס־קליקן אויף אפטייל קעפלעך",
        "tog-watchcreations": "צולייגן בלעטער וואס איך באשאף און טעקעס וואס איך לאד ארויף צו מיין אכטונג ליסטע",
        "tog-watchdefault": "צולייגן בלעטער וואס איך רעדאקטיר צו מיין אכטונג ליסטע",
index 5c703d6..13f4b01 100644 (file)
        "tog-shownumberswatching": "显示监视用户数",
        "tog-oldsig": "当前签名:",
        "tog-fancysig": "将签名视为维基文本(不自动生成链接)",
-       "tog-uselivepreview": "使用实时预览(试验中)",
+       "tog-uselivepreview": "使用实时预览",
        "tog-forceeditsummary": "未输入编辑摘要时提醒我",
        "tog-watchlisthideown": "隐藏监视列表中的我的编辑",
        "tog-watchlisthidebots": "隐藏监视列表中的机器人编辑",
        "file-no-thumb-animation": "'''注意:由于技术限制,该文件的缩略图无法进行动画处理。'''",
        "file-no-thumb-animation-gif": "'''注意:由于技术限制,高分辨率GIF图像的缩略图无法进行动画处理。'''",
        "newimages": "新文件图库",
-       "imagelisttext": "以下是按$2排列的'''$1'''个文件列表。",
+       "imagelisttext": "以下是按$2排列的<strong>$1</strong>个文件列表。",
        "newimages-summary": "本特殊页面展示最后上传的文件。",
        "newimages-legend": "过滤",
        "newimages-label": "文件名(或它的一部份):",
index 8b3a491..93e0ea5 100644 (file)
        "right-protect": "更改保護層級及編輯被連鎖保護的頁面",
        "right-editprotected": "編輯保護層級為 \"{{int:protect-level-sysop}}\" 的頁面",
        "right-editsemiprotected": "編輯保護層級為 \"{{int:protect-level-autoconfirmed}}\" 的頁面",
+       "right-editcontentmodel": "編輯頁面的內容模型",
        "right-editinterface": "編輯使用者介面",
        "right-editusercssjs": "編輯其他使用者的 CSS 和 JavaScript 檔案",
        "right-editusercss": "編輯其他使用者的 CSS 檔案",
        "action-viewmywatchlist": "檢視您的監視清單",
        "action-viewmyprivateinfo": "檢視您的個人資訊",
        "action-editmyprivateinfo": "編輯您的個人資訊",
+       "action-editcontentmodel": "編輯頁面的內容模型",
        "nchanges": "$1 次變更",
        "enhancedrc-since-last-visit": "自上次訪問已有 $1",
        "enhancedrc-history": "歷史",
        "tooltip-p-logo": "前往主頁面",
        "tooltip-n-mainpage": "前往主頁面",
        "tooltip-n-mainpage-description": "前往主頁面",
-       "tooltip-n-portal": "關於本專案,您可以做什麼、哪裡可以找到事情",
-       "tooltip-n-currentevents": "尋找新聞中最新動態的背景資訊",
+       "tooltip-n-portal": "關於本專案、您可以做什麼、哪裡可以找到您需要的事物",
+       "tooltip-n-currentevents": "於最新動態中尋找背景資訊",
        "tooltip-n-recentchanges": "列出此 Wiki 中的近期變更清單",
        "tooltip-n-randompage": "隨機進入一個頁面",
        "tooltip-n-help": "尋求協助的地方",
        "tag-filter-submit": "搜尋",
        "tag-list-wrapper": "([[Special:Tags|標籤]]:$2)",
        "tags-title": "標籤",
-       "tags-intro": "此頁面列出所有可用來標示編輯的標籤,以及這些標籤的含意。",
+       "tags-intro": "此頁面列出所有可用來標示編輯內容的標籤以及這些標籤所代表的意思。",
        "tags-tag": "標籤名稱",
-       "tags-display-header": "在更改清單中的出現方式",
-       "tags-description-header": "完整含意說明",
+       "tags-display-header": "在變更日誌中顯示的方式",
+       "tags-description-header": "意義完整的說明",
        "tags-active-header": "開啟?",
-       "tags-hitcount-header": "已加上標籤的更改",
+       "tags-hitcount-header": "已標記的變更",
        "tags-active-yes": "是",
        "tags-active-no": "否",
        "tags-edit": "編輯",
        "expand_templates_generate_xml": "顯示 XML 解析樹",
        "expand_templates_generate_rawhtml": "顯示原始 HTML",
        "expand_templates_preview": "預覽",
+       "expand_templates_preview_fail_html": "<em>因連線階段的資料遺失且 {{SITENAME}} 已開啟顯示原始 HTML 功能,為預防 JavaScript 攻擊已隱藏預覽內容。</em>\n\n<strong>若您目前的預覽動作並無非法,請再試一次。</strong>\n若仍然無效,請嘗試[[Special:UserLogout|登出]]並再登入一次。",
+       "expand_templates_preview_fail_html_anon": "<em>因您尚未登入且 {{SITENAME}} 已開啟顯示原始 HTML 功能,為預防 JavaScript 攻擊已隱藏預覽內容。</em>\n\n<strong>若您目前的預覽動作並無非法,請[[Special:UserLogin|登入]]後再試一次。</strong>",
        "pagelanguage": "頁面語言選擇器",
        "pagelang-name": "頁面",
        "pagelang-language": "語言",
        "log-name-pagelang": "更改語言日誌",
        "log-description-pagelang": "此頁為頁面語言的變更日誌。",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|已更改}}頁面 $3 的語言從 $4 到 $5。",
-       "default-skin-not-found": "哎呀!您於 <code dir=\"ltr\">$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您的安裝程序應包含以下外觀。 請參考 [https://www.mediawiki.org/wiki/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何開啟外觀並設為預設值的資訊。\n\n$2\n\n; 若您才剛安裝完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。 您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 透過 git 複製 <code>mediawiki/skins/*</code> 儲存庫中其中一個外觀到您安裝的 MediaWiki <code dir=\"ltr\">skins/</code> 目錄中。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。\n\n; 若您才剛升級 MediaWiki:\n: MediaWiki 1.24 與較新的版本不再自動開啟已安裝的外觀 (請參考 [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery 操作手冊:外觀自動搜尋])。 您可以將下列行貼上至 <code>LocalSettings.php</code> 來開啟所有目前已經安裝的外觀:\n\n<pre dir=\"ltr\">$3</pre>\n\n; 若您才剛修改 <code>LocalSettings.php</code>:\n: 請再次確認您輸入的外觀名稱是否有誤。",
-       "default-skin-not-found-no-skins": "哎呀!您於 <code>$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您未安裝任何的外觀。\n\n; 若您才剛安裝完或升級完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。 MediaWiki 1.24 或較新的版本在主要儲存庫中不再包含任何的外觀。 請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。 您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 透過 git 複製 <code>mediawiki/skins/*</code> 儲存庫中其中一個外觀到您安裝的 MediaWiki <code dir=\"ltr\">skins/</code> 目錄中。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。 請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何開啟外觀並設為預設值的資訊。",
+       "default-skin-not-found": "哎呀!您於 <code dir=\"ltr\">$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您的安裝程序應包含以下外觀。 請參考 [https://www.mediawiki.org/wiki/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何開啟外觀並設為預設值的資訊。\n\n$2\n\n; 若您才剛安裝完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。 您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 自 [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] 下載個別外觀 tarball。\n:* 透過 git 複製 <code>mediawiki/skins/*</code> 儲存庫中其中一個外觀到您安裝的 MediaWiki <code dir=\"ltr\">skins/</code> 目錄中。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。\n\n; 若您才剛升級 MediaWiki:\n: MediaWiki 1.24 與較新的版本不再自動開啟已安裝的外觀 (請參考 [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery 操作手冊:外觀自動搜尋])。 您可以將下列行貼上至 <code>LocalSettings.php</code> 來開啟所有目前已經安裝的外觀:\n\n<pre dir=\"ltr\">$3</pre>\n\n; 若您才剛修改 <code>LocalSettings.php</code>:\n: 請再次確認您輸入的外觀名稱是否有誤。",
+       "default-skin-not-found-no-skins": "哎呀!您於 <code>$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您未安裝任何的外觀。\n\n; 若您才剛安裝完或升級完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。 MediaWiki 1.24 或較新的版本在主要儲存庫中不再包含任何的外觀。 請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。 您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 自 [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] 下載個別外觀 tarball。\n:* 透過 git 複製 <code>mediawiki/skins/*</code> 儲存庫中其中一個外觀到您安裝的 MediaWiki <code dir=\"ltr\">skins/</code> 目錄中。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。 請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何開啟外觀並設為預設值的資訊。",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (已開啟)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''已停用''')",
        "mediastatistics": "媒體統計資訊",
index f13cb44..85ebd51 100644 (file)
@@ -895,10 +895,10 @@ class TextPassDumper extends BackupDumper {
                                $this->thisPage .= $data;
                        }
                }
-               elseif ( $this->lastName == "model"  ) {
+               elseif ( $this->lastName == "model" ) {
                        $this->thisRevModel .= $data;
                }
-               elseif ( $this->lastName == "format"  ) {
+               elseif ( $this->lastName == "format" ) {
                        $this->thisRevFormat .= $data;
                }
 
index b97e1b0..2f533cf 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 require_once __DIR__ . '/Maintenance.php';
-require_once 'PHPUnit/Autoload.php';
 
 /**
  * @ingroup Maintenance
@@ -43,6 +42,17 @@ class CheckLess extends Maintenance {
                // require it here.
                require_once __DIR__ . '/../tests/TestsAutoLoader.php';
 
+               // If phpunit isn't available by autoloader try pulling it in
+               if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
+                       require_once 'PHPUnit/Autoload.php';
+               }
+
+               // RequestContext::resetMain() will print warnings unless this
+               // is defined.
+               if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+                       define( 'MW_PHPUNIT_TEST', true );
+               }
+
                $textUICommand = new PHPUnit_TextUI_Command();
                $argv = array(
                        "$IP/tests/phpunit/phpunit.php",
index 66e8da0..d17b06d 100644 (file)
@@ -91,6 +91,7 @@ class FindHooks extends Maintenance {
                        $IP . '/includes/jobqueue/',
                        $IP . '/includes/json/',
                        $IP . '/includes/logging/',
+                       $IP . '/includes/mail/',
                        $IP . '/includes/media/',
                        $IP . '/includes/page/',
                        $IP . '/includes/parser/',
index 910f56b..45cc339 100644 (file)
@@ -471,7 +471,7 @@ class RecompressTracked {
         * @param int $pageId
         */
        function doPage( $pageId ) {
-               $title = Title::newFromId( $pageId );
+               $title = Title::newFromID( $pageId );
                if ( $title ) {
                        $titleText = $title->getPrefixedText();
                } else {
index 470647a..55f535d 100644 (file)
@@ -37,12 +37,18 @@ class UpdateArticleCount extends Maintenance {
                parent::__construct();
                $this->mDescription = "Count of the number of articles and update the site statistics table";
                $this->addOption( 'update', 'Update the site_stats table with the new count' );
+               $this->addOption( 'use-master', 'Count using the master database' );
        }
 
        public function execute() {
                $this->output( "Counting articles..." );
 
-               $counter = new SiteStatsInit( false );
+               if ( $this->hasOption( 'use-master' ) ) {
+                       $dbr = wfGetDB( DB_MASTER );
+               } else {
+                       $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+               }
+               $counter = new SiteStatsInit( $dbr );
                $result = $counter->articles();
 
                $this->output( "found {$result}.\n" );
index d6a3181..1eda5d6 100644 (file)
@@ -44,6 +44,7 @@ return array(
        'user.cssprefs' => array( 'class' => 'ResourceLoaderUserCSSPrefsModule' ),
 
        // Populate mediawiki.user placeholders with information about the current user
+       'user.defaults' => array( 'class' => 'ResourceLoaderUserDefaultsModule' ),
        'user.options' => array( 'class' => 'ResourceLoaderUserOptionsModule' ),
        'user.tokens' => array( 'class' => 'ResourceLoaderUserTokensModule' ),
 
@@ -757,6 +758,7 @@ return array(
                        'zh-cn' => 'resources/lib/moment/locale/zh-cn.js',
                        'zh-tw' => 'resources/lib/moment/locale/zh-tw.js',
                ),
+               'targets' => array( 'desktop', 'mobile' ),
        ),
 
        /* MediaWiki */
@@ -1042,7 +1044,16 @@ return array(
                'dependencies' => array(
                        'jquery.form',
                        'jquery.spinner',
+                       'mediawiki.api',
                        'mediawiki.action.history.diff',
+                       'mediawiki.util',
+                       'mediawiki.jqueryMsg',
+               ),
+               'messages' => array(
+                       'otherlanguages',
+                       'tooltip-p-lang',
+                       'summary-preview',
+                       'parentheses',
                ),
        ),
        'mediawiki.action.edit.stash' => array(
diff --git a/resources/lib/oojs-ui/images/grab.cur b/resources/lib/oojs-ui/images/grab.cur
new file mode 100644 (file)
index 0000000..fba3ddc
Binary files /dev/null and b/resources/lib/oojs-ui/images/grab.cur differ
diff --git a/resources/lib/oojs-ui/images/grabbing.cur b/resources/lib/oojs-ui/images/grabbing.cur
new file mode 100644 (file)
index 0000000..41aaa62
Binary files /dev/null and b/resources/lib/oojs-ui/images/grabbing.cur differ
index 2756079..8f048e4 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:30Z
+ * Date: 2014-12-06T00:33:19Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+       /*
+        * HACK: In order to style horizontally, we must override
+        * OO.ui.OptionWidget's display rule that is currently set
+        * to be 'block'
+        */
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
index bf7e39f..f3f635d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:19Z
+ * Date: 2014-12-06T00:33:09Z
  */
 /* Instantiation */
 
index 7d4acb5..ccbc0b3 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:30Z
+ * Date: 2014-12-06T00:33:19Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+       /*
+        * HACK: In order to style horizontally, we must override
+        * OO.ui.OptionWidget's display rule that is currently set
+        * to be 'block'
+        */
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
index e360991..6fed540 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:30Z
+ * Date: 2014-12-06T00:33:19Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+       /*
+        * HACK: In order to style horizontally, we must override
+        * OO.ui.OptionWidget's display rule that is currently set
+        * to be 'block'
+        */
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
        left: 1px;
        border-bottom: solid 0.2em #d3d3d3;
 }
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
-       cursor: default;
-}
 .oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span::before {
+       cursor: default;
        background-color: lightgrey;
 }
 .oo-ui-textInputWidget {
index 875dab4..5bb3691 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:19Z
+ * Date: 2014-12-06T00:33:09Z
  */
 /**
  * @class
index 644eadc..2eb4746 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:30Z
+ * Date: 2014-12-06T00:33:19Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+       /*
+        * HACK: In order to style horizontally, we must override
+        * OO.ui.OptionWidget's display rule that is currently set
+        * to be 'block'
+        */
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
        left: 1px;
        border-bottom: solid 0.2em #d3d3d3;
 }
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
-       cursor: default;
-}
 .oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span::before {
+       cursor: default;
        background-color: lightgrey;
 }
 .oo-ui-textInputWidget {
index 1209434..dc92f1f 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.2.4
+ * OOjs UI v0.4.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-12-02T18:45:19Z
+ * Date: 2014-12-06T00:33:09Z
  */
 ( function ( OO ) {
 
@@ -730,7 +730,7 @@ OO.ui.Element = function OoUiElement( config ) {
        config = config || {};
 
        // Properties
-       this.$ = config.$ || OO.ui.Element.getJQuery( document );
+       this.$ = config.$ || OO.ui.Element.static.getJQuery( document );
        this.data = config.data;
        this.$element = this.$( this.$.context.createElement( this.getTagName() ) );
        this.elementGroup = null;
@@ -777,7 +777,7 @@ OO.ui.Element.static.tagName = 'div';
  *   not in an iframe
  * @return {Function} Bound jQuery function
  */
-OO.ui.Element.getJQuery = function ( context, $iframe ) {
+OO.ui.Element.static.getJQuery = function ( context, $iframe ) {
        function wrapper( selector ) {
                return $( selector, wrapper.context );
        }
@@ -798,7 +798,7 @@ OO.ui.Element.getJQuery = function ( context, $iframe ) {
  * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Object to get the document for
  * @return {HTMLDocument|null} Document object
  */
-OO.ui.Element.getDocument = function ( obj ) {
+OO.ui.Element.static.getDocument = function ( obj ) {
        // jQuery - selections created "offscreen" won't have a context, so .context isn't reliable
        return ( obj[0] && obj[0].ownerDocument ) ||
                // Empty jQuery selections might have a context
@@ -819,7 +819,7 @@ OO.ui.Element.getDocument = function ( obj ) {
  * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the window for
  * @return {Window} Window object
  */
-OO.ui.Element.getWindow = function ( obj ) {
+OO.ui.Element.static.getWindow = function ( obj ) {
        var doc = this.getDocument( obj );
        return doc.parentWindow || doc.defaultView;
 };
@@ -831,7 +831,7 @@ OO.ui.Element.getWindow = function ( obj ) {
  * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
  * @return {string} Text direction, either 'ltr' or 'rtl'
  */
-OO.ui.Element.getDir = function ( obj ) {
+OO.ui.Element.static.getDir = function ( obj ) {
        var isDoc, isWin;
 
        if ( obj instanceof jQuery ) {
@@ -859,7 +859,7 @@ OO.ui.Element.getDir = function ( obj ) {
  * @param {Object} [offset] Offset to start with, used internally
  * @return {Object} Offset object, containing left and top properties
  */
-OO.ui.Element.getFrameOffset = function ( from, to, offset ) {
+OO.ui.Element.static.getFrameOffset = function ( from, to, offset ) {
        var i, len, frames, frame, rect;
 
        if ( !to ) {
@@ -904,7 +904,7 @@ OO.ui.Element.getFrameOffset = function ( from, to, offset ) {
  * @param {jQuery} $anchor Element to get $element's position relative to
  * @return {Object} Translated position coordinates, containing top and left properties
  */
-OO.ui.Element.getRelativePosition = function ( $element, $anchor ) {
+OO.ui.Element.static.getRelativePosition = function ( $element, $anchor ) {
        var iframe, iframePos,
                pos = $element.offset(),
                anchorPos = $anchor.offset(),
@@ -934,7 +934,7 @@ OO.ui.Element.getRelativePosition = function ( $element, $anchor ) {
  * @param {HTMLElement} el Element to measure
  * @return {Object} Dimensions object with `top`, `left`, `bottom` and `right` properties
  */
-OO.ui.Element.getBorders = function ( el ) {
+OO.ui.Element.static.getBorders = function ( el ) {
        var doc = el.ownerDocument,
                win = doc.parentWindow || doc.defaultView,
                style = win && win.getComputedStyle ?
@@ -961,7 +961,7 @@ OO.ui.Element.getBorders = function ( el ) {
  * @param {HTMLElement|Window} el Element to measure
  * @return {Object} Dimensions object with `borders`, `scroll`, `scrollbar` and `rect` properties
  */
-OO.ui.Element.getDimensions = function ( el ) {
+OO.ui.Element.static.getDimensions = function ( el ) {
        var $el, $win,
                doc = el.ownerDocument || el.document,
                win = doc.parentWindow || doc.defaultView;
@@ -1010,7 +1010,7 @@ OO.ui.Element.getDimensions = function ( el ) {
  * @param {string} [dimension] Dimension of scrolling to look for; `x`, `y` or omit for either
  * @return {HTMLElement} Closest scrollable container
  */
-OO.ui.Element.getClosestScrollableContainer = function ( el, dimension ) {
+OO.ui.Element.static.getClosestScrollableContainer = function ( el, dimension ) {
        var i, val,
                props = [ 'overflow' ],
                $parent = $( el ).parent();
@@ -1046,7 +1046,7 @@ OO.ui.Element.getClosestScrollableContainer = function ( el, dimension ) {
  *  to scroll in both directions
  * @param {Function} [config.complete] Function to call when scrolling completes
  */
-OO.ui.Element.scrollIntoView = function ( el, config ) {
+OO.ui.Element.static.scrollIntoView = function ( el, config ) {
        // Configuration initialization
        config = config || {};
 
@@ -1106,35 +1106,6 @@ OO.ui.Element.scrollIntoView = function ( el, config ) {
        }
 };
 
-/**
- * Bind a handler for an event on a DOM element.
- *
- * Used to be for working around a jQuery bug (jqbug.com/14180),
- * but obsolete as of jQuery 1.11.0.
- *
- * @static
- * @deprecated Use jQuery#on instead.
- * @param {HTMLElement|jQuery} el DOM element
- * @param {string} event Event to bind
- * @param {Function} callback Callback to call when the event fires
- */
-OO.ui.Element.onDOMEvent = function ( el, event, callback ) {
-       $( el ).on( event, callback );
-};
-
-/**
- * Unbind a handler bound with #static-method-onDOMEvent.
- *
- * @deprecated Use jQuery#off instead.
- * @static
- * @param {HTMLElement|jQuery} el DOM element
- * @param {string} event Event to unbind
- * @param {Function} [callback] Callback to unbind
- */
-OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
-       $( el ).off( event, callback );
-};
-
 /* Methods */
 
 /**
@@ -1224,7 +1195,7 @@ OO.ui.Element.prototype.isElementAttached = function () {
  * @return {HTMLDocument} Document object
  */
 OO.ui.Element.prototype.getElementDocument = function () {
-       return OO.ui.Element.getDocument( this.$element );
+       return OO.ui.Element.static.getDocument( this.$element );
 };
 
 /**
@@ -1233,14 +1204,14 @@ OO.ui.Element.prototype.getElementDocument = function () {
  * @return {Window} Window object
  */
 OO.ui.Element.prototype.getElementWindow = function () {
-       return OO.ui.Element.getWindow( this.$element );
+       return OO.ui.Element.static.getWindow( this.$element );
 };
 
 /**
  * Get closest scrollable container.
  */
 OO.ui.Element.prototype.getClosestScrollableElementContainer = function () {
-       return OO.ui.Element.getClosestScrollableContainer( this.$element[0] );
+       return OO.ui.Element.static.getClosestScrollableContainer( this.$element[0] );
 };
 
 /**
@@ -1269,29 +1240,7 @@ OO.ui.Element.prototype.setElementGroup = function ( group ) {
  * @param {Object} [config] Configuration options
  */
 OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
-       return OO.ui.Element.scrollIntoView( this.$element[0], config );
-};
-
-/**
- * Bind a handler for an event on this.$element
- *
- * @deprecated Use jQuery#on instead.
- * @param {string} event
- * @param {Function} callback
- */
-OO.ui.Element.prototype.onDOMEvent = function ( event, callback ) {
-       OO.ui.Element.onDOMEvent( this.$element, event, callback );
-};
-
-/**
- * Unbind a handler bound with #offDOMEvent
- *
- * @deprecated Use jQuery#off instead.
- * @param {string} event
- * @param {Function} callback
- */
-OO.ui.Element.prototype.offDOMEvent = function ( event, callback ) {
-       OO.ui.Element.offDOMEvent( this.$element, event, callback );
+       return OO.ui.Element.static.scrollIntoView( this.$element[0], config );
 };
 
 /**
@@ -1954,7 +1903,7 @@ OO.ui.Window.prototype.setManager = function ( manager ) {
        this.toggle( false );
 
        // Figure out directionality:
-       this.dir = OO.ui.Element.getDir( this.$iframe || this.$content ) || 'ltr';
+       this.dir = OO.ui.Element.static.getDir( this.$iframe || this.$content ) || 'ltr';
 
        return this;
 };
@@ -2140,7 +2089,7 @@ OO.ui.Window.prototype.hold = function ( data ) {
 
        this.getHoldProcess( data ).execute().done( function () {
                // Get the focused element within the window's content
-               var $focus = win.$content.find( OO.ui.Element.getDocument( win.$content ).activeElement );
+               var $focus = win.$content.find( OO.ui.Element.static.getDocument( win.$content ).activeElement );
 
                // Blur the focused element
                if ( $focus.length ) {
@@ -2255,7 +2204,7 @@ OO.ui.Window.prototype.load = function () {
        doc.close();
 
        // Properties
-       this.$ = OO.ui.Element.getJQuery( doc, this.$iframe );
+       this.$ = OO.ui.Element.static.getJQuery( doc, this.$iframe );
        this.$content = this.$( '.oo-ui-window-content' ).attr( 'tabIndex', 0 );
        this.$document = this.$( doc );
 
@@ -3109,7 +3058,7 @@ OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
  * @throws {Error} If windows being removed are not being managed
  */
 OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
-       var i, len, win, name,
+       var i, len, win, name, cleanupWindow,
                manager = this,
                promises = [],
                cleanup = function ( name, win ) {
@@ -3123,7 +3072,8 @@ OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
                if ( !win ) {
                        throw new Error( 'Cannot remove window' );
                }
-               promises.push( this.closeWindow( name ).then( cleanup.bind( null, name, win ) ) );
+               cleanupWindow = cleanup.bind( null, name, win );
+               promises.push( this.closeWindow( name ).then( cleanupWindow, cleanupWindow ) );
        }
 
        return $.when.apply( $, promises );
@@ -3153,7 +3103,7 @@ OO.ui.WindowManager.prototype.updateWindowSize = function ( win ) {
                return;
        }
 
-       var viewport = OO.ui.Element.getDimensions( win.getElementWindow() ),
+       var viewport = OO.ui.Element.static.getDimensions( win.getElementWindow() ),
                sizes = this.constructor.static.sizes,
                size = win.getSize();
 
@@ -4155,6 +4105,371 @@ OO.ui.GroupElement.prototype.clearItems = function () {
        return this;
 };
 
+/**
+ * A mixin for an element that can be dragged and dropped.
+ * Use in conjunction with DragGroupWidget
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ */
+OO.ui.DraggableElement = function OoUiDraggableElement() {
+       // Properties
+       this.index = null;
+
+       // Initialize and events
+       this.$element
+               .attr( 'draggable', true )
+               .addClass( 'oo-ui-draggableElement' )
+               .on( {
+                       dragstart: this.onDragStart.bind( this ),
+                       dragover: this.onDragOver.bind( this ),
+                       dragend: this.onDragEnd.bind( this ),
+                       drop: this.onDrop.bind( this )
+               } );
+};
+
+/* Events */
+
+/**
+ * @event dragstart
+ * @param {OO.ui.DraggableElement} item Dragging item
+ */
+
+/**
+ * @event dragend
+ */
+
+/**
+ * @event drop
+ */
+
+/* Methods */
+
+/**
+ * Respond to dragstart event.
+ * @param {jQuery.Event} event jQuery event
+ * @fires dragstart
+ */
+OO.ui.DraggableElement.prototype.onDragStart = function ( e ) {
+       var dataTransfer = e.originalEvent.dataTransfer;
+       // Define drop effect
+       dataTransfer.dropEffect = 'none';
+       dataTransfer.effectAllowed = 'move';
+       // We must set up a dataTransfer data property or Firefox seems to
+       // ignore the fact the element is draggable.
+       try {
+               dataTransfer.setData( 'application-x/OOjs-UI-draggable', this.getIndex() );
+       } catch ( err ) {
+               // The above is only for firefox. No need to set a catch clause
+               // if it fails, move on.
+       }
+       // Add dragging class
+       this.$element.addClass( 'oo-ui-draggableElement-dragging' );
+       // Emit event
+       this.emit( 'dragstart', this );
+       return true;
+};
+
+/**
+ * Respond to dragend event.
+ * @fires dragend
+ */
+OO.ui.DraggableElement.prototype.onDragEnd = function () {
+       this.$element.removeClass( 'oo-ui-draggableElement-dragging' );
+       this.emit( 'dragend' );
+};
+
+/**
+ * Handle drop event.
+ * @param {jQuery.Event} event jQuery event
+ * @fires drop
+ */
+OO.ui.DraggableElement.prototype.onDrop = function ( e ) {
+       e.preventDefault();
+       this.emit( 'drop', e );
+};
+
+/**
+ * In order for drag/drop to work, the dragover event must
+ * return false and stop propogation.
+ */
+OO.ui.DraggableElement.prototype.onDragOver = function ( e ) {
+       e.preventDefault();
+};
+
+/**
+ * Set item index.
+ * Store it in the DOM so we can access from the widget drag event
+ * @param {number} Item index
+ */
+OO.ui.DraggableElement.prototype.setIndex = function ( index ) {
+       if ( this.index !== index ) {
+               this.index = index;
+               this.$element.data( 'index', index );
+       }
+};
+
+/**
+ * Get item index
+ * @return {number} Item index
+ */
+OO.ui.DraggableElement.prototype.getIndex = function () {
+       return this.index;
+};
+
+/**
+ * Element containing a sequence of child elements that can be dragged
+ * and dropped.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>`
+ * @cfg {string} [orientation] Item orientation, 'horizontal' or 'vertical'. Defaults to 'vertical'
+ */
+OO.ui.DraggableGroupElement = function OoUiDraggableGroupElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.GroupElement.call( this, config );
+
+       // Properties
+       this.orientation = config.orientation || 'vertical';
+       this.dragItem = null;
+       this.itemDragOver = null;
+       this.itemKeys = {};
+       this.sideInsertion = '';
+
+       // Events
+       this.aggregate( {
+               dragstart: 'itemDragStart',
+               dragend: 'itemDragEnd',
+               drop: 'itemDrop'
+       } );
+       this.connect( this, {
+               itemDragStart: 'onItemDragStart',
+               itemDrop: 'onItemDrop',
+               itemDragEnd: 'onItemDragEnd'
+       } );
+       this.$element.on( {
+               dragover: $.proxy( this.onDragOver, this ),
+               dragleave: $.proxy( this.onDragLeave, this )
+       } );
+
+       // Initialize
+       if ( $.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+       this.$placeholder = $( '<div>' )
+               .addClass( 'oo-ui-draggableGroupElement-placeholder' );
+       this.$element
+               .addClass( 'oo-ui-draggableGroupElement' )
+               .append( this.$status )
+               .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' )
+               .prepend( this.$placeholder );
+};
+
+/* Setup */
+OO.mixinClass( OO.ui.DraggableGroupElement, OO.ui.GroupElement );
+
+/* Events */
+
+/**
+ * @event reorder
+ * @param {OO.ui.DraggableElement} item Reordered item
+ * @param {number} [newIndex] New index for the item
+ */
+
+/* Methods */
+
+/**
+ * Respond to item drag start event
+ * @param {OO.ui.DraggableElement} item Dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
+       var i, len;
+
+       // Map the index of each object
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               this.items[i].setIndex( i );
+       }
+
+       if ( this.orientation === 'horizontal' ) {
+               // Set the height of the indicator
+               this.$placeholder.css( {
+                       height: item.$element.outerHeight(),
+                       width: 2
+               } );
+       } else {
+               // Set the width of the indicator
+               this.$placeholder.css( {
+                       height: 2,
+                       width: item.$element.outerWidth()
+               } );
+       }
+       this.setDragItem( item );
+};
+
+/**
+ * Respond to item drag end event
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDragEnd = function () {
+       this.unsetDragItem();
+       return false;
+};
+
+/**
+ * Handle drop event and switch the order of the items accordingly
+ * @param {OO.ui.DraggableElement} item Dropped item
+ * @fires reorder
+ */
+OO.ui.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
+       var toIndex = item.getIndex();
+       // Check if the dropped item is from the current group
+       // TODO: Figure out a way to configure a list of legally droppable
+       // elements even if they are not yet in the list
+       if ( this.getDragItem() ) {
+               // If the insertion point is 'after', the insertion index
+               // is shifted to the right (or to the left in RTL, hence 'after')
+               if ( this.sideInsertion === 'after' ) {
+                       toIndex++;
+               }
+               // Emit change event
+               this.emit( 'reorder', this.getDragItem(), toIndex );
+       }
+       // Return false to prevent propogation
+       return false;
+};
+
+/**
+ * Handle dragleave event.
+ */
+OO.ui.DraggableGroupElement.prototype.onDragLeave = function () {
+       // This means the item was dragged outside the widget
+       this.$placeholder
+               .css( 'left', 0 )
+               .hide();
+};
+
+/**
+ * Respond to dragover event
+ * @param {jQuery.Event} event Event details
+ */
+OO.ui.DraggableGroupElement.prototype.onDragOver = function ( e ) {
+       var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
+               itemSize, cssOutput, dragPosition, itemIndex, itemPosition,
+               clientX = e.originalEvent.clientX,
+               clientY = e.originalEvent.clientY;
+
+       // Get the OptionWidget item we are dragging over
+       dragOverObj = this.getElementDocument().elementFromPoint( clientX, clientY );
+       $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' );
+       if ( $optionWidget[0] ) {
+               itemOffset = $optionWidget.offset();
+               itemBoundingRect = $optionWidget[0].getBoundingClientRect();
+               itemPosition = $optionWidget.position();
+               itemIndex = $optionWidget.data( 'index' );
+       }
+
+       if (
+               itemOffset &&
+               this.isDragging() &&
+               itemIndex !== this.getDragItem().getIndex()
+       ) {
+               if ( this.orientation === 'horizontal' ) {
+                       // Calculate where the mouse is relative to the item width
+                       itemSize = itemBoundingRect.width;
+                       itemMidpoint = itemBoundingRect.left + itemSize / 2;
+                       dragPosition = clientX;
+                       // Which side of the item we hover over will dictate
+                       // where the placeholder will appear, on the left or
+                       // on the right
+                       cssOutput = {
+                               left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
+                               top: itemPosition.top
+                       };
+               } else {
+                       // Calculate where the mouse is relative to the item height
+                       itemSize = itemBoundingRect.height;
+                       itemMidpoint = itemBoundingRect.top + itemSize / 2;
+                       dragPosition = clientY;
+                       // Which side of the item we hover over will dictate
+                       // where the placeholder will appear, on the top or
+                       // on the bottom
+                       cssOutput = {
+                               top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
+                               left: itemPosition.left
+                       };
+               }
+               // Store whether we are before or after an item to rearrange
+               // For horizontal layout, we need to account for RTL, as this is flipped
+               if (  this.orientation === 'horizontal' && this.$element.css( 'direction' ) === 'rtl' ) {
+                       this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before';
+               } else {
+                       this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after';
+               }
+               // Add drop indicator between objects
+               if ( this.sideInsertion ) {
+                       this.$placeholder
+                               .css( cssOutput )
+                               .show();
+               } else {
+                       this.$placeholder
+                               .css( {
+                                       left: 0,
+                                       top: 0
+                               } )
+                               .hide();
+               }
+       } else {
+               // This means the item was dragged outside the widget
+               this.$placeholder
+                       .css( 'left', 0 )
+                       .hide();
+       }
+       // Prevent default
+       e.preventDefault();
+};
+
+/**
+ * Set a dragged item
+ * @param {OO.ui.DraggableElement} item Dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.setDragItem = function ( item ) {
+       this.dragItem = item;
+};
+
+/**
+ * Unset the current dragged item
+ */
+OO.ui.DraggableGroupElement.prototype.unsetDragItem = function () {
+       this.dragItem = null;
+       this.itemDragOver = null;
+       this.$placeholder.hide();
+       this.sideInsertion = '';
+};
+
+/**
+ * Get the current dragged item
+ * @return {OO.ui.DraggableElement|null} item Dragged item or null if no item is dragged
+ */
+OO.ui.DraggableGroupElement.prototype.getDragItem = function () {
+       return this.dragItem;
+};
+
+/**
+ * Check if there's an item being dragged.
+ * @return {Boolean} Item is being dragged
+ */
+OO.ui.DraggableGroupElement.prototype.isDragging = function () {
+       return this.getDragItem() !== null;
+};
+
 /**
  * Element containing an icon.
  *
@@ -4313,6 +4628,15 @@ OO.ui.IconElement.prototype.getIcon = function () {
        return this.icon;
 };
 
+/**
+ * Get icon title.
+ *
+ * @return {string} Icon title text
+ */
+OO.ui.IconElement.prototype.getIconTitle = function () {
+       return this.iconTitle;
+};
+
 /**
  * Element containing an indicator.
  *
@@ -4971,7 +5295,7 @@ OO.ui.ClippableElement.prototype.toggleClipping = function ( clipping ) {
                        // If the clippable container is the body, we have to listen to scroll events and check
                        // jQuery.scrollTop on the window because of browser inconsistencies
                        this.$clippableScroller = this.$clippableContainer.is( 'body' ) ?
-                               this.$( OO.ui.Element.getWindow( this.$clippableContainer ) ) :
+                               this.$( OO.ui.Element.static.getWindow( this.$clippableContainer ) ) :
                                this.$clippableContainer;
                        this.$clippableScroller.on( 'scroll', this.onClippableContainerScrollHandler );
                        this.$clippableWindow = this.$( this.getElementWindow() )
@@ -6460,7 +6784,7 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
        }
        if ( this.autoFocus ) {
                // Event 'focus' does not bubble, but 'focusin' does
-               this.stackLayout.onDOMEvent( 'focusin', this.onStackLayoutFocus.bind( this ) );
+               this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
        }
 
        // Initialization
@@ -6685,7 +7009,7 @@ OO.ui.BookletLayout.prototype.getPage = function ( name ) {
  *
  * @return {string|null} Current page name
  */
-OO.ui.BookletLayout.prototype.getPageName = function () {
+OO.ui.BookletLayout.prototype.getCurrentPageName = function () {
        return this.currentPageName;
 };
 
@@ -7228,7 +7552,7 @@ OO.ui.GridLayout.prototype.update = function () {
                                top: Math.round( top * 100 ) + '%'
                        };
                        // If RTL, reverse:
-                       if ( OO.ui.Element.getDir( this.$.context ) === 'rtl' ) {
+                       if ( OO.ui.Element.static.getDir( this.$.context ) === 'rtl' ) {
                                dimensions.right = Math.round( left * 100 ) + '%';
                        } else {
                                dimensions.left = Math.round( left * 100 ) + '%';
@@ -7312,7 +7636,6 @@ OO.inheritClass( OO.ui.PanelLayout, OO.ui.Layout );
  * @constructor
  * @param {string} name Unique symbolic name of page
  * @param {Object} [config] Configuration options
- * @param {string} [outlineItem] Outline item widget
  */
 OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
        // Configuration initialization
@@ -7323,7 +7646,7 @@ OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
 
        // Properties
        this.name = name;
-       this.outlineItem = config.outlineItem || null;
+       this.outlineItem = null;
        this.active = false;
 
        // Initialization
@@ -7427,7 +7750,6 @@ OO.ui.PageLayout.prototype.setActive = function ( active ) {
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {boolean} [continuous=false] Show all pages, one after another
- * @cfg {string} [icon=''] Symbolic icon name
  * @cfg {OO.ui.Layout[]} [items] Layouts to add
  */
 OO.ui.StackLayout = function OoUiStackLayout( config ) {
@@ -8154,7 +8476,7 @@ OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
        this.lookupInput = input;
        this.$overlay = config.$overlay || this.$element;
        this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, {
-               $: OO.ui.Element.getJQuery( this.$overlay ),
+               $: OO.ui.Element.static.getJQuery( this.$overlay ),
                input: this.lookupInput,
                $container: config.$container
        } );
@@ -8669,7 +8991,7 @@ OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.GroupElement );
  */
 OO.ui.ButtonWidget = function OoUiButtonWidget( config ) {
        // Configuration initialization
-       config = $.extend( { target: '_blank' }, config );
+       config = config || {};
 
        // Parent constructor
        OO.ui.ButtonWidget.super.call( this, config );
@@ -9374,15 +9696,15 @@ OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
  */
 OO.ui.InputWidget.prototype.setValue = function ( value ) {
        value = this.cleanUpValue( value );
+       // Update the DOM if it has changed. Note that with cleanUpValue, it
+       // is possible for the DOM value to change without this.value changing.
+       if ( this.$input.val() !== value ) {
+               this.$input.val( value );
+       }
        if ( this.value !== value ) {
                this.value = value;
                this.emit( 'change', this.value );
        }
-       // Update the DOM if it has changed. Note that with cleanUpValue, it
-       // is possible for the DOM value to change without this.value changing.
-       if ( this.$input.val() !== this.value ) {
-               this.$input.val( this.value );
-       }
        return this;
 };
 
@@ -9779,6 +10101,14 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
        this.validate = null;
 
+       // Clone for resizing
+       if ( this.autosize ) {
+               this.$clone = this.$input
+                       .clone()
+                       .insertAfter( this.$input )
+                       .hide();
+       }
+
        this.setValidation( config.validate );
 
        // Events
@@ -9941,28 +10271,40 @@ OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
  * @chainable
  */
 OO.ui.TextInputWidget.prototype.adjustSize = function () {
-       var $clone, scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
+       var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
 
        if ( this.multiline && this.autosize && this.$input.val() !== this.valCache ) {
-               $clone = this.$input.clone()
+               this.$clone
                        .val( this.$input.val() )
+                       .attr( 'rows', '' )
                        // Set inline height property to 0 to measure scroll height
-                       .css( 'height', 0 )
-                       .insertAfter( this.$input );
+                       .css( 'height', 0 );
+
+               this.$clone[0].style.display = 'block';
+
                this.valCache = this.$input.val();
-               scrollHeight = $clone[0].scrollHeight;
+
+               scrollHeight = this.$clone[0].scrollHeight;
+
                // Remove inline height property to measure natural heights
-               $clone.css( 'height', '' );
-               innerHeight = $clone.innerHeight();
-               outerHeight = $clone.outerHeight();
+               this.$clone.css( 'height', '' );
+               innerHeight = this.$clone.innerHeight();
+               outerHeight = this.$clone.outerHeight();
+
                // Measure max rows height
-               $clone.attr( 'rows', this.maxRows ).css( 'height', 'auto' ).val( '' );
-               maxInnerHeight = $clone.innerHeight();
+               this.$clone
+                       .attr( 'rows', this.maxRows )
+                       .css( 'height', 'auto' )
+                       .val( '' );
+               maxInnerHeight = this.$clone.innerHeight();
+
                // Difference between reported innerHeight and scrollHeight with no scrollbars present
                // Equals 1 on Blink-based browsers and 0 everywhere else
-               measurementError = maxInnerHeight - $clone[0].scrollHeight;
-               $clone.remove();
+               measurementError = maxInnerHeight - this.$clone[0].scrollHeight;
                idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
+
+               this.$clone[0].style.display = 'none';
+
                // Only apply inline height when expansion beyond natural height is needed
                if ( idealHeight > innerHeight ) {
                        // Use the difference between the inner and outer height as a buffer
@@ -10078,7 +10420,7 @@ OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
        ) );
        this.menu = new OO.ui.TextInputMenuSelectWidget( this.input, $.extend(
                {
-                       $: OO.ui.Element.getJQuery( this.$overlay ),
+                       $: OO.ui.Element.static.getJQuery( this.$overlay ),
                        widget: this,
                        input: this.input,
                        disabled: this.isDisabled()
@@ -11616,32 +11958,30 @@ OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
 /**
  * Get an item relative to another one.
  *
- * @param {OO.ui.OptionWidget} item Item to start at
- * @param {number} direction Direction to move in, -1 to look backward, 1 to move forward
+ * @param {OO.ui.OptionWidget|null} item Item to start at, null to get relative to list start
+ * @param {number} direction Direction to move in, -1 to move backward, 1 to move forward
  * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the menu
  */
 OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction ) {
-       var inc = direction > 0 ? 1 : -1,
-               len = this.items.length,
-               index = item instanceof OO.ui.OptionWidget ?
-                       $.inArray( item, this.items ) : ( inc > 0 ? -1 : 0 ),
-               stopAt = Math.max( Math.min( index, len - 1 ), 0 ),
-               i = inc > 0 ?
-                       // Default to 0 instead of -1, if nothing is selected let's start at the beginning
-                       Math.max( index, -1 ) :
-                       // Default to n-1 instead of -1, if nothing is selected let's start at the end
-                       Math.min( index, len );
-
-       while ( len !== 0 ) {
-               i = ( i + inc + len ) % len;
-               item = this.items[i];
+       var currentIndex, nextIndex, i,
+               increase = direction > 0 ? 1 : -1,
+               len = this.items.length;
+
+       if ( item instanceof OO.ui.OptionWidget ) {
+               currentIndex = $.inArray( item, this.items );
+               nextIndex = ( currentIndex + increase + len ) % len;
+       } else {
+               // If no item is selected and moving forward, start at the beginning.
+               // If moving backward, start at the end.
+               nextIndex = direction > 0 ? 0 : len - 1;
+       }
+
+       for ( i = 0; i < len; i++ ) {
+               item = this.items[nextIndex];
                if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
                        return item;
                }
-               // Stop iterating when we've looped all the way around
-               if ( i === stopAt ) {
-                       break;
-               }
+               nextIndex = ( nextIndex + increase + len ) % len;
        }
        return null;
 };
@@ -12149,7 +12489,7 @@ OO.ui.TextInputMenuSelectWidget.prototype.toggle = function ( visible ) {
  */
 OO.ui.TextInputMenuSelectWidget.prototype.position = function () {
        var $container = this.$container,
-               pos = OO.ui.Element.getRelativePosition( $container, this.$element.offsetParent() );
+               pos = OO.ui.Element.static.getRelativePosition( $container, this.$element.offsetParent() );
 
        // Position under input
        pos.top += $container.height();
index 662a688..3796b0b 100644 (file)
                                // Tanslations for conforming browser names
                                nameTranslations = [],
                                // Names of known layout engines
-                               layouts = ['gecko', 'konqueror', 'msie', 'trident', 'opera', 'webkit'],
+                               layouts = ['gecko', 'konqueror', 'msie', 'trident', 'edge', 'opera', 'webkit'],
                                // Translations for conforming layout names
                                layoutTranslations = [ ['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto'] ],
                                // Names of supported layout engines for version number
-                               layoutVersions = ['applewebkit', 'gecko', 'trident'],
+                               layoutVersions = ['applewebkit', 'gecko', 'trident', 'edge'],
                                // Names of known operating systems
                                platforms = ['win', 'wow64', 'mac', 'linux', 'sunos', 'solaris', 'iphone'],
                                // Translations for conforming operating system names
                                        version = match[1];
                                }
                        }
+                       // And IE 12's different lies about not being IE
+                       if ( name === 'chrome' && ( match = ua.match( /\bedge\/([0-9\.]*)/ ) ) ) {
+                               name = 'msie';
+                               version = match[1];
+                               layout = 'edge';
+                               layoutversion = parseInt( match[1], 10 );
+                       }
                        // And Amazon Silk's lies about being Android on mobile or Safari on desktop
                        if ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) {
                                if ( match[1] ) {
index 632769b..bd6518d 100644 (file)
                                                                selText = selText.replace( /\r?\n/g, '\r\n' );
                                                                post = post.replace( /\r?\n/g, '\r\n' );
                                                        }
-                                                       if ( isSample && options.selectPeri && !options.splitlines ) {
+                                                       if ( isSample && options.selectPeri && ( !options.splitlines || ( options.splitlines && selText.indexOf( '\n' ) === -1 ) ) ) {
                                                                this.selectionStart = startPos + pre.length;
                                                                this.selectionEnd = startPos + pre.length + selText.length;
                                                        } else {
index 6b212c2..e4f4249 100644 (file)
@@ -8,16 +8,16 @@
         * @param {jQuery.Event} e
         */
        function doLivePreview( e ) {
-               var $wikiPreview, $editform, copySelectors, $copyElements, $spinner,
-                       targetUrl, postData, $previewDataHolder;
+               var isDiff, api, request, postData, copySelectors, section,
+                       $wikiPreview, $wikiDiff, $editform, $copyElements, $spinner;
 
                e.preventDefault();
 
-               // Deprecated: Use mw.hook instead
-               $( mw ).trigger( 'LivePreviewPrepare' );
-
+               isDiff = ( e.target.name === 'wpDiff' );
                $wikiPreview = $( '#wikiPreview' );
+               $wikiDiff = $( '#wikiDiff' );
                $editform = $( '#editform' );
+               section = $editform.find( '[name="wpSection"]' ).val();
 
                // Show #wikiPreview if it's hidden to be able to scroll to it
                // (if it is hidden, it's also empty, so nothing changes in the rendering)
                // Jump to where the preview will appear
                $wikiPreview[0].scrollIntoView();
 
-               // List of selectors matching elements that we will
-               // update from from the ajax-loaded preview page.
                copySelectors = [
                        // Main
                        '#firstHeading',
                        '#wikiPreview',
                        '#wikiDiff',
                        '#catlinks',
-                       '.hiddencats',
                        '#p-lang',
                        // Editing-related
                        '.templatesUsed',
                // (e.g. empty #catlinks)
                $copyElements.animate( { opacity: 0.4 }, 'fast' );
 
-               $previewDataHolder = $( '<div>' );
-               targetUrl = $editform.attr( 'action' );
-               targetUrl += targetUrl.indexOf( '?' ) !== -1 ? '&' : '?';
-               targetUrl += $.param( {
-                       debug: mw.config.get( 'debug' ),
+               api = new mw.Api();
+               postData = {
+                       action: 'parse',
                        uselang: mw.config.get( 'wgUserLanguage' ),
-                       useskin: mw.config.get( 'skin' )
-               } );
+                       title: mw.config.get( 'wgPageName' ),
+                       text: $editform.find( '#wpTextbox1' ).val(),
+                       summary: $editform.find( '#wpSummary' ).val()
+               };
 
-               // Gather all the data from the form
-               postData = $editform.formToArray();
-               postData.push( {
-                       name: e.target.name,
-                       value: ''
-               } );
+               if ( isDiff ) {
+                       $wikiPreview.hide();
 
-               // Load new preview data.
-               // TODO: This should use the action=parse API instead of loading the entire page,
-               // although that requires figuring out how to convert that raw data into proper HTML.
-               $previewDataHolder.load( targetUrl + ' ' + copySelectors.join( ',' ), postData, function () {
-                       var i, $from, $next, $parent;
+                       // First PST the input, then diff it
+                       postData.onlypst = '';
+                       request = api.post( postData );
+                       request.done( function ( response ) {
+                               var postData;
+                               postData = {
+                                       action: 'query',
+                                       indexpageids: '',
+                                       prop: 'revisions',
+                                       titles: mw.config.get( 'wgPageName' ),
+                                       rvdifftotext: response.parse.text['*'],
+                                       rvprop: ''
+                               };
+                               if ( section !== '' ) {
+                                       postData.rvsection = section;
+                               }
+                               return api.post( postData ).done( function ( result2 ) {
+                                       try {
+                                               var diffHtml = result2.query.pages[result2.query.pageids[0]]
+                                                       .revisions[0].diff['*'];
+                                               $wikiDiff.find( 'table.diff tbody' ).html( diffHtml );
+                                       } catch ( e ) {
+                                               // "result.blah is undefined" error, ignore
+                                               mw.log.warn( e );
+                                       }
+                                       $wikiDiff.show();
+                               } );
+                       } );
+               } else {
+                       $wikiDiff.hide();
+                       $.extend( postData, {
+                               pst: '',
+                               preview: '',
+                               prop: 'text|displaytitle|modules|categorieshtml|templates|langlinks|limitreporthtml'
+                       } );
+                       if ( section !== '' ) {
+                               postData.sectionpreview = '';
+                       }
+                       request = api.post( postData );
+                       request.done( function ( response ) {
+                               var li, newList, $next, $parent, $list;
+                               if ( response.parse.modules ) {
+                                       mw.loader.load( response.parse.modules.concat(
+                                               response.parse.modulescripts,
+                                               response.parse.modulestyles,
+                                               response.parse.modulemessages ) );
+                               }
+                               if ( response.parse.displaytitle ) {
+                                       $( '#firstHeading' ).html( '<span dir="auto">' + response.parse.displaytitle + '</span>' );
+                               }
+                               if ( response.parse.categorieshtml ) {
+                                       $( '#catlinks' ).replaceWith( response.parse.categorieshtml['*'] );
+                               }
+                               if ( response.parse.templates ) {
+                                       newList = [];
+                                       $.each( response.parse.templates, function ( i, template ) {
+                                               li = $( '<li>' )
+                                                       .append( $('<a>')
+                                                               .attr( {
+                                                                       'href': mw.util.getUrl( template['*'] ),
+                                                                       'class': ( template.exists !== undefined ? '' : 'new' )
+                                                               } )
+                                                               .text( template['*'] )
+                                                       );
+                                               newList.push( li );
+                                       } );
 
-                       // Copy the contents of the specified elements from the loaded page to the real page.
-                       // Also copy their class attributes.
-                       for ( i = 0; i < copySelectors.length; i++ ) {
-                               $from = $previewDataHolder.find( copySelectors[i] );
+                                       $editform.find( '.mw-editfooter-list' ).detach().empty().append( newList ).appendTo( '.templatesUsed' );
+                               }
+                               if ( response.parse.limitreporthtml ) {
+                                       $( '.limitreport' ).html( response.parse.limitreporthtml['*'] );
+                               }
+                               if ( response.parse.langlinks && mw.config.get( 'skin' ) === 'vector' ) {
+                                       newList = [];
+                                       $.each( response.parse.langlinks, function ( i, langlink ) {
+                                               li = $( '<li>' )
+                                                       .addClass( 'interlanguage-link interwiki-' + langlink.lang )
+                                                       .append( $( '<a>' )
+                                                               .attr( {
+                                                                       'href': langlink.url,
+                                                                       'title': langlink['*'] + ' - ' + langlink.langname,
+                                                                       'lang': langlink.lang,
+                                                                       'hreflang': langlink.lang
+                                                               } )
+                                                               .text( langlink.autonym )
+                                                       );
+                                               newList.push( li );
+                                       } );
+                                       $list = $( '#p-lang ul' );
+                                       $parent = $list.parent();
+                                       $list.detach().empty().append( newList ).prependTo( $parent );
+                               }
 
-                               if ( copySelectors[i] === '#wikiPreview' ) {
+                               if ( response.parse.text['*'] ) {
                                        $next = $wikiPreview.next();
                                        // If there is no next node, use parent instead.
                                        // Only query parent if needed, false otherwise.
 
                                        $wikiPreview
                                                .detach()
-                                               .empty()
-                                               .append( $from.contents() )
-                                               .attr( 'class', $from.attr( 'class' ) );
+                                               .html( response.parse.text['*'] );
 
                                        mw.hook( 'wikipage.content' ).fire( $wikiPreview );
 
                                        } else {
                                                $next.before( $wikiPreview );
                                        }
+                                       $wikiPreview.show();
 
-                               } else {
-                                       $( copySelectors[i] )
-                                               .empty()
-                                               .append( $from.contents() )
-                                               .attr( 'class', $from.attr( 'class' ) );
                                }
+                       } );
+               }
+               request.done( function ( response ) {
+                       if ( response.parse.parsedsummary ) {
+                               // TODO implement special behavior for section === 'new'
+                               $editform.find( '.mw-summary-preview' )
+                                       .empty()
+                                       .append(
+                                               mw.message( 'summary-preview' ).parse(),
+                                               ' ',
+                                               $( '<span>' ).addClass( 'comment' ).html(
+                                                       // There is no equivalent to rawParams
+                                                       mw.message( 'parentheses' ).escaped()
+                                                               .replace( '$1', response.parse.parsedsummary['*'] )
+                                               )
+                                       );
                        }
-
-                       // Deprecated: Use mw.hook instead
-                       $( mw ).trigger( 'LivePreviewDone', [copySelectors] );
-
+               } );
+               request.always( function () {
                        $spinner.remove();
                        $copyElements.animate( {
                                opacity: 1
                // have to fish and (hopefully) put them in the right place (since skins
                // can change where they are output).
 
-               if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) ) {
-                       $( '#p-tb' ).after(
-                               $( '<div>' ).attr( 'id', 'p-lang' )
+               if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) && mw.config.get( 'skin' ) === 'vector' ) {
+                       $( '.portal:last' ).after(
+                               $( '<div>' ).attr( {
+                                       'class': 'portal',
+                                       'id': 'p-lang',
+                                       'role': 'navigation',
+                                       'title': mw.msg( 'tooltip-p-lang' ),
+                                       'aria-labelledby': 'p-lang-label'
+                               } )
+                               .append( $( '<h3>' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) )
+                               .append( $( '<div>' ).addClass( 'body' ).append( '<ul>' ) )
                        );
                }
 
 
                if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
                        $( '#wikiPreview' ).after(
-                               $( '<div>' ).attr( 'id', 'wikiDiff' )
+                               $( '<div>' )
+                                       .attr( 'id', 'wikiDiff' )
+                                       .html( '<table class="diff"><col class="diff-marker"/><col class="diff-content"/>' +
+                                               '<col class="diff-marker"/><col class="diff-content"/><tbody/></table>' )
                        );
                }
 
diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png
new file mode 100644 (file)
index 0000000..712b1b4
Binary files /dev/null and b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.png differ
diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg b/resources/src/mediawiki.legacy/images/magnify-clip-ltr.svg
new file mode 100644 (file)
index 0000000..4d3dcb6
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 15" width="15" height="11">
+    <g id="magnify-clip" fill="#fff" stroke="#000">
+        <path id="bigbox" d="M1.509 1.865h10.99v7.919h-10.99z"/>
+        <path id="smallbox" d="M-1.499 6.868h5.943v4.904h-5.943z"/>
+    </g>
+</svg>
diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png
new file mode 100644 (file)
index 0000000..1d03a8c
Binary files /dev/null and b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.png differ
diff --git a/resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg b/resources/src/mediawiki.legacy/images/magnify-clip-rtl.svg
new file mode 100644 (file)
index 0000000..582e4ae
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 15" width="15" height="11">
+    <g id="magnify-clip" fill="#fff" stroke="#000">
+        <path id="bigbox" d="M9.491 1.865h-10.99v7.919h10.99z"/>
+        <path id="smallbox" d="M12.499 6.868h-5.943v4.904h5.943z"/>
+    </g>
+</svg>
index d92d3bb..c2bd5a7 100644 (file)
@@ -119,8 +119,12 @@ div.magnify a {
        /* …and replace it with the image */
        width: 15px;
        height: 11px;
-       /* @embed */
+       /* Use same SVG support hack as mediawiki.legacy's shared.css */
        background: url(images/magnify-clip-ltr.png) center center no-repeat;
+       /* @embed */
+       background-image: -webkit-linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg);
+       /* @embed */
+       background-image: linear-gradient(transparent, transparent), url(images/magnify-clip-ltr.svg);
        /* Don't annoy people who copy-paste everything too much */
        -moz-user-select: none;
        -webkit-user-select: none;
index bb5ddfc..abfb279 100644 (file)
         */
 
        /**
-        * A factory method to create a variation of mw.Uri with a different default location (for
-        * relative URLs, including protocol-relative URLs). Used so the library is still testable &
-        * purely functional.
+        * A factory method to create a Uri class with a default location to resolve relative URLs
+        * against (including protocol-relative URLs).
         *
         * @method
+        * @param {string|Function} documentLocation A full url, or function returning one.
+        *  If passed a function, the return value may change over time and this will be honoured. (T74334)
         * @member mw
         */
        mw.UriRelative = function ( documentLocation ) {
-               var defaultUri;
+               var getDefaultUri = ( function () {
+                       // Cache
+                       var href, uri;
+
+                       return function () {
+                               var hrefCur = typeof documentLocation === 'string' ? documentLocation : documentLocation();
+                               if ( href === hrefCur ) {
+                                       return uri;
+                               }
+                               href = hrefCur;
+                               uri = new Uri( href );
+                               return uri;
+                       };
+               }() );
 
                /**
                 * @class mw.Uri
                 *  override each other (`true`) or automagically convert them to an array (`false`).
                 */
                function Uri( uri, options ) {
+                       var prop,
+                               defaultUri = getDefaultUri();
+
                        options = typeof options === 'object' ? options : { strictMode: !!options };
                        options = $.extend( {
                                strictMode: false,
                                        this.parse( uri, options );
                                } else if ( typeof uri === 'object' ) {
                                        // Copy data over from existing URI object
-                                       for ( var prop in uri ) {
+                                       for ( prop in uri ) {
                                                // Only copy direct properties, not inherited ones
                                                if ( uri.hasOwnProperty( prop ) ) {
                                                        // Deep copy object properties
                        }
                };
 
-               defaultUri = new Uri( documentLocation );
-
                return Uri;
        };
 
        // Default to the current browsing location (for relative URLs).
-       mw.Uri = mw.UriRelative( location.href );
+       mw.Uri = mw.UriRelative( function () {
+               return location.href;
+       } );
 
 }( mediaWiki, jQuery ) );
index 6bcb87f..587f5b9 100644 (file)
                                { redirect: true }
                        )
                        .done( function ( result ) {
-                               if ( result.edit !== undefined ) {
-                                       if ( result.edit.result === 'Success' ) {
-                                               fb.displayThanks();
-                                       } else {
-                                               // unknown API result
-                                               fb.displayError( 'feedback-error1' );
-                                       }
+                               if ( result.edit.result === 'Success' ) {
+                                       fb.displayThanks();
                                } else {
-                                       // edit failed
-                                       fb.displayError( 'feedback-error2' );
+                                       // unknown API result
+                                       fb.displayError( 'feedback-error1' );
                                }
                        } )
-                       .fail( function () {
-                               // ajax request failed
-                               fb.displayError( 'feedback-error3' );
+                       .fail( function ( code, result ) {
+                               if ( code === 'http' ) {
+                                       // ajax request failed
+                                       fb.displayError( 'feedback-error3' );
+                                       mw.log.warn( 'Feedback report failed with HTTP error: ' +  result.textStatus );
+                               } else {
+                                       fb.displayError( 'feedback-error2' );
+                                       mw.log.warn( 'Feedback report failed with API error: ' +  code );
+                               }
                        } );
                },
 
index 9e11842..bb91663 100644 (file)
                        }
 
                        /**
-                        * Generates an ISO8601 "basic" string from a UNIX timestamp
+                        * Convert UNIX timestamp to ISO8601 format
+                        * @param {number} timestamp UNIX timestamp
                         * @private
                         */
                        function formatVersionNumber( timestamp ) {
                                addScript( sourceLoadScript + '?' + $.param( request ) + '&*', null, async );
                        }
 
+                       /**
+                        * Resolve indexed dependencies.
+                        *
+                        * ResourceLoader uses an optimization to save space which replaces module names in
+                        * dependency lists with the index of that module within the array of module
+                        * registration data if it exists. The benefit is a significant reduction in the data
+                        * size of the startup module. This function changes those dependency lists back to
+                        * arrays of strings.
+                        *
+                        * @param {Array} modules Modules array
+                        */
+                       function resolveIndexedDependencies( modules ) {
+                               var i, iLen, j, jLen, module, dependency;
+
+                               // Expand indexed dependency names
+                               for ( i = 0, iLen = modules.length; i < iLen; i++ ) {
+                                       module = modules[i];
+                                       if ( module[2] ) {
+                                               for ( j = 0, jLen = module[2].length; j < jLen; j++ ) {
+                                                       dependency = module[2][j];
+                                                       if ( typeof dependency === 'number' ) {
+                                                               module[2][j] = modules[dependency][0];
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
                        /* Public Members */
                        return {
                                /**
                                 * Register a module, letting the system know about it and its
                                 * properties. Startup modules contain calls to this function.
                                 *
-                                * @param {string} module Module name
+                                * When using multiple module registration by passing an array, dependencies that
+                                * are specified as references to modules within the array will be resolved before
+                                * the modules are registered.
+                                *
+                                * @param {string|Array} module Module name or array of arrays, each containing
+                                *   a list of arguments compatible with this method
                                 * @param {number} version Module version number as a timestamp (falls backs to 0)
                                 * @param {string|Array|Function} dependencies One string or array of strings of module
                                 *  names on which this module depends, or a function that returns that array.
                                 * @param {string} [skip=null] Script body of the skip function
                                 */
                                register: function ( module, version, dependencies, group, source, skip ) {
-                                       var m;
+                                       var i, len;
                                        // Allow multiple registration
                                        if ( typeof module === 'object' ) {
-                                               for ( m = 0; m < module.length; m += 1 ) {
+                                               resolveIndexedDependencies( module );
+                                               for ( i = 0, len = module.length; i < len; i++ ) {
                                                        // module is an array of module names
-                                                       if ( typeof module[m] === 'string' ) {
-                                                               mw.loader.register( module[m] );
+                                                       if ( typeof module[i] === 'string' ) {
+                                                               mw.loader.register( module[i] );
                                                        // module is an array of arrays
-                                                       } else if ( typeof module[m] === 'object' ) {
-                                                               mw.loader.register.apply( mw.loader, module[m] );
+                                                       } else if ( typeof module[i] === 'object' ) {
+                                                               mw.loader.register.apply( mw.loader, module[i] );
                                                        }
                                                }
                                                return;
                                /**
                                 * Get the version of a module.
                                 *
-                                * @param {string} module Name of module to get version for
+                                * @param {string} module Name of module
                                 * @return {string|null} The version, or null if the module (or its version) is not
                                 *  in the registry.
                                 */
                                getVersion: function ( module ) {
-                                       if ( registry[module] !== undefined && registry[module].version !== undefined ) {
-                                               return formatVersionNumber( registry[module].version );
+                                       if ( !registry[module] || registry[module].version === undefined ) {
+                                               return null;
                                        }
-                                       return null;
+                                       return formatVersionNumber( registry[module].version );
                                },
 
                                /**
                                 * Get the state of a module.
                                 *
-                                * @param {string} module Name of module to get state for
+                                * @param {string} module Name of module
+                                * @return {string|null} The state, or null if the module (or its version) is not
+                                *  in the registry.
                                 */
                                getState: function ( module ) {
-                                       if ( registry[module] !== undefined && registry[module].state !== undefined ) {
-                                               return registry[module].state;
+                                       if ( !registry[module] || registry[module].state === undefined ) {
+                                               return null;
                                        }
-                                       return null;
+                                       return registry[module].state;
                                },
 
                                /**
index d35ec26..4ed28a8 100644 (file)
@@ -48,11 +48,6 @@ $wgAutoloadClasses += array(
        'TestUser' => "$testDir/phpunit/includes/TestUser.php",
        'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
 
-       # tests/phpunit/includes
-       'BlockTest' => "$testDir/phpunit/includes/BlockTest.php",
-       'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
-       'WikiPageTest' => "$testDir/phpunit/includes/WikiPageTest.php",
-
        # tests/phpunit/includes/api
        'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
        'ApiQueryTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryTestBase.php",
index 39129cb..78e1387 100644 (file)
@@ -16,7 +16,7 @@
 # cat           add category links
 # ill           add inter-language links
 # subpage       enable subpages (disabled by default)
-# noxml         don't check for XML well formdness
+# noxml         don't check for XML well-formedness
 # title=[[XXX]] run test using article title XXX
 # language=XXX  set content language to XXX for this test
 # variant=XXX   set the variant of language for this test (eg zh-tw)
@@ -538,7 +538,6 @@ Italics and bold: 2-quote opening sequence: (2,2)
 </p>
 !!end
 
-
 !! test
 Italics and bold: 2-quote opening sequence: (2,3)
 !! options
@@ -550,18 +549,16 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 2-quote opening sequence: (2,3) w/ nowiki
 !! wikitext
-''<nowiki>foo'</nowiki>''
+''foo'<nowiki/>''
 !! html
 <p><i>foo'</i>
 </p>
 !! end
 
-
 !! test
 Italics and bold: 2-quote opening sequence: (2,4)
 !! options
@@ -573,18 +570,16 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 2-quote opening sequence: (2,4) w/ nowiki
 !! wikitext
-''<nowiki>foo''</nowiki>''
+''foo<nowiki>''</nowiki>''
 !! html
 <p><i>foo''</i>
 </p>
 !! end
 
-
 # The PHP parser strips the empty tags out for giggles; parsoid doesn't.
 !! test
 Italics and bold: 2-quote opening sequence: (2,5)
@@ -620,13 +615,24 @@ Italics and bold: 2-quote opening sequence: (2,5+3) w/ nowiki
 
 !! test
 Italics and bold: 3-quote opening sequence: (3,2)
+!! options
+parsoid=wt2html
 !! wikitext
 '''foo''
-!! html
+!! html/*
 <p>'<i>foo</i>
 </p>
 !!end
 
+# same html as previous, but wikitext adjusted to match parsoid html2wt
+!! test
+Italics and bold: 3-quote opening sequence: (3,2) w/ nowiki
+!! wikitext
+'<nowiki/>''foo''
+!! html
+<p>'<i>foo</i>
+</p>
+!!end
 
 !! test
 Italics and bold: 3-quote opening sequence: (3,3)
@@ -637,7 +643,6 @@ Italics and bold: 3-quote opening sequence: (3,3)
 </p>
 !!end
 
-
 !! test
 Italics and bold: 3-quote opening sequence: (3,4)
 !! options
@@ -649,18 +654,16 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 3-quote opening sequence: (3,4) w/ nowiki
 !! wikitext
-'''<nowiki>foo'</nowiki>'''
+'''foo'<nowiki/>'''
 !! html
 <p><b>foo'</b>
 </p>
 !! end
 
-
 # The PHP parser strips the empty tags out for giggles; parsoid doesn't.
 !! test
 Italics and bold: 3-quote opening sequence: (3,5)
@@ -705,7 +708,6 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 4-quote opening sequence: (4,2) w/ nowiki
@@ -716,16 +718,26 @@ Italics and bold: 4-quote opening sequence: (4,2) w/ nowiki
 </p>
 !! end
 
-
 !! test
 Italics and bold: 4-quote opening sequence: (4,3)
+!! options
+parsoid=wt2html
 !! wikitext
 ''''foo'''
-!! html
+!! html/*
 <p>'<b>foo</b>
 </p>
 !!end
 
+# same html as previous, but wikitext adjusted to match parsoid html2wt
+!! test
+Italics and bold: 4-quote opening sequence: (4,3) w/ nowiki
+!! wikitext
+'<nowiki/>'''foo'''
+!! html
+<p>'<b>foo</b>
+</p>
+!!end
 
 !! test
 Italics and bold: 4-quote opening sequence: (4,4)
@@ -738,18 +750,16 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 4-quote opening sequence: (4,4) w/ nowiki
 !! wikitext
-''''<nowiki>foo'</nowiki>'''
+'<nowiki/>'''foo'<nowiki/>'''
 !! html
 <p>'<b>foo'</b>
 </p>
 !! end
 
-
 # The PHP parser strips the empty tags out for giggles; parsoid doesn't.
 !! test
 Italics and bold: 4-quote opening sequence: (4,5)
@@ -769,7 +779,7 @@ parsoid=wt2html
 !! test
 Italics and bold: 4-quote opening sequence: (4,5+2) w/ nowiki
 !! wikitext
-''''foo'''''<nowiki/>''
+'<nowiki/>'''foo'''''<nowiki/>''
 !! html/php
 <p>'<b>foo</b>
 </p>
@@ -794,7 +804,6 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 # skipping wt2html and html2html because it wants to put <i> before <b>
 !! test
@@ -819,7 +828,6 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 5-quote opening sequence: (5,3+2)
@@ -830,7 +838,6 @@ Italics and bold: 5-quote opening sequence: (5,3+2)
 </p>
 !! end
 
-
 !! test
 Italics and bold: 5-quote opening sequence: (5,4)
 !! options
@@ -842,18 +849,16 @@ parsoid=wt2html
 </p>
 !!end
 
-
 # same html as previous, but wikitext adjusted to match parsoid html2wt
 !! test
 Italics and bold: 5-quote opening sequence: (5,4+2) w/ nowiki
 !! wikitext
-'''''<nowiki>foo'</nowiki>'''''
+'''''foo'<nowiki/>'''''
 !! html
 <p><i><b>foo'</b></i>
 </p>
 !! end
 
-
 !! test
 Italics and bold: 5-quote opening sequence: (5,5)
 !! wikitext
@@ -882,7 +887,7 @@ parsoid=wt2html
 !! test
 Italics and bold: multiple quote sequences: (2,4,2+3) w/ nowiki
 !! wikitext
-''<nowiki>foo'</nowiki>'''bar'''''
+''foo'<nowiki/>'''bar'''''
 !! html
 <p><i>foo'<b>bar</b></i>
 </p>
@@ -905,7 +910,7 @@ parsoid=wt2html
 !! test
 Italics and bold: multiple quote sequences: (2,4,3+2) w/ nowiki
 !! wikitext
-''<nowiki>foo'</nowiki>'''bar'''''
+''foo'<nowiki/>'''bar'''''
 !! html
 <p><i>foo'<b>bar</b></i>
 </p>
@@ -928,7 +933,7 @@ parsoid=wt2html
 !! test
 Italics and bold: multiple quote sequences: (2,4,4+2) w/ nowiki
 !! wikitext
-''<nowiki>foo'</nowiki>'''<nowiki>bar'</nowiki>'''''
+''foo'<nowiki/>'''bar'<nowiki/>'''''
 !! html
 <p><i>foo'<b>bar'</b></i>
 </p>
@@ -1030,14 +1035,11 @@ parsoid=wt2html
 
 
 # same html as previous, but wikitext adjusted to match parsoid html2wt
-# add 'parsoid' option to use 'parsoid' normalization of the placeholder
 !! test
 Italics and bold: other quote tests: (3,2,3+2+2,2)
-!! options
-parsoid
 !! wikitext
 '''this is about ''foo'''''<nowiki/>''s family''
-!! html/*
+!! html
 <p><b>this is about <i>foo</i></b><i>s family</i>
 </p>
 !! end
@@ -1046,8 +1048,20 @@ parsoid
 !! test
 Italics and bold: other quote tests: (3,2,3,3)
 !! options
+parsoid=wt2html
 !! wikitext
 '''this is about ''foo'''s family'''
+!! html/*
+<p>'<i>this is about </i>foo<b>s family</b>
+</p>
+!!end
+
+
+# same html as previous, but wikitext adjusted to match parsoid html2wt
+!! test
+Italics and bold: other quote tests: (3,2,3,3) w/ nowiki
+!! wikitext
+'<nowiki/>''this is about ''foo'''s family'''
 !! html
 <p>'<i>this is about </i>foo<b>s family</b>
 </p>
@@ -1776,7 +1790,7 @@ b</div>
 ## of eager output of buffered tokens in the p-wrapper. But, I'm going to ignore
 ## them for now.
 !! test
-P-wrapping should leave sol-transparent tags outside p-tags where possible
+1. P-wrapping should leave sol-transparent tags outside p-tags where possible
 !! options
 parsoid=wt2html
 !! wikitext
@@ -1788,6 +1802,16 @@ a [[Category:A1]] [[Category:A2]]
 <link href="Category:A1"/> <link href="Category:A2"/> <link href="Category:A3"/> <link href="Category:A4"/>
 !! end
 
+!! test
+2. P-wrapping should leave sol-transparent tags outside p-tags where possible
+!! options
+parsoid=wt2html
+!! wikitext
+[[Category:A1]]a
+!! html/parsoid
+<link href="Category:A1"/><p>a</p>
+!! end
+
 ###
 ### Preformatted text
 ###
@@ -2244,7 +2268,8 @@ parsoid=wt2html
 <table><pre></pre></table>
 
 !! html/parsoid
-<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;pre &lt;pre>x&lt;/pre>"}},"i":0}}]}'>&lt;pre </p><pre>x</pre>
+<pre about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"a":{"&lt;pre":null},"sa":{"&lt;pre":""},"stx":"html","pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;pre &lt;pre>x&lt;/pre>"}},"i":0}}]}'>x</pre>
+
 
 <p>&lt;pre </p>
 
@@ -2423,6 +2448,41 @@ Templates: Handle comments in the target
 <p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"foo"}},"i":0}}]}'>foo</p>
 !!end
 
+!! test
+Templates: Handle comments in parameter names (bug 67657)
+!! wikitext
+{{echo|1
+<!-- should be ignored -->
+=foo}}
+
+{{echo|
+<!-- should be ignored -->
+1 = foo}}
+
+{{echo|1<!-- should be ignored --> = foo}}
+
+{{echo|<!-- should be ignored -->1 = foo}}
+!!html/parsoid
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"foo","key":{"wt":"1\n&lt;!-- should be ignored -->"}}},"i":0}}]}'>foo</p>
+
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"foo","key":{"wt":"&lt;!-- should be ignored -->\n1"}}},"i":0}}]}'>foo</p>
+
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"foo","key":{"wt":"1&lt;!-- should be ignored -->"}}},"i":0}}]}'>foo</p>
+
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"foo","key":{"wt":"&lt;!-- should be ignored -->1"}}},"i":0}}]}'>foo</p>
+!!end
+
+!! test
+Templates: Other wikitext in parameter names (bug 67657)
+!! wikitext
+{{echo|''1''=foo}}
+!!html/parsoid
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"&#39;&#39;1&#39;&#39;":{"wt":"foo"}},"i":0}}]}'>{{{1}}}</p>
+!!html/php
+<p>{{{1}}}
+</p>
+!!end
+
 #--------------------------------------------------------------------
 # Transclusion parameter escaping tests
 #--------------------------------------------------------------------
@@ -4099,6 +4159,31 @@ External links: with no contents
 <p><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" title="wikipedia:Foo"><span>Bar</span></a></p>
 !! end
 
+!! test
+External links: Free with trailing punctuation
+!! wikitext
+http://example.com,
+http://example.com;
+http://example.com\
+http://example.com.
+http://example.com:
+http://example.com!
+http://example.com?
+http://example.com)
+http://example.com/url_with_(brackets)
+!! html
+<p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>,
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>;
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>\
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>.
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>:
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>!
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>?
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>)
+<a rel="nofollow" class="external free" href="http://example.com/url_with_(brackets)">http://example.com/url_with_(brackets)</a>
+</p>
+!! end
+
 !! test
 External image
 !! wikitext
@@ -5440,6 +5525,9 @@ Template-generated table cell attributes and cell content
 {|
 |{{table_attribs}}
 | {{table_attribs}}
+| <!--foo--> <!--bar--> <!--baz--> {{table_attribs}}
+|align=center {{table_attribs}}
+| <!--foo--> align=center <!--bar--> {{table_attribs}}
 |}
 !! html
 <table>
@@ -5447,26 +5535,18 @@ Template-generated table cell attributes and cell content
 <td style="color: red"> Foo
 </td>
 <td style="color: red"> Foo
-</td></tr></table>
-
-!! end
-
-!! test
-Template-generated table cell attributes and cell content (2)
-!! wikitext
-{|
-|align=center {{table_attribs}}
-|}
-!! html
-<table>
-<tr>
+</td>
+<td style="color: red"> Foo
+</td>
+<td align="center" style="color: red"> Foo
+</td>
 <td align="center" style="color: red"> Foo
 </td></tr></table>
 
 !! end
 
 !! test
-Template-generated table cell attributes and cell content (3)
+Template-generated table cell attributes and cell content (2)
 !! wikitext
 {|
 |align=center {{table_cells}}
@@ -5639,6 +5719,68 @@ Build table with {{!}}
 
 !! end
 
+!! test
+Build table with pipe as data
+!! wikitext
+{| class="wikitable"
+! header
+! second header
+|- style="color:red;"
+| data || style="color:red;" | second data
+|-
+| style="color:red;" | data with | || style="color:red;" | second data with |
+|-
+|| data with | ||| second data with |
+|}
+!! html
+<table class="wikitable">
+<tr>
+<th> header
+</th>
+<th> second header
+</th></tr>
+<tr style="color:red;">
+<td> data </td>
+<td style="color:red;"> second data
+</td></tr>
+<tr>
+<td style="color:red;"> data with | </td>
+<td style="color:red;"> second data with |
+</td></tr>
+<tr>
+<td> data with | </td>
+<td> second data with |
+</td></tr></table>
+
+!! end
+
+!! test
+Build table with wikilink
+!! wikitext
+{| class="wikitable"
+! header || second header
+|- style="color:red;"
+| data [[Main Page|linktext]] || second data [[Main Page|linktext]]
+|-
+| data || second data [[Main Page|link|text with pipe]]
+|}
+!! html
+<table class="wikitable">
+<tr>
+<th> header </th>
+<th> second header
+</th></tr>
+<tr style="color:red;">
+<td> data <a href="/wiki/Main_Page" title="Main Page">linktext</a> </td>
+<td> second data <a href="/wiki/Main_Page" title="Main Page">linktext</a>
+</td></tr>
+<tr>
+<td> data </td>
+<td> second data <a href="/wiki/Main_Page" title="Main Page">link|text with pipe</a>
+</td></tr></table>
+
+!! end
+
 # The expected HTML structure in this test is debatable. The PHP parser does
 # not parse this kind of table at all. The main focus for Parsoid is on
 # round-tripping, so this output is ok for now. TODO: revisit!
@@ -6310,7 +6452,7 @@ Link containing double-single-quotes '' in text embedded in italics (bug 4598 sa
 !! test
 Link with double quotes in title part (literal) and alternate part (interpreted)
 !! wikitext
-[[File:Denys Savchenko ''Pentecoste''.jpg]]
+[[File:Denys_Savchenko_''Pentecoste''.jpg]]
 
 [[''Pentecoste'']]
 
@@ -6324,7 +6466,7 @@ Link with double quotes in title part (literal) and alternate part (interpreted)
 </p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)"><i>Pentecoste</i></a>
 </p>
 !! html/parsoid
-<meta typeof="mw:Placeholder"/>
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}]}'><a href="./File:Denys_Savchenko_''Pentecoste''.jpg"><img resource="./File:Denys_Savchenko_''Pentecoste''.jpg" src="./Special:FilePath/Denys_Savchenko_''Pentecoste''.jpg" height="220" width="220"/></a></span></p>
 <p><a rel="mw:WikiLink" href="''Pentecoste''" title="''Pentecoste''">''Pentecoste''</a></p>
 <p><a rel="mw:WikiLink" href="''Pentecoste''" title="''Pentecoste''">Pentecoste</a></p>
 <p><a rel="mw:WikiLink" href="''Pentecoste''" title="''Pentecoste''"><i>Pentecoste</i></a></p>
@@ -6334,15 +6476,20 @@ Link with double quotes in title part (literal) and alternate part (interpreted)
 Broken image links with HTML captions (bug 39700)
 !! wikitext
 [[File:Nonexistent|<script></script>]]
-[[File:Nonexistent|100px|<script></script>]]
+[[File:Nonexistent|100x100px|<script></script>]]
 [[File:Nonexistent|&lt;]]
 [[File:Nonexistent|a<i>b</i>c]]
-!! html
+!! html/php
 <p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
 <a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
 <a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;</a>
 <a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">abc</a>
 </p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent"><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220"/></a></span>
+<span typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"&lt;script>&lt;/script>"}'><a href="./File:Nonexistent" data-parsoid='{"a":{"href":"./File:Nonexistent"},"sa":{}}'><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="100" width="100"/></a></span>
+<span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"&amp;lt;"}'><a href="./File:Nonexistent"><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220"/></a></span>
+<span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"a&lt;i>b&lt;/i>c"}'><a href="./File:Nonexistent"><img resource="./File:Nonexistent" src="./Special:FilePath/Nonexistent" height="220" width="220"/></a></span></p>
 !! end
 
 !! test
@@ -6916,6 +7063,8 @@ parsoid=wt2html,wt2wt,html2html
 
 !! test
 Interlanguage link
+!! options
+parsoid=wt2html,wt2wt,html2html
 !! wikitext
 Blah blah blah
 [[zh:Chinese]]
@@ -6944,6 +7093,8 @@ Blah blah blah
 
 !! test
 Double interlanguage link
+!! options
+parsoid=wt2html,wt2wt,html2html
 !! wikitext
 Blah blah blah
 [[es:Spanish]]
@@ -6959,17 +7110,23 @@ Blah blah blah
 
 !! test
 Interlanguage link variations
+!! options
+parsoid=wt2html,wt2wt,html2html
 !! wikitext
 Blah blah blah
 [[   es :Spanish]]
 [[ ZH :Chinese]]
+[[es:Foo_bar]]
+[[es:Foo bar]]
 !! html/php
 <p>Blah blah blah
 </p>
 !! html/parsoid
 <p>Blah blah blah</p>
-<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Spanish" data-parsoid='{"stx":"simple","a":{"href":"http://es.wikipedia.org/wiki/Spanish"},"sa":{"href":"   es :Spanish"}}'/>
-<link rel="mw:PageProp/Language" href="http://zh.wikipedia.org/wiki/Chinese" data-parsoid='{"stx":"simple","a":{"href":"http://zh.wikipedia.org/wiki/Chinese"},"sa":{"href":" ZH :Chinese"}}'/>
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Spanish" />
+<link rel="mw:PageProp/Language" href="http://zh.wikipedia.org/wiki/Chinese" />
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Foo_bar" />
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Foo_bar" />
 !! end
 
 !! test
@@ -7373,6 +7530,8 @@ Failing to transform badly formed HTML into correct XHTML
 </p>
 !!end
 
+## FIXME: Is Parsoid's acceptance of self-closing html-tags
+## a feature or a bug? See https://phabricator.wikimedia.org/T76962
 !! test
 Handling html with a div self-closing tag
 !! wikitext
@@ -7382,7 +7541,7 @@ Handling html with a div self-closing tag
 <div title=bar />
 <div title=bar/>
 <div title=bar/ >
-!! html
+!! html/php
 <p>&lt;div title /&gt;
 &lt;div title/&gt;
 </p>
@@ -7393,6 +7552,13 @@ Handling html with a div self-closing tag
 <div title="bar/"></div>
 </div>
 
+!! html/parsoid
+<div title="" data-parsoid='{"stx":"html","selfClose":true}'></div>
+<div title="" data-parsoid='{"stx":"html","selfClose":true}'></div>
+<div title="" data-parsoid='{"stx":"html","selfClose":true,"brokenHTMLTag":true}'></div>
+<div title="bar" data-parsoid='{"stx":"html","selfClose":true}'></div>
+<div title="bar" data-parsoid='{"stx":"html","selfClose":true}'></div>
+<div title="bar/" data-parsoid='{"stx":"html","autoInsertedEnd":true}'></div>
 !! end
 
 !! test
@@ -7415,7 +7581,7 @@ Handling html with a br self-closing tag
 !! html/parsoid
 <p><br title="" />
 <br title="" />
-<br />
+<br title="" />
 <br title="bar" />
 <br title="bar" />
 <br title="bar/" />
@@ -8963,8 +9129,7 @@ Template with complex template as argument
 !! test
 Template with thumb image (with link in description)
 !! wikitext
-{{paramtest|
-  param =[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]}}
+{{paramtest|param =[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]}}
 !! html/php
 This is a test template with parameter <div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=Special:Upload&amp;wpDestFile=Noimage.png" class="new" title="File:Noimage.png">File:Noimage.png</a>  <div class="thumbcaption"><a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">link</a> <a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">caption</a></div></div></div>
 
@@ -8975,6 +9140,8 @@ This is a test template with parameter <div class="thumb tright"><div class="thu
 <div class="thumbcaption"><a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">link</a> <a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">caption</a></div>
 </div>
 </div>
+!! html/parsoid
+<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"paramtest","href":"./Template:Paramtest"},"params":{"param":{"wt":"[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]"}},"i":0}}]}'>This is a test template with parameter </p><figure class="mw-default-size" typeof="mw:Error mw:Image/Thumb" about="#mwt1" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}]}'><a href="./File:Noimage.png" ><img resource="./File:Noimage.png" src="./Special:FilePath/Noimage.png" height="220" width="220"/></a><figcaption><a rel="mw:WikiLink" href="./No_link" title="No link">link</a> <a rel="mw:WikiLink" href="./No_link" title="No link">caption</a></figcaption></figure>
 !! end
 
 !! article
@@ -12067,11 +12234,13 @@ File:Barfoo.jpg
 #REDIRECT [[File:Barfoo.jpg]]
 !! endarticle
 
+# FIXME: Parsoid should run this test -- but we'd need to teach the
+# mockAPI about the redirected Barfoo.jpg image.
 !! test
 Redirected image
 !! wikitext
 [[Image:Barfoo.jpg]]
-!! html
+!! html/php
 <p><a href="/wiki/File:Barfoo.jpg" title="File:Barfoo.jpg">File:Barfoo.jpg</a>
 </p>
 !! end
@@ -12081,10 +12250,12 @@ Missing image with uploads disabled
 !! options
 wgEnableUploads=0
 !! wikitext
-[[Image:Foobaz.jpg]]
-!! html
+[[File:Foobaz.jpg]]
+!! html/php
 <p><a href="/wiki/File:Foobaz.jpg" title="File:Foobaz.jpg">File:Foobaz.jpg</a>
 </p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}]}'><a href="File:Foobaz.jpg"><img resource="./File:Foobaz.jpg" src="./Special:FilePath/Foobaz.jpg" height="220" width="220"/></a></span></p>
 !! end
 
 # Parsoid-specific testing for images
@@ -12323,7 +12494,12 @@ subpage title=[[Subpage test]]
 </p>
 !! end
 
-# TODO: make this PHP-parser compatible!
+!! article
+Subpage test/1/2/subpage
+!! text
+blah
+!! endarticle
+
 !! test
 Relative subpage noslash link
 !! options
@@ -12333,8 +12509,12 @@ subpage title=[[Subpage test/1/2/3/4]]
 [[../../subpage/]]
 
 [[../../subpage]]
-!! html
-<p><a rel="mw:WikiLink" href="Subpage_test/1/2/subpage/" title="Subpage test/1/2/subpage/">subpage</a></p>
+!! html/php
+<p><a href="/wiki/Subpage_test/1/2/subpage" title="Subpage test/1/2/subpage">subpage</a>
+</p><p><a href="/wiki/Subpage_test/1/2/subpage" title="Subpage test/1/2/subpage">Subpage test/1/2/subpage</a>
+</p>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="Subpage_test/1/2/subpage" title="Subpage test/1/2/subpage">subpage</a></p>
 <p><a rel="mw:WikiLink" href="Subpage_test/1/2/subpage" title="Subpage test/1/2/subpage">Subpage_test/1/2/subpage</a></p>
 !! end
 
@@ -13671,19 +13851,23 @@ Media link to nonexistent file (bug 1702)
 !! test
 Image link to nonexistent file (bug 1850 - good)
 !! wikitext
-[[Image:No such.jpg]]
-!! html
+[[File:No_such.jpg]]
+!! html/php
 <p><a href="/index.php?title=Special:Upload&amp;wpDestFile=No_such.jpg" class="new" title="File:No such.jpg">File:No such.jpg</a>
 </p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}]}'><a href="File:No_such.jpg"><img resource="./File:No_such.jpg" src="./Special:FilePath/No_such.jpg" height="220" width="220"/></a></span></p>
 !! end
 
 !! test
 :Image link to nonexistent file (bug 1850 - bad)
 !! wikitext
 [[:Image:No such.jpg]]
-!! html
+!! html/php
 <p><a href="/index.php?title=File:No_such.jpg&amp;action=edit&amp;redlink=1" class="new" title="File:No such.jpg (page does not exist)">Image:No such.jpg</a>
 </p>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="./File:No_such.jpg" title="File:No such.jpg">Image:No such.jpg</a></p>
 !! end
 
 
@@ -15042,7 +15226,7 @@ Fuzz testing: image with bogus manual thumbnail
 <div class="thumb tright"><div class="thumbinner" style="width:182px;">Error creating thumbnail:   <div class="thumbcaption"></div></div></div>
 
 !! html/parsoid
-<meta typeof="mw:Placeholder" data-parsoid='{"src":"[[Image:foobar.jpg|thumbnail= ]]","optList":[{"ck":"manualthumb","ak":"thumbnail= "}]}'/>
+<figure class="mw-default-size" typeof="mw:Error mw:Image/Thumb" data-parsoid='{"optList":[{"ck":"manualthumb","ak":"thumbnail= "}],"dsr":[0,32,2,2]}' data-mw='{"errors":[{"key":"missing-thumbnail","message":"This thumbnail does not exist.","params":{"name":""}}],"thumb":""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{},"dsr":[2,30,null,null]}'><img resource="./File:Foobar.jpg" src="./Special:FilePath/" height="220" width="220" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"220"},"sa":{"resource":"Image:foobar.jpg"}}'/></a></figure>
 !!end
 
 !! test
@@ -16625,9 +16809,11 @@ Image with page parameter
 djvu
 !! wikitext
 [[File:LoremIpsum.djvu|page=2]]
-!! html
+!! html/php
 <p><a href="/index.php?title=File:LoremIpsum.djvu&amp;page=2" class="image"><img alt="LoremIpsum.djvu" src="http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-2480px-LoremIpsum.djvu.jpg" width="2480" height="3508" srcset="http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-3720px-LoremIpsum.djvu.jpg 1.5x, http://example.com/images/thumb/5/5f/LoremIpsum.djvu/page2-4960px-LoremIpsum.djvu.jpg 2x" /></a>
 </p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"page","ak":"page=2"}]}'><a href="./File:LoremIpsum.djvu" data-parsoid='{"a":{"href":"./File:LoremIpsum.djvu"},"sa":{}}'><img resource="./File:LoremIpsum.djvu" src="//example.com/images/5/5f/LoremIpsum.djvu" height="3508" width="2480" data-parsoid='{"a":{"resource":"./File:LoremIpsum.djvu","height":"3508","width":"2480"},"sa":{"resource":"File:LoremIpsum.djvu"}}'/></a></span></p>
 !! end
 
 !! test
@@ -18156,30 +18342,34 @@ comment
 <a href="/index.php?title=ABC3D%25_%2B%2B&amp;action=edit&amp;redlink=1" class="new" title="ABC3D% ++ (page does not exist)">ABC3D% ++</a> <a href="/index.php?title=ABC3D%25_%2B%2B&amp;action=edit&amp;redlink=1" class="new" title="ABC3D% ++ (page does not exist)">+%20</a>
 !! end
 
-# FIXME: Omitting the php sections here because of differences in the local and
-# jenkins output. But, more importantly, the Bad.jpg isn't being stripped,
-# which seems to be a problem with the testing infrastructure.
+# Parsoid doesn't support this yet: see bug 73581
+# but it *should* omit the 'src' attribute if the image is bad.
+# PHP side of tests was disabled in
+# mediawiki/core:6bd31e7d95161a6e88fa86df60871051da997c3c
+# because of issues in the PHP parserTests infrastructure
+# (but the output below is indeed what the PHP side emits)
 !! test
 Bad images - basic functionality
 !! wikitext
 [[File:Bad.jpg]]
+!! DISABLED/html/php
 !! html/parsoid
-<meta typeof="mw:Placeholder" data-parsoid='{"src":"[[File:Bad.jpg]]","optList":[]}'/>
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"bad-image","message":"This image is blacklisted in this context."}]}'><a href="File:Bad.jpg"><img resource="./File:Bad.jpg" height="220" width="220"/></a></span></p>
 !! end
 
-# FIXME: Same reasoning as above. The expected php is:
-#  <p>Foo bar
-#  </p><p>Bar foo
-#  </p>
 !! test
 Bad images - bug 16039: text after bad image disappears
 !! wikitext
 Foo bar
 [[File:Bad.jpg]]
 Bar foo
+!! DISABLED/html/php
+<p>Foo bar
+</p><p>Bar foo
+</p>
 !! html/parsoid
 <p>Foo bar
-<meta typeof="mw:Placeholder" data-parsoid='{"src":"[[File:Bad.jpg]]","optList":[]}'/>
+<span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"bad-image","message":"This image is blacklisted in this context."}]}'><a href="File:Bad.jpg"><img resource="./File:Bad.jpg" height="220" width="220"/></a></span>
 Bar foo</p>
 !! end
 
@@ -18393,14 +18583,16 @@ percent-encoding and + signs in internal links (Bug 26410)
 !! wikitext
 [[User:+%]] [[Page+title%]]
 [[%+]] [[%+|%20]] [[%+ ]] [[%+r]]
-[[%]] [[+]] [[image:%+abc%39|foo|[[bar]]]]
+[[%]] [[+]] [[File:%+abc%39|foo|[[bar]]]]
 [[%33%45]] [[%33%45+]]
-!! html
+!! html/php
 <p><a href="/index.php?title=User:%2B%25&amp;action=edit&amp;redlink=1" class="new" title="User:+% (page does not exist)">User:+%</a> <a href="/index.php?title=Page%2Btitle%25&amp;action=edit&amp;redlink=1" class="new" title="Page+title% (page does not exist)">Page+title%</a>
 <a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%+</a> <a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%20</a> <a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%+ </a> <a href="/index.php?title=%25%2Br&amp;action=edit&amp;redlink=1" class="new" title="%+r (page does not exist)">%+r</a>
 <a href="/index.php?title=%25&amp;action=edit&amp;redlink=1" class="new" title="% (page does not exist)">%</a> <a href="/index.php?title=%2B&amp;action=edit&amp;redlink=1" class="new" title="+ (page does not exist)">+</a> <a href="/index.php?title=Special:Upload&amp;wpDestFile=%25%2Babc9" class="new" title="File:%+abc9">bar</a>
 <a href="/index.php?title=3E&amp;action=edit&amp;redlink=1" class="new" title="3E (page does not exist)">3E</a> <a href="/index.php?title=3E%2B&amp;action=edit&amp;redlink=1" class="new" title="3E+ (page does not exist)">3E+</a>
 </p>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="User:+%" title="User:+%">User:+%</a> <a rel="mw:WikiLink" href="Page+title%" title="Page+title%">Page+title%</a> <a rel="mw:WikiLink" href="%+" title="%+">%+</a> <a rel="mw:WikiLink" href="%+" title="%+">%20</a> <a rel="mw:WikiLink" href="%+" title="%+">%+ </a> <a rel="mw:WikiLink" href="%+r" title="%+r">%+r</a> <a rel="mw:WikiLink" href="%" title="%">%</a> <a rel="mw:WikiLink" href="+" title="+">+</a> <span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"[[bar]]"}'><a href="File:%+abc9"><img resource="./File:%25+abc9" src="./Special:FilePath/%+abc9" height="220" width="220"/></a></span> <a rel="mw:WikiLink" href="3E" title="3E">3E</a> <a rel="mw:WikiLink" href="3E+" title="3E+">3E+</a></p>
 !! end
 
 !! test
@@ -18408,13 +18600,15 @@ Special characters in embedded file links (bug 27679)
 !! wikitext
 [[File:Contains & ampersand.jpg]]
 [[File:Does not exist.jpg|Title with & ampersand]]
-!! html
+!! html/php
 <p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Contains_%26_ampersand.jpg" class="new" title="File:Contains &amp; ampersand.jpg">File:Contains &amp; ampersand.jpg</a>
 <a href="/index.php?title=Special:Upload&amp;wpDestFile=Does_not_exist.jpg" class="new" title="File:Does not exist.jpg">Title with &amp; ampersand</a>
 </p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}]}'><a href="File:Contains_&amp;_ampersand.jpg"><img resource="./File:Contains_&amp;_ampersand.jpg" src="./Special:FilePath/Contains_&amp;_ampersand.jpg" height="220" width="220"/></a></span>
+<span class="mw-default-size" typeof="mw:Error mw:Image" data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"Title with &amp; ampersand"}'><a href="File:Does_not_exist.jpg"><img resource="./File:Does_not_exist.jpg" src="./Special:FilePath/Does_not_exist.jpg" height="220" width="220"/></a></span></p>
 !! end
 
-
 !! test
 Confirm that 'apos' named character reference doesn't make it to output (not legal in HTML 4)
 !! wikitext
@@ -19399,6 +19593,22 @@ A <ref >foo</ref >
 <li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo</li></ol>
 !!end
 
+!!test
+Ref: 17. Generate valid HTML5 id/about attributes
+!!options
+parsoid
+!!wikitext
+<ref name="a b">foo</ref>
+
+<references />
+!!html
+<p><span class="reference" id="cite_ref-a_b-1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{"name":"a b"}}'><a href="#cite_note-a_b-1">[1]</a></span>
+</p>
+
+<ol class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'>
+<li id="cite_note-a_b-1"><span rel="mw:referencedBy"><a href="#cite_ref-a_b-1-0">↑</a></span> foo</li>
+!!end
+
 !!test
 References: 1. references tag without any refs should be handled properly
 !!options
@@ -19536,7 +19746,7 @@ parsoid
 !! wikitext
 <ref name="test &amp; me">hi</ref>
 !! html
-<p><span about="#mwt2" class="reference" id="cite_ref-test &amp; me-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref name=\"test &amp;amp; me\">hi&lt;/ref>"}' data-mw='{"name":"ref","body":{"html":"hi"},"attrs":{"name":"test &amp; me"}}'><a href="#cite_note-test &amp; me-1">[1]</a></span></p>
+<p><span about="#mwt2" class="reference" id="cite_ref-test_&amp;_me-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref name=\"test &amp;amp; me\">hi&lt;/ref>"}' data-mw='{"name":"ref","body":{"html":"hi"},"attrs":{"name":"test &amp; me"}}'><a href="#cite_note-test_&amp;_me-1">[1]</a></span></p>
 !! end
 
 # This test is wt2html only because we're permitting the serializer to produce
@@ -20550,24 +20760,24 @@ ISBN 1234567890's
 !! options
 parsoid=html2wt,wt2wt
 !! wikitext
-''<nowiki>'foo'</nowiki>''
+''<nowiki/>'foo'<nowiki/>''
 ''<nowiki>''foo''</nowiki>''
 ''<nowiki>'''foo'''</nowiki>''
 ''foo''<nowiki/>'s
-'''<nowiki>'foo'</nowiki>'''
+'''<nowiki/>'foo'<nowiki/>'''
 '''<nowiki>''foo''</nowiki>'''
 '''<nowiki>'''foo'''</nowiki>'''
-'''<nowiki>foo'</nowiki>''<nowiki>bar'</nowiki>''baz'''
+'''foo'<nowiki/>''bar'<nowiki/>''baz'''
 '''foo'''<nowiki/>'s
-'''foo''
+'<nowiki/>''foo''
 ''foo''<nowiki/>'
 '<nowiki/>''foo''<nowiki/>'
-''''foo'''
+'<nowiki/>'''foo'''
 '''foo'''<nowiki/>'
 '<nowiki/>'''foo'''<nowiki/>'
 ''fools'<span> errand</span>''
 ''<span>fool</span>'s errand''
-!! html
+!! html/*
 <p><i>'foo'</i>
 <i>''foo''</i>
 <i>'''foo'''</i>
@@ -20582,9 +20792,10 @@ parsoid=html2wt,wt2wt
 '<i>foo</i>'
 '<b>foo</b>
 <b>foo</b>'
-'<b>foo</b>'</p>
+'<b>foo</b>'
 <i>fools'<span> errand</span></i>
 <i><span>fool</span>'s errand</i>
+</p>
 !! end
 
 !! test
@@ -21221,15 +21432,12 @@ parsoid
 
 !!test
 Multi-line image caption generated by templates with/without trailing newlines
-!!options
-parsoid
 !! wikitext
-[[File:foo.jpg|thumb|300px|foo\n{{echo|A}}\n{{echo|B}}\n{{echo|C}}]]
-[[File:foo.jpg|thumb|300px|foo\n{{echo|A}}\n{{echo|B}}\n{{echo|C}}\n\n]]
-!! html
-<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=Special:Upload&amp;wpDestFile=Foo.jpg" class="new" title="File:Foo.jpg">File:Foo.jpg</a>  <div class="thumbcaption">foo\nA\nB\nC</div></div></div>
-<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=Special:Upload&amp;wpDestFile=Foo.jpg" class="new" title="File:Foo.jpg">File:Foo.jpg</a>  <div class="thumbcaption">foo\nA\nB\nC\n\n</div></div></div>
-
+[[File:Foobar.jpg|thumb|300x300px|foo\n{{echo|A}}\n{{echo|B}}\n{{echo|C}}]]
+[[File:Foobar.jpg|thumb|300x300px|foo\n{{echo|A}}\n{{echo|B}}\n{{echo|C}}\n\n]]
+!! html/parsoid
+<figure typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg" height="34" width="300"/></a><figcaption>foo\n<span about="#mwt9" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"A"}},"i":0}}]}'>A</span>\n<span about="#mwt10" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"B"}},"i":0}}]}'>B</span>\n<span about="#mwt11" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"C"}},"i":0}}]}'>C</span></figcaption></figure>
+<figure typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/300px-Foobar.jpg" height="34" width="300"/></a><figcaption>foo\n<span about="#mwt12" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"A"}},"i":0}}]}'>A</span>\n<span about="#mwt13" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"B"}},"i":0}}]}'>B</span>\n<span about="#mwt14" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"C"}},"i":0}}]}'>C</span>\n\n</figcaption></figure>
 !!end
 
 !! test
@@ -21533,6 +21741,17 @@ parsoid=html2wt
 <object data="test.swf"></object>
 !!end
 
+!! test
+Don't block XML namespace declaration
+!! wikitext
+<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">MediaWiki</span>
+!! html/php
+<p><span>MediaWiki</span>
+</p>
+!! html/parsoid
+<p><span xmlns:dct="http://purl.org/dc/terms/" data-x-property="dct:title" data-parsoid='{"stx":"html"}'>MediaWiki</span></p>
+!! end
+
 # -----------------------------------------------------------------
 # The following section of tests are primarily to spec requirements
 # around serialization of new/edited content.
@@ -21551,6 +21770,64 @@ parsoid=html2wt
 <p><a rel="mw:ExtLink" href="http://mi.wikipedia.org/wiki/Foo">Foo</a></p>
 !! end
 
+!! test
+New wiki links (href variations)
+!! options
+parsoid=html2wt
+!! html
+<a rel="mw:WikiLink" href="./Foo_bar">Foo_bar</a>
+<a rel="mw:WikiLink" href="Foo_bar">Foo_bar</a>
+<a rel="mw:WikiLink" href="Foo bar">Foo_bar</a>
+<a rel="mw:WikiLink" href="./Toxine_bact%C3%A9rienne">Toxine bactérienne</a>
+!! wikitext
+[[Foo_bar]]
+[[Foo_bar]]
+[[Foo_bar]]
+[[Toxine bactérienne]]
+!! end
+
+!! test
+New wiki links (content string variations)
+!! options
+parsoid=html2wt
+!! html
+<a rel="mw:WikiLink" href="./Foo_bar">Foo_bar</a>
+<a rel="mw:WikiLink" href="./Foo_bar">Foo bar</a>
+<a rel="mw:WikiLink" href="./Foo_bar">./Foo_bar</a>
+!! wikitext
+[[Foo_bar]]
+[[Foo bar]]
+[[Foo_bar|./Foo_bar]]
+!! end
+
+!! test
+New category links (href variations)
+!! options
+parsoid=html2wt
+!! html
+<link rel="mw:PageProp/Category" href="./Category:Toxine_bactérienne" />
+<link rel="mw:PageProp/Category" href="./Category:Toxine_bact%C3%A9rienne" />
+<link rel="mw:PageProp/Category" href="Category:Toxine_bact%C3%A9rienne" />
+!! wikitext
+[[Category:Toxine bactérienne]]
+[[Category:Toxine bactérienne]]
+[[Category:Toxine bactérienne]]
+!! end
+
+!! test
+New interlanguage links (href variations)
+!! options
+parsoid=html2wt
+!! html
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Toxine bactérienne" />
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Toxine_bactérienne" />
+<link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Toxine_bact%C3%A9rienne" />
+!! wikitext
+[[es:Toxine bactérienne]]
+[[es:Toxine_bactérienne]]
+[[es:Toxine_bactérienne]]
+!! end
+
 !! test
 Image: Modifying size of an image (1)
 !! options
@@ -21693,33 +21970,31 @@ parsoid
 #!! options
 #parsoid=html2wt
 #language=ar
-#!! input
+#!! wikitext
 #[[Imagen:Foobar.jpg|derecha|miniaturadeimagen]]
-#!! result
+#!! html
 #<figure class="mw-default-size mw-halign-right" typeof="mw:Image/Thumb"><a href="Imagen:Foobar.jpg"><img resource="./Imagen:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" height="20" width="180"/></a></figure>
 #!! end
 
 !! test
 Image: Block level image should have \n before and after
-!! options
-parsoid
 !! wikitext
 123
 [[File:Foobar.jpg|right|thumb|150x150px]]
 456
-!! html
-<p>123</p><figure typeof="mw:Image/Thumb" class="mw-halign-right"><a href="./File:Foobar.png"><img src="http://192.168.142.128/mw/images/thumb/b/bc/Foobar.png/131px-Foobar.png" width="131" height="150" resource="./File:Foobar.png" data-parsoid='{"a":{"resource":"./File:Foobar.png","width":"131"},"sa":{"resource":"File:Foobar.png","width":"150"}}'></a></figure><p>456</p>
+!! html/parsoid
+<p>123</p>
+<figure class="mw-halign-right" typeof="mw:Image/Thumb"><a href="File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" height="17" width="150"/></a></figure>
+<p>456</p>
 !!end
 
 !! test
 Image: New block level image should have \n before and after (existing content)
-!! options
-parsoid
 !! wikitext
 123
 [[File:Foobar.jpg|right|thumb|150x150px]]
 456
-!! html
+!! html/parsoid
 <p>123</p>
 <figure class="mw-halign-right" typeof="mw:Image/Thumb" data-parsoid='{"optList":[{"ck":"right","ak":"right"},{"ck":"thumbnail","ak":"thumb"},{"ck":"width","ak":"150x150px"}]}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"}}'><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/150px-Foobar.jpg" height="17" width="150" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"17","width":"150"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></figure>
 <p>456</p>
@@ -22025,7 +22300,7 @@ parsoid=html2wt
 !! wikitext
 ''A''<i>B</i>
 
-''A'''''<i>B</i>'''
+''A''<nowiki/>'''<i>B</i>'''
 !! html
 <p><i>A</i><i data-parsoid='{"stx":"html"}'>B</i></p>
 <p><i>A</i><b><i data-parsoid='{"stx":"html"}'>B</i></b></p>
@@ -22176,6 +22451,16 @@ parsoid=html2wt
 <link rel="mw:PageProp/redirect" href="Bar" data-parsoid='{"src":"#REDIRECT ","a":{"href":"./Foo"},"sa":{"href":"Foo"}}'>
 !! end
 
+!! test
+T75121: Infer extension name from typeOf if data-mw is not present
+!! options
+parsoid=html2wt
+!! wikitext
+<foo />
+!! html
+<div typeOf="mw:Extension/foo"></div>
+!! end
+
 # -----------------------------------------------------------------
 # End of section for Parsoid-only html2wt tests for serialization
 # of new content
index d0f6208..e737056 100644 (file)
@@ -42,12 +42,12 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
                $this->insertPage( 'Example Foo/Bar' );
                $this->insertPage( 'Example/Baz' );
                $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
-               $this->insertPage( 'Redirect Test');
-               $this->insertPage( 'Redirect Test Worse Result');
+               $this->insertPage( 'Redirect Test' );
+               $this->insertPage( 'Redirect Test Worse Result' );
                $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
                $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
-               $this->insertPage( 'Redirect Test2');
-               $this->insertPage( 'Redirect Test2 Worse Result');
+               $this->insertPage( 'Redirect Test2' );
+               $this->insertPage( 'Redirect Test2 Worse Result' );
 
                $this->insertPage( 'Talk:Sandbox' );
                $this->insertPage( 'Talk:Example' );
@@ -171,7 +171,7 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
                array_shift( $case['results'] );
                // And sometimes we expect a different last result
                $expected = isset( $case['offsetresult'] ) ?
-                       array_merge( $case['results'], $case['offsetresult'] ):
+                       array_merge( $case['results'], $case['offsetresult'] ) :
                        $case['results'];
 
                $this->assertEquals(
index 4bf6deb..51d03ed 100644 (file)
@@ -61,4 +61,22 @@ class ApiMainTest extends ApiTestCase {
                }
        }
 
+       /**
+        * Test if all classes in the main module manager exists
+        */
+       public function testClassNamesInModuleManager() {
+               global $wgAutoloadLocalClasses;
+
+               $api = new ApiMain(
+                       new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) )
+               );
+               $modules = $api->getModuleManager()->getNamesWithClasses();
+               foreach( $modules as $name => $class ) {
+                       $this->assertArrayHasKey(
+                               $class,
+                               $wgAutoloadLocalClasses,
+                               'Class ' . $class . ' for api module ' . $name . ' not in autoloader (with exact case)'
+                       );
+               }
+       }
 }
index 200027d..3ab1334 100644 (file)
@@ -116,4 +116,24 @@ class ApiQueryTest extends ApiTestCase {
                        array( 'apiquerytestiw:foo', NS_MAIN, null, true ),
                );
        }
+
+       /**
+        * Test if all classes in the query module manager exists
+        */
+       public function testClassNamesInModuleManager() {
+               global $wgAutoloadLocalClasses;
+
+               $api = new ApiMain(
+                       new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) )
+               );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $modules = $queryApi->getModuleManager()->getNamesWithClasses();
+               foreach( $modules as $name => $class ) {
+                       $this->assertArrayHasKey(
+                               $class,
+                               $wgAutoloadLocalClasses,
+                               'Class ' . $class . ' for api module ' . $name . ' not in autoloader (with exact case)'
+                       );
+               }
+       }
 }
index 24b5186..a0d308a 100644 (file)
@@ -7,8 +7,6 @@
  */
 class LocalisationCacheTest extends MediaWikiTestCase {
        protected function setUp() {
-               global $IP;
-
                parent::setUp();
                $this->setMwGlobals( array(
                        'wgExtensionMessagesFiles' => array(),
index 588e544..d32c1a6 100644 (file)
@@ -297,7 +297,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                        $out = $wgProfiler['output'];
                        if ( $out === 'db' ) {
                                $profileToDb = true;
-                       } elseif( is_array( $out ) && in_array( 'db', $out ) ) {
+                       } elseif ( is_array( $out ) && in_array( 'db', $out ) ) {
                                $profileToDb = true;
                        }
                }
index 97aa0a3..54758f9 100644 (file)
@@ -90,14 +90,14 @@ class FormatMetadataTest extends MediaWikiMediaTestCase {
                        'multiValue' => array( array( 'first', 'second', 'third', '_type' => 'ol' ), 'first' ),
                        'noType' => array( array( 'first', 'second', 'third' ), 'first' ),
                        'typeFirst' => array( array( '_type' => 'ol', 'first', 'second', 'third' ), 'first' ),
-                   'multilang' => array(
-                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
-                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
-                   ),
-                   'multilang-multivalue' => array(
-                           array( 'en' => array( 'first', 'second' ), 'de' => array( 'Erste', 'Zweite' ), '_type' => 'lang' ),
-                           array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
-                   ),
+                       'multilang' => array(
+                               array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                               array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                       ),
+                       'multilang-multivalue' => array(
+                               array( 'en' => array( 'first', 'second' ), 'de' => array( 'Erste', 'Zweite' ), '_type' => 'lang' ),
+                               array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ),
+                       ),
                );
        }
 }
index 1c25211..3fddc1e 100644 (file)
@@ -23,7 +23,7 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400"
+        1388534400
     ]
 ] );',
                        ) ),
@@ -40,17 +40,17 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400"
+        1388534400
     ],
     [
         "test.group.foo",
-        "1388534400",
+        1388534400,
         [],
         "x-foo"
     ],
     [
         "test.group.bar",
-        "1388534400",
+        1388534400,
         [],
         "x-bar"
     ]
@@ -68,7 +68,7 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400"
+        1388534400
     ]
 ] );'
                        ) ),
@@ -90,7 +90,7 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400",
+        1388534400,
         [],
         null,
         "example"
@@ -115,8 +115,8 @@ mw.loader.addSource( {
                                        'test.z.foo' => new ResourceLoaderTestModule( array(
                                                'dependencies' => array(
                                                        'test.x.core',
-                                                       'test.x.polyfil',
-                                                       'test.y.polyfil',
+                                                       'test.x.polyfill',
+                                                       'test.y.polyfill',
                                                ),
                                        ) ),
                                ),
@@ -126,31 +126,31 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.x.core",
-        "1388534400"
+        1388534400
     ],
     [
         "test.x.polyfill",
-        "1388534400",
+        1388534400,
         [],
         null,
-        "local",
+        null,
         "return true;"
     ],
     [
         "test.y.polyfill",
-        "1388534400",
+        1388534400,
         [],
         null,
-        "local",
+        null,
         "return !!(    window.JSON \u0026\u0026    JSON.parse \u0026\u0026    JSON.stringify);"
     ],
     [
         "test.z.foo",
-        "1388534400",
+        1388534400,
         [
-            "test.x.core",
-            "test.x.polyfil",
-            "test.y.polyfil"
+            0,
+            1,
+            2
         ]
     ]
 ] );',
@@ -222,63 +222,63 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400"
+        1388534400
     ],
     [
         "test.x.core",
-        "1388534400"
+        1388534400
     ],
     [
         "test.x.util",
-        "1388534400",
+        1388534400,
         [
-            "test.x.core"
+            1
         ]
     ],
     [
         "test.x.foo",
-        "1388534400",
+        1388534400,
         [
-            "test.x.core"
+            1
         ]
     ],
     [
         "test.x.bar",
-        "1388534400",
+        1388534400,
         [
-            "test.x.util"
+            2
         ]
     ],
     [
         "test.x.quux",
-        "1388534400",
+        1388534400,
         [
-            "test.x.foo",
-            "test.x.bar",
+            3,
+            4,
             "test.x.unknown"
         ]
     ],
     [
         "test.group.foo.1",
-        "1388534400",
+        1388534400,
         [],
         "x-foo"
     ],
     [
         "test.group.foo.2",
-        "1388534400",
+        1388534400,
         [],
         "x-foo"
     ],
     [
         "test.group.bar.1",
-        "1388534400",
+        1388534400,
         [],
         "x-bar"
     ],
     [
         "test.group.bar.2",
-        "1388534400",
+        1388534400,
         [],
         "x-bar",
         "example"
@@ -344,8 +344,8 @@ mw.loader.addSource( {
                $this->assertEquals(
 'mw.loader.addSource({"local":"/w/load.php"});'
 . 'mw.loader.register(['
-. '["test.blank","1388534400"],'
-. '["test.min","1388534400",["test.blank"],null,"local",'
+. '["test.blank",1388534400],'
+. '["test.min",1388534400,[0],null,null,'
 . '"return!!(window.JSON\u0026\u0026JSON.parse\u0026\u0026JSON.stringify);"'
 . ']]);',
                        $module->getModuleRegistrations( $context ),
@@ -367,16 +367,16 @@ mw.loader.addSource( {
 } );mw.loader.register( [
     [
         "test.blank",
-        "1388534400"
+        1388534400
     ],
     [
         "test.min",
-        "1388534400",
+        1388534400,
         [
-            "test.blank"
+            0
         ],
         null,
-        "local",
+        null,
         "return !!(    window.JSON \u0026\u0026    JSON.parse \u0026\u0026    JSON.stringify);"
     ]
 ] );',
index 26662d5..6ef2e23 100644 (file)
@@ -608,4 +608,4 @@ class BackupTextPassTestModelHandler extends TextContentHandler {
                return strtoupper( $text );
        }
 
-}
\ No newline at end of file
+}
index c6dd91c..ee0f060 100644 (file)
                                        rtl: true
                                }
                        },
+                       // Internet Explorer 12
+                       'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0': {
+                               title: 'Internet Explorer 12',
+                               platform: 'WOW64',
+                               profile: {
+                                       name: 'msie',
+                                       layout: 'edge',
+                                       layoutVersion: 12,
+                                       platform: 'win',
+                                       version: '12.0',
+                                       versionBase: '12',
+                                       versionNumber: 12
+                               },
+                               wikiEditor: {
+                                       ltr: true,
+                                       rtl: true
+                               }
+                       },
                        // Firefox 2
                        // Firefox 3.5
                        'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': {
index 7571b92..795c2bb 100644 (file)
                assert.equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
        } );
 
-       QUnit.test( 'Is functions', 15, function ( assert ) {
-               assert.strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
-                       'isDomElement: #qunit-header Node' );
-               assert.strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
-                       'isDomElement: #random-name (null)' );
+       QUnit.test( 'isDomElement', 6, function ( assert ) {
+               assert.strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
+                       'isDomElement: HTMLElement' );
+               assert.strictEqual( $.isDomElement( document.createTextNode( '' ) ), true,
+                       'isDomElement: TextNode' );
+               assert.strictEqual( $.isDomElement( null ), false,
+                       'isDomElement: null' );
                assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
-                       'isDomElement: getElementsByTagName Array' );
-               assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
-                       'isDomElement: getElementsByTagName(..)[0] Node' );
+                       'isDomElement: NodeList' );
                assert.strictEqual( $.isDomElement( $( 'div' ) ), false,
-                       'isDomElement: jQuery object' );
-               assert.strictEqual( $.isDomElement( $( 'div' ).get( 0 ) ), true,
-                       'isDomElement: jQuery object > Get node' );
-               assert.strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
-                       'isDomElement: createElement' );
+                       'isDomElement: jQuery' );
                assert.strictEqual( $.isDomElement( { foo: 1 } ), false,
-                       'isDomElement: Object' );
+                       'isDomElement: Plain Object' );
+       } );
 
+       QUnit.test( 'isEmpty', 7, function ( assert ) {
                assert.strictEqual( $.isEmpty( 'string' ), false, 'isEmpty: "string"' );
                assert.strictEqual( $.isEmpty( '0' ), true, 'isEmpty: "0"' );
                assert.strictEqual( $.isEmpty( '' ), true, 'isEmpty: ""' );
index 7a58d38..ba36655 100644 (file)
                assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
        } );
 
+       QUnit.test( 'Variable defaultUri', 2, function ( assert ) {
+               var uri,
+                       href = 'http://example.org/w/index.php#here',
+                       UriClass = mw.UriRelative( function () {
+                               return href;
+                       } );
+
+               uri = new UriClass();
+               assert.deepEqual(
+                       {
+                               protocol: uri.protocol,
+                               user: uri.user,
+                               password: uri.password,
+                               host: uri.host,
+                               port: uri.port,
+                               path: uri.path,
+                               query: uri.query,
+                               fragment: uri.fragment
+                       },
+                       {
+                               protocol: 'http',
+                               user: undefined,
+                               password: undefined,
+                               host: 'example.org',
+                               port: undefined,
+                               path: '/w/index.php',
+                               query: {},
+                               fragment: 'here'
+                       },
+                       'basic object properties'
+               );
+
+               // Default URI may change, e.g. via history.replaceState, pushState or location.hash (T74334)
+               href = 'https://example.com/wiki/Foo?v=2';
+               uri = new UriClass();
+               assert.deepEqual(
+                       {
+                               protocol: uri.protocol,
+                               user: uri.user,
+                               password: uri.password,
+                               host: uri.host,
+                               port: uri.port,
+                               path: uri.path,
+                               query: uri.query,
+                               fragment: uri.fragment
+                       },
+                       {
+                               protocol: 'https',
+                               user: undefined,
+                               password: undefined,
+                               host: 'example.com',
+                               port: undefined,
+                               path: '/wiki/Foo',
+                               query: { 'v': '2' },
+                               fragment: undefined
+                       },
+                       'basic object properties'
+               );
+       } );
+
        QUnit.test( 'Advanced URL', 11, function ( assert ) {
                var uri, queryString, relativePath;
 
                uri = new UriClass( testPath );
                href = uri.toString();
                assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
-
        } );
 }( mediaWiki, jQuery ) );