Merge "Add Unicode to ICU mappings for versions 51-57"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 21 Jul 2016 05:23:22 +0000 (05:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 21 Jul 2016 05:23:22 +0000 (05:23 +0000)
44 files changed:
RELEASE-NOTES-1.28
composer.json
docs/distributors.txt
docs/hooks.txt
includes/Html.php
includes/Title.php
includes/WatchedItemStore.php
includes/api/ApiLogin.php
includes/api/ApiPurge.php
includes/collation/IcuCollation.php
includes/db/Database.php
includes/db/IDatabase.php
includes/deferred/LinksDeletionUpdate.php
includes/deferred/SiteStatsUpdate.php
includes/installer/i18n/ko.json
includes/installer/i18n/wuu.json
includes/linker/LinkRenderer.php
includes/media/MediaTransformOutput.php
includes/profiler/ProfilerStub.php
includes/resourceloader/ResourceLoader.php
includes/revisiondelete/RevDelArchivedFileItem.php
includes/revisiondelete/RevDelFileItem.php
includes/specials/SpecialCreateAccount.php
includes/specials/SpecialUserLogin.php
includes/specials/pre-authmanager/SpecialUserlogin.php
includes/tidy/Balancer.php
languages/i18n/aeb-arab.json
languages/i18n/ar.json
languages/i18n/fr.json
languages/i18n/ia.json
languages/i18n/ja.json
languages/i18n/ko.json
languages/i18n/lij.json
languages/i18n/nn.json
languages/i18n/ru.json
languages/i18n/sr-ec.json
languages/i18n/wuu.json
languages/i18n/zh-hant.json
tests/parser/parserTests.txt
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/WatchedItemStoreUnitTest.php
tests/phpunit/includes/libs/XmlTypeCheckTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/tidy/BalancerTest.php

index 8b00d2e..429c5fe 100644 (file)
@@ -68,6 +68,9 @@ changes to languages because of Phabricator reports.
 * Deprecated APIEditBeforeSave hook in favor of EditFilterMergedContent.
 * The 'UploadVerification' hook is deprecated. Use 'UploadVerifyFile' instead.
 * SiteConfiguration::isLocalVHost() was removed (deprecated since 1.25).
+* The 'UserLoginComplete' hook has a new parameter to differentiate between actual
+  login and visiting the login page while already logged in.
+* ResourceLoader::makeLoaderURL() was removed (deprecated since 1.24).
 
 == Compatibility ==
 
index d5bf93c..54eb3c0 100644 (file)
@@ -40,7 +40,7 @@
                "wikimedia/relpath": "1.0.3",
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/utfnormal": "1.0.3",
-               "wikimedia/wrappedstring": "2.1.1",
+               "wikimedia/wrappedstring": "2.2.0",
                "zordius/lightncandy": "0.23"
        },
        "require-dev": {
index efa573d..f19574c 100644 (file)
@@ -1,23 +1,23 @@
 This document is intended to provide useful advice for parties seeking to
-redistribute MediaWiki to end users.  It's targeted particularly at maintainers
+redistribute MediaWiki to end users. It's targeted particularly at maintainers
 for Linux distributions, since it's been observed that distribution packages of
-MediaWiki often break.  We've consistently had to recommend that users seeking
+MediaWiki often break. We've consistently had to recommend that users seeking
 support use official tarballs instead of their distribution's packages, and
-this often solves whatever problem the user is having.  It would be nice if
+this often solves whatever problem the user is having. It would be nice if
 this could change.
 
 == Background: why web applications are different ==
 
 MediaWiki is intended to be usable on any web host that provides support for
-PHP and a database.  Many users of low-end shared hosting have very limited
+PHP and a database. Many users of low-end shared hosting have very limited
 access to their machine: often only FTP access to some subdirectory of the web
-root.  Support for these users entails several restrictions, such as:
+root. Support for these users entails several restrictions, such as:
 
-  1) We cannot require installation of any files outside the web root.  Few of
+  1) We cannot require installation of any files outside the web root. Few of
   our users have access to directories like /usr or /etc.
   2) We cannot require the ability to run any utility on the command line.
   Many shared hosts have exec() and similar PHP functions disabled.
-  3) We cannot assume that the software has write access anywhere useful.  The
+  3) We cannot assume that the software has write access anywhere useful. The
   user account that MediaWiki (including its installer) runs under is often
   different from the account the user used to upload the files, and we might be
   restricted by PHP settings such as safe mode or open_basedir.
@@ -30,28 +30,28 @@ root.  Support for these users entails several restrictions, such as:
 
 Since anything that works on cheap shared hosting will work if you have shell
 or root access too, MediaWiki's design is based around catering to the lowest
-common denominator.  Although we support higher-end setups as well (like
+common denominator. Although we support higher-end setups as well (like
 Wikipedia!), the way many things work by default is tailored toward shared
-hosting.  These defaults are unconventional from the point of view of normal
+hosting. These defaults are unconventional from the point of view of normal
 (non-web) applications -- they might conflict with distributors' policies, and
 they certainly aren't ideal for someone who's installing MediaWiki as root.
 
 == Directory structure ==
 
 Because of constraint (1) above, MediaWiki does not conform to normal
-Unix filesystem layout.  Hopefully we'll offer direct support for standard
+Unix filesystem layout. Hopefully we'll offer direct support for standard
 layouts in the future, but for now *any change to the location of files is
-unsupported*.  Moving things and leaving symlinks will *probably* not break
+unsupported*. Moving things and leaving symlinks will *probably* not break
 anything, but it is *strongly* advised not to try any more intrusive changes to
-get MediaWiki to conform more closely to your filesystem hierarchy.  Any such
+get MediaWiki to conform more closely to your filesystem hierarchy. Any such
 attempt will almost certainly result in unnecessary bugs.
 
 The standard recommended location to install MediaWiki, relative to the web
-root, is /w (so, e.g., /var/www/w).  Rewrite rules can then be used to enable
-"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article.  (This
+root, is /w (so, e.g., /var/www/w). Rewrite rules can then be used to enable
+"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article. (This
 is the convention Wikipedia uses.)  In theory, it should be possible to enable
 the appropriate rewrite rules by default, if you can reconfigure the web
-server, but you'd need to alter LocalSettings.php too.  See
+server, but you'd need to alter LocalSettings.php too. See
 <https://www.mediawiki.org/wiki/Manual:Short_URL> for details on short URLs.
 
 If you really must mess around with the directory structure, note that the
@@ -59,37 +59,38 @@ following files *must* all be web-accessible for MediaWiki to function
 correctly:
 
   * api.php, img_auth.php, index.php, load.php, opensearch_desc.php, thumb.php,
-  profileinfo.php, redirect.php, trackback.php.  These are the entry points for
-  normal usage.  This list may be incomplete and is subject to change.
+  profileinfo.php. These are the entry points for normal usage. This list may be
+  incomplete and is subject to change.
   * mw-config/index.php: Used for web-based installation (sets up the database,
   prompts for the name of the wiki, etc.).
-  * images/: Used for uploaded files.  This could be somewhere else if
+  * images/: Used for uploaded files. This could be somewhere else if
   $wgUploadDirectory and $wgUploadPath are changed appropriately.
   * skins/*/: Subdirectories of skins/ contain CSS and JavaScript files that
-  must be accessible to web browsers.  The PHP files and Skin.sample in skins/
-  don't need to be accessible.  This could be somewhere else if
+  must be accessible to web browsers. The PHP files and Skin.sample in skins/
+  don't need to be accessible. This could be somewhere else if
   $wgStyleDirectory and $wgStylePath are changed appropriately.
   * extensions/: Many extensions include CSS and JavaScript files in their
-  extensions directory, and will break if they aren't web-accessible.  Some
+  extensions directory, and will break if they aren't web-accessible. Some
   extensions might theoretically provide additional entry points as well, at
   least in principle.
 
 But all files should keep their position relative to the web-visible
-installation directory no matter what.  If you must move includes/ somewhere in
-/usr/share, provide a symlink from /var/www/w.  If you don't, you *will* break
-something.  You have been warned.
+installation directory no matter what. If you must move includes/ somewhere in
+/usr/share, provide a symlink from /var/www/w. If you don't, you *will* break
+something. You have been warned.
 
 == Configuration ==
 
-MediaWiki is configured using LocalSettings.php.  This is a PHP file that's
+MediaWiki is configured using LocalSettings.php. This is a PHP file that's
 generated when the user visits mw-config/index.php to install the software, and
-which the user can edit by hand thereafter.  It's just a plain old PHP file,
-and can contain any PHP statements.  It usually sets global variables that are
+which the user can edit by hand thereafter. It's just a plain old PHP file,
+and can contain any PHP statements. It usually sets global variables that are
 used for configuration, and includes files used by any extensions.
 
-Distributors can easily add extra statements to the autogenerated
-LocalSettings.php by changing mw-config/overrides.php (see that file for details
-and examples).
+Distributors can easily change the installer behavior, including LocalSettings
+generated, by placing their overrides into mw-config/overrides directory. Doing
+that is highly preferred to modifying MediaWiki code directly. See
+mw-config/overrides/README for more details and examples.
 
 There's a new maintenance/install.php script which could be used for performing
 an install through the command line.
@@ -98,7 +99,7 @@ Some configuration options that distributors might be in a position to set
 intelligently:
 
   * $wgEmergencyContact: An e-mail address that can be used to contact the wiki
-  administrator.  By default, "wikiadmin@ServerName".
+  administrator. By default, "wikiadmin@ServerName".
   * $wgPasswordSender: The e-mail address to use when sending password e-mails.
   By default, "MediaWiki Mail <apache@ServerName>".
        (with ServerName guessed from the http request)
@@ -115,16 +116,16 @@ Any package manager which replaces the files but doesn't update the db is leavin
 an inconsistent wiki that may produce blank pages (php errors) when new features 
 using the changed schema would be used.
 
-Since MediaWiki 1.17 it is possible to upgrade using the installer by providing 
+Since MediaWiki 1.17 it is possible to upgrade using the web installer by providing
 an arbitrary secret value stored as $wgUpgradeKey in LocalSettings (older versions 
 needed to rename LocalSettings.php in order to upgrade using the installer).
 
 == Documentation ==
 
 MediaWiki's official documentation is split between two places: the source
-code, and <https://www.mediawiki.org/>.  The source code documentation is written
+code, and <https://www.mediawiki.org/>. The source code documentation is written
 exclusively by developers, and so is likely to be reliable (at worst,
-outdated).  However, it can be pretty sparse.  mediawiki.org documentation is
+outdated). However, it can be pretty sparse. mediawiki.org documentation is
 often much more thorough, but it's maintained by a wiki that's open to
 anonymous edits, so its quality is sometimes sketchy -- don't assume that
 anything there is officially endorsed!
@@ -132,31 +133,27 @@ anything there is officially endorsed!
 == Upstream ==
 
 MediaWiki is a project hosted and led by the Wikimedia Foundation, the
-not-for-profit charity that operates Wikipedia.  Wikimedia employs the lead
+not-for-profit charity that operates Wikipedia. Wikimedia employs the lead
 developer and several other paid developers, but commit access is given out
-liberally and there are multiple very active volunteer developers as well.  A
+liberally and there are multiple very active volunteer developers as well. A
 list of developers can be found at <https://www.mediawiki.org/wiki/Developers>.
 
-MediaWiki's bug tracker is at <https://bugzilla.wikimedia.org>.  However, most
-developers follow the bug tracker little or not at all.  The best place to
-post if you want to get developers' attention is the wikitech-l mailing list
-<https://lists.wikimedia.org/mailman/listinfo/wikitech-l>.  Posts to wikitech-l
-will inevitably be read by multiple experienced MediaWiki developers.  There's
+MediaWiki's bug tracker is at <https://phabricator.wikimedia.org>. However, you
+might find that the best place to post if you want to get developers' attention
+is the wikitech-l mailing list
+<https://lists.wikimedia.org/mailman/listinfo/wikitech-l>. Posts to wikitech-l
+will inevitably be read by multiple experienced MediaWiki developers. There's
 also an active IRC chat at <irc://irc.freenode.net/mediawiki>, where there are
 usually several developers at reasonably busy times of day.
 
-Unfortunately, we don't have a very good system for patch review.  Patches
-should be submitted on Bugzilla (as unified diffs produced with "svn diff"
-against the latest trunk revision), but many patches languish without review
-until they bitrot into uselessness.  You might want to get a developer to
-commit to reviewing your patch before you put too much effort into it.
-Reasonably straightforward patches shouldn't be too hard to get accepted if
-there's an interested developer, however -- posting to Bugzilla and then
-dropping a note on wikitech-l if nobody responds is a good tactic.
+Our Git repositories are hosted at <https://gerrit.wikimedia.org>, see
+<https://www.mediawiki.org/wiki/Gerrit> for more information. Patches should
+be submitted there. If you know which developers are best suited to review your
+patch, add them to it, otherwise ask on IRC to get better review time.
 
 All redistributors of MediaWiki should be subscribed to mediawiki-announce
-<https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce>.  It's
-extremely low-traffic, with an average of less than one post per month.  All
+<https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce>. It's
+extremely low-traffic, with an average of less than one post per month. All
 new releases are announced here, including critical security updates.
 
 == Useful software to install ==
@@ -167,41 +164,40 @@ perhaps configure it to use them (see Configuration section of this document):
 
   * APC (Alternative PHP Cache), XCache, or similar: Will greatly speed up the
   execution of MediaWiki, and all other PHP applications, at some cost in
-  memory usage.  Will be used automatically for the most part.
-  * clamav: Can be used for virus scanning of uploaded files.  Enable with
+  memory usage. Will be used automatically for the most part.
+  * clamav: Can be used for virus scanning of uploaded files. Enable with
   "$wgAntivirus = 'clamav';".
-  * DjVuLibre: Allows processing of DjVu files.  To enable this, set
+  * DjVuLibre: Allows processing of DjVu files. To enable this, set
   "$wgDjvuDump = 'djvudump'; $wgDjvuRenderer = 'ddjvu'; $wgDjvuTxt = 'djvutxt';".
-  * HTML Tidy: Fixes errors in HTML at runtime.  Can be enabled with 
+  * HTML Tidy: Fixes errors in HTML at runtime. Can be enabled with 
        "$wgUseTidy = true;".
-  * ImageMagick: For resizing images.  "$wgUseImageMagick = true;" will enable
-  it.  PHP's GD can also be used, but ImageMagick is preferable.
-  * Squid: Can provide a drastic speedup and a major cut in resource
-  consumption, but enabling it may interfere with other applications.  It might
-  be suitable for a separate mediawiki-squid package.  For setup details, see:
-  <https://www.mediawiki.org/wiki/Manual:Squid_caching>
+  * ImageMagick: For resizing images. "$wgUseImageMagick = true;" will enable
+  it. PHP's GD can also be used, but ImageMagick is preferable.
+  * HTTP cache such as Varnish or Squid: can provide a drastic speedup and a
+  major cut in resource consumption, but enabling it may interfere with other
+  applications. It might be suitable for a separate package. For setup details, see:
+  - <https://www.mediawiki.org/wiki/Manual:Varnish_caching>
+  - <https://www.mediawiki.org/wiki/Manual:Squid_caching>
   * rsvg or other SVG rasterizer: ImageMagick can be used for SVG support, but
-  is not ideal.  Wikipedia (as of the time of this writing) uses rsvg.  To
+  is not ideal. Wikipedia (as of the time of this writing) uses rsvg. To
   enable, set "$wgSVGConverter = 'rsvg';" (or other as appropriate).
-  * texvc: Included with MediaWiki.  Instructions for compiling and
-  installing it are in the math/ directory.
 
-MediaWiki uses some standard GNU utilities as well, such as diff and diff3.  If
+MediaWiki uses some standard GNU utilities as well, such as diff and diff3. If
 these are present in /usr/bin or some other reasonable location, they will be
 configured automatically on install.
 
-MediaWiki also has a "job queue" that handles background processing.  Because
+MediaWiki also has a "job queue" that handles background processing. Because
 shared hosts often don't provide access to cron, the job queue is run on every
-page view by default.  This means the background tasks aren't really done in
-the background.  Busy wikis can set $wgJobRunRate to 0 and run
-maintenance/runJobs.php periodically out of cron.  Distributors probably
+page view by default. This means the background tasks aren't really done in
+the background. Busy wikis can set $wgJobRunRate to 0 and run
+maintenance/runJobs.php periodically out of cron. Distributors probably
 shouldn't set this up as a default, however, since the extra cron job is
 unnecessary overhead for a little-used wiki.
 
 == Web server configuration ==
 
 MediaWiki includes several .htaccess files to restrict access to some
-directories.  If the web server is not configured to support these files, and
+directories. If the web server is not configured to support these files, and
 the relevant directories haven't been moved someplace inaccessible anyway (e.g.
 symlinked in /usr/share with the web server configured to not follow symlinks),
 then it might be useful to deny web access to those directories in the web
index 2b3116d..e1b3974 100644 (file)
@@ -3467,6 +3467,9 @@ $user: User object for the logged-in user
 For functionality that needs to run after any login (API or web) use UserLoggedIn.
 &$user: the user object that was created on login
 &$inject_html: Any HTML to inject after the "logged in" message.
+$direct: (bool) The hook is called directly after a successful login. This will only happen once
+  per login. A UserLoginComplete call with direct=false can happen when the user visits the login
+  page while already logged in.
 
 'UserLoginForm': DEPRECATED! Create an AuthenticationProvider instead.
 Manipulate the login form.
index e5128d1..a5567fc 100644 (file)
@@ -1020,9 +1020,21 @@ class Html {
        static function srcSet( array $urls ) {
                $candidates = [];
                foreach ( $urls as $density => $url ) {
-                       // Cast density to float to strip 'x'.
-                       $candidates[] = $url . ' ' . (float)$density . 'x';
+                       // Cast density to float to strip 'x', then back to string to serve
+                       // as array index.
+                       $density = (string)(float)$density;
+                       $candidates[$density] = $url;
                }
+
+               // Remove duplicates that are the same as a smaller value
+               ksort( $candidates, SORT_NUMERIC );
+               $candidates = array_unique( $candidates );
+
+               // Append density info to the url
+               foreach ( $candidates as $density => $url ) {
+                       $candidates[$density] = $url . ' ' . $density . 'x';
+               }
+
                return implode( ", ", $candidates );
        }
 }
index 3dd37d7..62f4060 100644 (file)
@@ -1773,12 +1773,13 @@ class Title implements LinkTarget {
         *
         * @param array $query
         * @param bool $query2
-        * @param string $proto Protocol to use; setting this will cause a full URL to be used
+        * @param string|int|bool $proto A PROTO_* constant on how the URL should be expanded,
+        *                               or false (default) for no expansion
         * @see self::getLocalURL for the arguments.
         * @return string The URL
         */
-       public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
-               if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) {
+       public function getLinkURL( $query = '', $query2 = false, $proto = false ) {
+               if ( $this->isExternal() || $proto !== false ) {
                        $ret = $this->getFullURL( $query, $query2, $proto );
                } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
                        $ret = $this->getFragmentForURL();
index 515fbfc..89ca50c 100644 (file)
@@ -719,28 +719,29 @@ class WatchedItemStore implements StatsdAwareInterface {
         */
        public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
                $dbw = $this->getConnection( DB_MASTER );
-               $res = $dbw->select( [ 'watchlist' ],
-                       [ 'wl_user' ],
+               $uids = $dbw->selectFieldValues(
+                       'watchlist',
+                       'wl_user',
                        [
                                'wl_user != ' . intval( $editor->getId() ),
                                'wl_namespace' => $target->getNamespace(),
                                'wl_title' => $target->getDBkey(),
                                'wl_notificationtimestamp IS NULL',
-                       ], __METHOD__
+                       ],
+                       __METHOD__
                );
+               $this->reuseConnection( $dbw );
 
-               $watchers = [];
-               foreach ( $res as $row ) {
-                       $watchers[] = intval( $row->wl_user );
-               }
-
+               $watchers = array_map( 'intval', $uids );
                if ( $watchers ) {
                        // Update wl_notificationtimestamp for all watching users except the editor
                        $fname = __METHOD__;
-                       $dbw->onTransactionIdle(
-                               function () use ( $dbw, $timestamp, $watchers, $target, $fname ) {
+                       DeferredUpdates::addCallableUpdate(
+                               function () use ( $timestamp, $watchers, $target, $fname ) {
                                        global $wgUpdateRowsPerQuery;
 
+                                       $dbw = $this->getConnection( DB_MASTER );
+
                                        $watchersChunks = array_chunk( $watchers, $wgUpdateRowsPerQuery );
                                        foreach ( $watchersChunks as $watchersChunk ) {
                                                $dbw->update( 'watchlist',
@@ -758,12 +759,12 @@ class WatchedItemStore implements StatsdAwareInterface {
                                                }
                                        }
                                        $this->uncacheLinkTarget( $target );
+
+                                       $this->reuseConnection( $dbw );
                                }
                        );
                }
 
-               $this->reuseConnection( $dbw );
-
                return $watchers;
        }
 
index 3572229..0e4c6e0 100644 (file)
@@ -208,7 +208,7 @@ class ApiLogin extends ApiBase {
 
                                // Deprecated hook
                                $injected_html = '';
-                               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+                               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, true ] );
 
                                $result['lguserid'] = intval( $user->getId() );
                                $result['lgusername'] = $user->getName();
index 64bb9ba..822369a 100644 (file)
@@ -68,35 +68,37 @@ class ApiPurge extends ApiBase {
 
                                        # Parse content; note that HTML generation is only needed if we want to cache the result.
                                        $content = $page->getContent( Revision::RAW );
-                                       $enableParserCache = $this->getConfig()->get( 'EnableParserCache' );
-                                       $p_result = $content->getParserOutput(
-                                               $title,
-                                               $page->getLatest(),
-                                               $popts,
-                                               $enableParserCache
-                                       );
-
-                                       # Logging to better see expensive usage patterns
-                                       if ( $forceRecursiveLinkUpdate ) {
-                                               LoggerFactory::getInstance( 'RecursiveLinkPurge' )->info(
-                                                       "Recursive link purge enqueued for {title}",
-                                                       [
-                                                               'user' => $this->getUser()->getName(),
-                                                               'title' => $title->getPrefixedText()
-                                                       ]
+                                       if ( $content ) {
+                                               $enableParserCache = $this->getConfig()->get( 'EnableParserCache' );
+                                               $p_result = $content->getParserOutput(
+                                                       $title,
+                                                       $page->getLatest(),
+                                                       $popts,
+                                                       $enableParserCache
                                                );
-                                       }
-
-                                       # Update the links tables
-                                       $updates = $content->getSecondaryDataUpdates(
-                                               $title, null, $forceRecursiveLinkUpdate, $p_result );
-                                       DataUpdate::runUpdates( $updates );
-
-                                       $r['linkupdate'] = true;
 
-                                       if ( $enableParserCache ) {
-                                               $pcache = ParserCache::singleton();
-                                               $pcache->save( $p_result, $page, $popts );
+                                               # Logging to better see expensive usage patterns
+                                               if ( $forceRecursiveLinkUpdate ) {
+                                                       LoggerFactory::getInstance( 'RecursiveLinkPurge' )->info(
+                                                               "Recursive link purge enqueued for {title}",
+                                                               [
+                                                                       'user' => $this->getUser()->getName(),
+                                                                       'title' => $title->getPrefixedText()
+                                                               ]
+                                                       );
+                                               }
+
+                                               # Update the links tables
+                                               $updates = $content->getSecondaryDataUpdates(
+                                                       $title, null, $forceRecursiveLinkUpdate, $p_result );
+                                               DataUpdate::runUpdates( $updates );
+
+                                               $r['linkupdate'] = true;
+
+                                               if ( $enableParserCache ) {
+                                                       $pcache = ParserCache::singleton();
+                                                       $pcache->save( $p_result, $page, $popts );
+                                               }
                                        }
                                } else {
                                        $error = $this->parseMsg( [ 'actionthrottledtext' ] );
index dc8c607..c2e8b24 100644 (file)
@@ -93,7 +93,14 @@ class IcuCollation extends Collation {
                'be-tarask' => [ "Ё" ],
                'cy' => [ "Ch", "Dd", "Ff", "Ng", "Ll", "Ph", "Rh", "Th" ],
                'en' => [],
-               'fa' => [ "آ", "ء", "ه" ],
+               // RTL, let's put each letter on a new line
+               'fa' => [
+                       "آ",
+                       "ء",
+                       "ه",
+                       "ا",
+                       "و"
+               ],
                'fi' => [ "Å", "Ä", "Ö" ],
                'fr' => [],
                'hu' => [ "Cs", "Dz", "Dzs", "Gy", "Ly", "Ny", "Ö", "Sz", "Ty", "Ü", "Zs" ],
index 16ea219..f23e24f 100644 (file)
@@ -2460,7 +2460,7 @@ abstract class DatabaseBase implements IDatabase {
        final public function onTransactionIdle( callable $callback ) {
                $this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
                if ( !$this->mTrxLevel ) {
-                       $this->runOnTransactionIdleCallbacks();
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
                }
        }
 
@@ -2475,9 +2475,10 @@ abstract class DatabaseBase implements IDatabase {
        /**
         * Actually any "on transaction idle" callbacks.
         *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
         * @since 1.20
         */
-       protected function runOnTransactionIdleCallbacks() {
+       protected function runOnTransactionIdleCallbacks( $trigger ) {
                $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
 
                $e = $ePrior = null; // last exception
@@ -2492,7 +2493,7 @@ abstract class DatabaseBase implements IDatabase {
                                try {
                                        list( $phpCallback ) = $callback;
                                        $this->clearFlag( DBO_TRX ); // make each query its own transaction
-                                       call_user_func( $phpCallback );
+                                       call_user_func_array( $phpCallback, [ $trigger ] );
                                        if ( $autoTrx ) {
                                                $this->setFlag( DBO_TRX ); // restore automatic begin()
                                        } else {
@@ -2623,7 +2624,7 @@ abstract class DatabaseBase implements IDatabase {
                                        $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
                        }
 
-                       $this->runOnTransactionIdleCallbacks();
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
                }
 
                // Avoid fatals if close() was called
@@ -2699,7 +2700,7 @@ abstract class DatabaseBase implements IDatabase {
                                $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
                }
 
-               $this->runOnTransactionIdleCallbacks();
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
        }
 
        /**
@@ -2739,7 +2740,7 @@ abstract class DatabaseBase implements IDatabase {
 
                $this->mTrxIdleCallbacks = []; // clear
                $this->mTrxPreCommitCallbacks = []; // clear
-               $this->runOnTransactionIdleCallbacks();
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
        }
 
        /**
index 36772b8..c024632 100644 (file)
  * @ingroup Database
  */
 interface IDatabase {
+       /* Constants to onTransactionResolution() callbacks */
+       const TRIGGER_IDLE = 1;
+       const TRIGGER_COMMIT = 2;
+       const TRIGGER_ROLLBACK = 3;
+
        /**
         * A string describing the current software version, and possibly
         * other details in a user-friendly way. Will be listed on Special:Version, etc.
@@ -1223,6 +1228,9 @@ interface IDatabase {
         *
         * This is useful for combining cooperative locks and DB transactions.
         *
+        * The callback takes one argument:
+        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
+        *
         * @param callable $callback
         * @return mixed
         * @since 1.28
@@ -1242,6 +1250,9 @@ interface IDatabase {
         *
         * Updates will execute in the order they were enqueued.
         *
+        * The callback takes one argument:
+        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
+        *
         * @param callable $callback
         * @since 1.20
         */
index b60ed8a..0009781 100644 (file)
@@ -179,10 +179,8 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                        }
                }
 
-               $this->mDb->onTransactionIdle( function() use ( &$scopedLock ) {
-                       // Release the lock *after* the final COMMIT for correctness
-                       ScopedCallback::consume( $scopedLock );
-               } );
+               // Commit and release the lock
+               ScopedCallback::consume( $scopedLock );
        }
 
        private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
index 5a62185..b8e2726 100644 (file)
@@ -74,7 +74,7 @@ class SiteStatsUpdate implements DeferrableUpdate {
                        $this->doUpdatePendingDeltas();
                } else {
                        // Need a separate transaction because this a global lock
-                       wfGetDB( DB_MASTER )->onTransactionIdle( [ $this, 'tryDBUpdateInternal' ] );
+                       DeferredUpdates::addCallableUpdate( [ $this, 'tryDBUpdateInternal' ] );
                }
        }
 
index 5e9c34d..f23fbed 100644 (file)
@@ -80,8 +80,8 @@
        "config-no-scaling": "GD 라이브러리나 ImageMagick를 찾을 수 없습니다.\n그림 섬네일이 비활성화됩니다.",
        "config-no-uri": "<strong>오류:</strong> 현재 URI를 확인할 수 없습니다.\n설치가 중단되었습니다.",
        "config-no-cli-uri": "<strong>경고:</strong> 기본값을 사용하여 <code>--scriptpath</code>를 지정하지 않았습니다: <code>$1</code>.",
-       "config-using-server": "\"<nowiki>$1</nowiki>\"(을)를 서버 이름으로 사용합니다.",
-       "config-using-uri": "\"<nowiki>$1$2</nowiki>\"(을)를 서버 URL로 사용합니다.",
+       "config-using-server": "\"<nowiki>$1</nowiki>\" 서버 이름을 사용 중입니다.",
+       "config-using-uri": "\"<nowiki>$1$2</nowiki>\" 서버 URL을 사용 중입니다.",
        "config-uploads-not-safe": "<strong>경고:</strong> 올리기에 대한 기본 디렉터리(<code>$1</code>)는 임의의 스크립트 실행에 취약합니다.\n미디어위키는 보안 위협 때문에 모든 올려진 파일을 검사하지만, 올리기를 활성화하기 전에 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security 이 보안 취약점을 해결할 것]을 매우 권장합니다.",
        "config-no-cli-uploads-check": "<strong>경고:</strong> 올리기를 위한 기본 디렉터리(<code>$1</code>)는 CLI를 설치하는 동안 임의의 스크립트 실행에 대한 취약점에 대해 검사되지 않습니다.",
        "config-brokenlibxml": "시스템에 버그가 있는 PHP와 libxml2의 조합이 있으며 미디어위키나 다른 웹 애플리케이션에 숨겨진 데이터 손상을 일으킬 수 있습니다.\nlibxml2 2.7.3 이후 버전으로 업그레이드하세요. ([https://bugs.php.net/bug.php?id=45996 PHP에 제기한 버그])\n설치가 중단되었습니다.",
index 8b8d02d..6b5bfe0 100644 (file)
@@ -7,6 +7,7 @@
                ]
        },
        "config-information": "信息",
+       "config-back": "← 转去",
        "config-page-language": "闲话",
        "mainpagetext": "<strong>MediaWiki安装好哉。</strong>",
        "mainpagedocfooter": "请访问[https://meta.wikimedia.org/wiki/Help:Contents 用户手册]以获得使用此维基软件个信息!\n\n== 入门 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki 配置设置列表]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki 常见问题解答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki 发布邮件列表]"
index 7ab9cd3..2d67b87 100644 (file)
@@ -400,16 +400,13 @@ class LinkRenderer {
        private function getLinkURL( LinkTarget $target, array $query = [] ) {
                // TODO: Use a LinkTargetResolver service instead of Title
                $title = Title::newFromLinkTarget( $target );
-               $proto = $this->expandUrls !== false
-                       ? $this->expandUrls
-                       : PROTO_RELATIVE;
                if ( $this->forceArticlePath ) {
                        $realQuery = $query;
                        $query = [];
                } else {
                        $realQuery = [];
                }
-               $url = $title->getLinkURL( $query, false, $proto );
+               $url = $title->getLinkURL( $query, false, $this->expandUrls );
 
                if ( $this->forceArticlePath && $realQuery ) {
                        $url = wfAppendQuery( $url, $realQuery );
index 9176b54..b3a555a 100644 (file)
@@ -421,8 +421,10 @@ class ThumbnailImage extends MediaTransformOutput {
                }
 
                // Additional densities for responsive images, if specified.
-               if ( !empty( $this->responsiveUrls ) ) {
-                       $attribs['srcset'] = Html::srcSet( $this->responsiveUrls );
+               // If any of these urls is the same as src url, it'll be excluded.
+               $responsiveUrls = array_diff( $this->responsiveUrls, [ $this->url ] );
+               if ( !empty( $responsiveUrls ) ) {
+                       $attribs['srcset'] = Html::srcSet( $responsiveUrls );
                }
 
                Hooks::run( 'ThumbnailBeforeProduceHTML', [ $this, &$attribs, &$linkAttribs ] );
index 3fe9cdd..1017e44 100644 (file)
@@ -40,10 +40,6 @@ class ProfilerStub extends Profiler {
        public function close() {
        }
 
-       public function getCurrentSection() {
-               return '';
-       }
-
        public function logData() {
        }
 
index ccf764b..6596112 100644 (file)
@@ -1468,34 +1468,6 @@ MESSAGE;
                return wfAppendQuery( $script, $query );
        }
 
-       /**
-        * Build a load.php URL
-        * @deprecated since 1.24 Use createLoaderURL() instead
-        * @param array $modules Array of module names (strings)
-        * @param string $lang Language code
-        * @param string $skin Skin name
-        * @param string|null $user User name. If null, the &user= parameter is omitted
-        * @param string|null $version Versioning timestamp
-        * @param bool $debug Whether the request should be in debug mode
-        * @param string|null $only &only= parameter
-        * @param bool $printable Printable mode
-        * @param bool $handheld Handheld mode
-        * @param array $extraQuery Extra query parameters to add
-        * @return string URL to load.php. May be protocol-relative if $wgLoadScript is, too.
-        */
-       public static function makeLoaderURL( $modules, $lang, $skin, $user = null,
-               $version = null, $debug = false, $only = null, $printable = false,
-               $handheld = false, $extraQuery = []
-       ) {
-               global $wgLoadScript;
-
-               $query = self::makeLoaderQuery( $modules, $lang, $skin, $user, $version, $debug,
-                       $only, $printable, $handheld, $extraQuery
-               );
-
-               return wfAppendQuery( $wgLoadScript, $query );
-       }
-
        /**
         * Helper for createLoaderURL()
         *
@@ -1505,7 +1477,7 @@ MESSAGE;
         * @param array $extraQuery
         * @return array
         */
-       public static function createLoaderQuery( ResourceLoaderContext $context, $extraQuery = [] ) {
+       protected static function createLoaderQuery( ResourceLoaderContext $context, $extraQuery = [] ) {
                return self::makeLoaderQuery(
                        $context->getModules(),
                        $context->getLanguage(),
@@ -1522,7 +1494,7 @@ MESSAGE;
 
        /**
         * Build a query array (array representation of query string) for load.php. Helper
-        * function for makeLoaderURL().
+        * function for createLoaderURL().
         *
         * @param array $modules
         * @param string $lang
index e2bb516..f47a70b 100644 (file)
@@ -107,8 +107,7 @@ class RevDelArchivedFileItem extends RevDelFileItem {
                                                'target' => $this->list->title->getPrefixedText(),
                                                'file' => $file->getKey(),
                                                'token' => $user->getEditToken( $file->getKey() )
-                                       ],
-                                       false, PROTO_RELATIVE
+                                       ]
                                ),
                        ];
                }
index 921fe5a..dca4d5a 100644 (file)
@@ -215,8 +215,7 @@ class RevDelFileItem extends RevDelItem {
                                                'target' => $this->list->title->getPrefixedText(),
                                                'file' => $file->getArchiveName(),
                                                'token' => $user->getEditToken( $file->getArchiveName() )
-                                       ],
-                                       false, PROTO_RELATIVE
+                                       ]
                                ),
                        ];
                }
index d01751e..f06a192 100644 (file)
@@ -129,7 +129,7 @@ class SpecialCreateAccount extends LoginSignupSpecialPage {
                # Run any hooks; display injected HTML
                $injected_html = '';
                $welcome_creation_msg = 'welcomecreation-msg';
-               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, $direct ] );
 
                /**
                 * Let any extensions change what message is shown.
index db20d87..21f5659 100644 (file)
@@ -124,7 +124,7 @@ class SpecialUserLogin extends LoginSignupSpecialPage {
 
                # Run any hooks; display injected HTML if any, else redirect
                $injected_html = '';
-               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, $direct ] );
 
                if ( $injected_html !== '' || $extraMessages ) {
                        $this->showSuccessPage( 'success', $this->msg( 'loginsuccesstitle' ),
index 951cb52..09132f2 100644 (file)
@@ -1262,7 +1262,8 @@ class LoginFormPreAuthManager extends SpecialPage {
                # Run any hooks; display injected HTML if any, else redirect
                $currentUser = $this->getUser();
                $injected_html = '';
-               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html ] );
+               $direct = RequestContext::getMain()->getRequest()->wasPosted();
+               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html, $direct ] );
 
                if ( $injected_html !== '' ) {
                        $this->displaySuccessfulAction( 'success', $this->msg( 'loginsuccesstitle' ),
@@ -1283,8 +1284,9 @@ class LoginFormPreAuthManager extends SpecialPage {
                $currentUser = $this->getUser();
                $injected_html = '';
                $welcome_creation_msg = 'welcomecreation-msg';
+               $direct = RequestContext::getMain()->getRequest()->wasPosted();
 
-               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html ] );
+               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html, $direct ] );
 
                /**
                 * Let any extensions change what message is shown.
index 9e96b14..37807ba 100644 (file)
@@ -43,6 +43,11 @@ use \Sanitizer;
 # as soon as possible (usually as soon as the tag is closed) to reduce
 # its memory footprint.
 
+# We've been gradually lifting some of these restrictions to handle
+# non-sanitized output generated by extensions, but we shortcut the tokenizer
+# for speed (primarily by splitting on `<`) and so rely on syntactic
+# well-formedness.
+
 # On the other hand, I've been pretty careful to note with comments in the
 # code the places where this implementation omits features of the spec or
 # depends on the MediaWiki Sanitizer.  Perhaps in the future we'll want to
@@ -69,10 +74,10 @@ class BalanceSets {
        public static $unsupportedSet = [
                self::HTML_NAMESPACE => [
                        'html' => true, 'head' => true, 'body' => true, 'frameset' => true,
-                       'form' => true, 'frame' => true,
+                       'frame' => true,
                        'plaintext' => true, 'isindex' => true, 'textarea' => true,
                        'xmp' => true, 'iframe' => true, 'noembed' => true,
-                       'noscript' => true, 'select' => true, 'script' => true,
+                       'noscript' => true, 'script' => true,
                        'title' => true
                ]
        ];
@@ -185,7 +190,14 @@ class BalanceSets {
                ]
        ];
 
-       # OMITTED: formAssociatedSet, since we don't allow <form>
+       // See https://html.spec.whatwg.org/multipage/forms.html#form-associated-element
+       public static $formAssociatedSet = [
+               self::HTML_NAMESPACE => [
+                       'button' => true, 'fieldset' => true, 'input' => true,
+                       'keygen' => true, 'object' => true, 'output' => true,
+                       'select' => true, 'textarea' => true, 'img' => true
+               ]
+       ];
 
        public static $inScopeSet = [
                self::HTML_NAMESPACE => [
@@ -228,6 +240,12 @@ class BalanceSets {
                ]
        ];
 
+       public static $inInvertedSelectScopeSet = [
+               self::HTML_NAMESPACE => [
+                       'option' => true, 'optgroup' => true
+               ]
+       ];
+
        public static $mathmlTextIntegrationPointSet = [
                self::MATHML_NAMESPACE => [
                        'mi' => true, 'mo' => true, 'mn' => true, 'ms' => true,
@@ -663,19 +681,29 @@ class BalanceStack implements IteratorAggregate {
                return $out;
        }
 
+       /**
+        * Insert a comment at the appropriate place for inserting a node.
+        * @param string $value Content of the comment.
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#insert-a-comment
+        */
+       public function insertComment( $value ) {
+               // Just another type of text node, except for tidy p-wrapping.
+               return $this->insertText( '<!--' . $value . '-->', true );
+       }
+
        /**
         * Insert text at the appropriate place for inserting a node.
         * @param string $value
         * @see https://html.spec.whatwg.org/multipage/syntax.html#appropriate-place-for-inserting-a-node
         */
-       public function insertText( $value ) {
+       public function insertText( $value, $isComment = false ) {
                if (
                        $this->fosterParentMode &&
                        $this->currentNode->isA( BalanceSets::$tableSectionRowSet )
                ) {
                        $this->fosterParent( $value );
                } elseif (
-                       $this->tidyCompat &&
+                       $this->tidyCompat && !$isComment &&
                        $this->currentNode->isA( BalanceSets::$tidyPWrapSet )
                ) {
                        $this->insertHTMLELement( 'mw:p-wrap', [] );
@@ -784,6 +812,26 @@ class BalanceStack implements IteratorAggregate {
                return $this->inSpecificScope( $tag, BalanceSets::$inTableScopeSet );
        }
 
+       /**
+        * Determine if the stack has $tag in select scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-select-scope
+        */
+       public function inSelectScope( $tag ) {
+               // Can't use inSpecificScope to implement this, since it involves
+               // *inverting* a set of tags.  Implement manually.
+               foreach ( $this as $elt ) {
+                       if ( $elt->isA( $tag ) ) {
+                               return true;
+                       }
+                       if ( !$elt->isA( BalanceSets::$inInvertedSelectScopeSet ) ) {
+                               return false;
+                       }
+               }
+               return false;
+       }
+
        /**
         * Determine if the stack has $tag in a specific scope, $set.
         * @param BalanceElement|array|string $tag
@@ -1613,9 +1661,11 @@ class BalanceActiveFormattingElements {
 
                // Loop backward through the list until we find a marker or an
                // open element
+               $foundit = false;
                while ( $entry->prevAFE ) {
                        $entry = $entry->prevAFE;
                        if ( $entry instanceof BalanceMarker || $stack->indexOf( $entry ) >= 0 ) {
+                               $foundit = true;
                                break;
                        }
                }
@@ -1624,7 +1674,7 @@ class BalanceActiveFormattingElements {
                // the first element if we didn't find a marker or open element),
                // recreating formatting elements and pushing them back onto the list
                // of open elements.
-               if ( $entry->prevAFE ) {
+               if ( $foundit ) {
                        $entry = $entry->nextAFE;
                }
                do {
@@ -1690,16 +1740,16 @@ class BalanceActiveFormattingElements {
  * - The document is never in "quirks mode".
  * - All occurrences of < and > have been entity escaped, so we
  *   can parse tags by simply splitting on those two characters.
+ *   The character < must not appear inside comments.
  *   Similarly, all attributes have been "cleaned" and are double-quoted
  *   and escaped.
- * - All comments and null characters are assumed to have been removed.
+ * - All null characters are assumed to have been removed.
  * - We don't alter linefeeds after <pre>/<listing>.
  * - The following elements are disallowed: <html>, <head>, <body>, <frameset>,
- *   <form>, <frame>, <plaintext>, <isindex>, <textarea>, <xmp>, <iframe>,
- *   <noembed>, <noscript>, <select>, <script>, <title>.  As a result,
+ *   <frame>, <plaintext>, <isindex>, <textarea>, <xmp>, <iframe>,
+ *   <noembed>, <noscript>, <script>, <title>.  As a result,
  *   further simplifications can be made:
  *   - `frameset-ok` is not tracked.
- *   - `form element pointer` is not tracked.
  *   - `head element pointer` is not tracked (but presumed non-null)
  *   - Tokenizer has only a single mode.
  *
@@ -1723,11 +1773,44 @@ class Balancer {
        private $stack;
        private $strict;
        private $tidyCompat;
+       private $allowComments;
 
        private $textIntegrationMode = false;
        private $pendingTableText;
        private $originalInsertionMode;
        private $fragmentContext;
+       private $formElementPointer;
+
+       /**
+        * Valid HTML5 comments.
+        * Regex borrowed from Tim Starling's "remex-html" project.
+        */
+       const VALID_COMMENT_REGEX = "~ !--
+               (                             # 1. Comment match detector
+                       > | -> | # Invalid short close
+                       (                         # 2. Comment contents
+                               (?:
+                                       (?! --> )
+                                       (?! --!> )
+                                       (?! --! \z )
+                                       (?! -- \z )
+                                       (?! - \z )
+                                       .
+                               )*+
+                       )
+                       (                         # 3. Comment close
+                               --> |   # Normal close
+                               --!> |  # Comment end bang
+                               (                     # 4. Indicate matches requiring EOF
+                                       --! |   # EOF in comment end bang state
+                                       -- |    # EOF in comment end state
+                                       -  |    # EOF in comment end dash state
+                                               # EOF in comment state
+                               )
+                       )
+               )
+               ([^<]*) \z                    # 5. Non-tag text after the comment
+               ~xs";
 
        /**
         * Create a new Balancer.
@@ -1747,16 +1830,23 @@ class Balancer {
         *         program: <p>-wrapping is done to the children of
         *         <body> and <blockquote> elements, and empty elements
         *         are removed.
+        *     'allowComments': boolean, defaults to true.
+        *         When true, allows HTML comments in the input.
+        *         The Sanitizer generally strips all comments, so if you
+        *         are running on sanitized output you can set this to
+        *         false to get a bit more performance.
         */
        public function __construct( array $config = [] ) {
                $config = $config + [
                        'strict' => false,
                        'allowedHtmlElements' => null,
                        'tidyCompat' => false,
+                       'allowComments' => true,
                ];
                $this->allowedHtmlElements = $config['allowedHtmlElements'];
                $this->strict = $config['strict'];
                $this->tidyCompat = $config['tidyCompat'];
+               $this->allowComments = $config['allowComments'];
                if ( $this->allowedHtmlElements !== null ) {
                        # Sanity check!
                        $bad = array_uintersect_assoc(
@@ -1805,6 +1895,13 @@ class Balancer {
                $this->fragmentContext =
                        new BalanceElement( BalanceSets::HTML_NAMESPACE, 'body', [] );
                $this->resetInsertionMode();
+               $this->formElementPointer = null;
+               for ( $e = $this->fragmentContext; $e != null; $e = $e->parent ) {
+                       if ( $e->isHtmlNamed( 'form' ) ) {
+                               $this->formElementPointer = $e;
+                               break;
+                       }
+               }
 
                // First element is text not tag
                $x = $this->bitsIterator->current();
@@ -1821,6 +1918,7 @@ class Balancer {
                $this->afe = null;
                $this->stack = null;
                $this->fragmentContext = null;
+               $this->formElementPointer = null;
                return $result;
        }
 
@@ -1987,12 +2085,26 @@ class Balancer {
 
        /**
         * Grab the next "token" from $bitsIterator.  This is either a open/close
-        * tag or text, depending on whether the Sanitizer approves.
+        * tag or text or a comment, depending on whether the Sanitizer approves.
         */
        private function advance() {
                $x = $this->bitsIterator->current();
                $this->bitsIterator->next();
                $regs = [];
+               # Handle comments.  These won't be generated by mediawiki (they
+               # are stripped in the Sanitizer) but may be generated by extensions.
+               if (
+                       $this->allowComments &&
+                       preg_match( Balancer::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
+                       /* verify EOF condition where necessary */
+                       ( $regs[4][1] < 0 || !$this->bitsIterator->valid() )
+               ) {
+                       $contents = $regs[2][0];
+                       $rest = $regs[5][0];
+                       $this->insertToken( 'comment', $contents );
+                       $this->insertToken( 'text', str_replace( '>', '&gt;', $rest ) );
+                       return;
+               }
                # $slash: Does the current element start with a '/'?
                # $t: Current element name
                # $attribStr: String between element name and >
@@ -2074,8 +2186,6 @@ class Balancer {
                        }
                        if ( $node->isHtml() ) {
                                switch ( $node->localName ) {
-                               # OMITTED: <select>
-                               /*
                                case 'select':
                                        $stacklen = $this->stack->length();
                                        for ( $j = $i + 1; $j < $stacklen-1; $j++ ) {
@@ -2090,7 +2200,6 @@ class Balancer {
                                        }
                                        $this->switchMode( 'inSelectMode' );
                                        return;
-                               */
                                case 'tr':
                                        $this->switchMode( 'inRowMode' );
                                        return;
@@ -2232,6 +2341,9 @@ class Balancer {
                                // ignore any other end tag
                                return true;
                        }
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
                }
 
                // If not handled above
@@ -2324,7 +2436,21 @@ class Balancer {
                                # 2. OMITTED: frameset_ok
                                return true;
 
-                       # OMITTED: <form>
+                       case 'form':
+                               if (
+                                       $this->formElementPointer &&
+                                       $this->stack->indexOf( 'template' ) < 0
+                               ) {
+                                       return true; // in a form, not in a template.
+                               }
+                               if ( $this->stack->inButtonScope( "p" ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $elt = $this->stack->insertHTMLElement( $value, $attribs );
+                               if ( $this->stack->indexOf( 'template' ) < 0 ) {
+                                       $this->formElementPointer = $elt;
+                               }
+                               return true;
 
                        case 'li':
                                # OMITTED: frameset_ok
@@ -2487,8 +2613,6 @@ class Balancer {
                        # OMITTED: <noembed>
                        # OMITTED: <noscript>
 
-                       # OMITTED: <select>
-                       /*
                        case 'select':
                                $this->afe->reconstruct( $this->stack );
                                $this->stack->insertHTMLElement( $value, $attribs );
@@ -2504,7 +2628,6 @@ class Balancer {
                                        $this->switchMode( 'inSelectMode' );
                                        return true;
                                }
-                       */
 
                        case 'optgroup':
                        case 'option':
@@ -2621,7 +2744,26 @@ class Balancer {
                                $this->stack->popTag( $value );
                                return true;
 
-                       # OMITTED: <form>
+                       case 'form':
+                               if ( $this->stack->indexOf( 'template' ) < 0 ) {
+                                       $openform = $this->formElementPointer;
+                                       $this->formElementPointer = null;
+                                       if ( !$openform || !$this->stack->inScope( $openform ) ) {
+                                               return true;
+                                       }
+                                       $this->stack->generateImpliedEndTags();
+                                       // Don't flatten yet if we're removing a <form> element
+                                       // out-of-order. (eg. `<form><div></form>`)
+                                       $flatten = ( $this->stack->currentNode === $openform );
+                                       $this->stack->removeElement( $openform, $flatten );
+                               } else {
+                                       if ( !$this->stack->inScope( 'form' ) ) {
+                                               return true;
+                                       }
+                                       $this->stack->generateImpliedEndTags();
+                                       $this->stack->popTag( 'form' );
+                               }
+                               return true;
 
                        case 'p':
                                if ( !$this->stack->inButtonScope( 'p' ) ) {
@@ -2656,7 +2798,7 @@ class Balancer {
                        case 'h5':
                        case 'h6':
                                if ( !$this->stack->inScope( BalanceSets::$headingSet ) ) {
-                                       return;
+                                       return true; # ignore
                                }
                                $this->stack->generateImpliedEndTags();
                                $this->stack->popTag( BalanceSets::$headingSet );
@@ -2712,6 +2854,9 @@ class Balancer {
                                }
                        }
                        return true;
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
                } else {
                        Assert::invariant( false, "Bad token type: $token" );
                }
@@ -2777,7 +2922,17 @@ class Balancer {
                                $this->stack->pop();
                                return true;
 
-                       # OMITTED: <form>
+                       case 'form':
+                               if (
+                                       $this->formElementPointer ||
+                                       $this->stack->indexOf( 'template' ) >= 0
+                               ) {
+                                       return true; // ignore this token
+                               }
+                               $this->formElementPointer =
+                                       $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->popTag( $this->formElementPointer );
+                               return true;
                        }
                        // Fall through for "anything else" clause.
                } elseif ( $token === 'endtag' ) {
@@ -2805,6 +2960,9 @@ class Balancer {
                                return $this->inHeadMode( $token, $value, $attribs, $selfclose );
                        }
                        // Fall through for "anything else" clause.
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
                }
                // This is the "anything else" case:
                $this->stack->fosterParentMode = true;
@@ -2932,6 +3090,9 @@ class Balancer {
                        // Fall through for "anything else".
                } elseif ( $token === 'eof' ) {
                        return $this->inBodyMode( $token, $value, $attribs, $selfclose );
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
                }
 
                // Anything else
@@ -3141,19 +3302,109 @@ class Balancer {
                return $this->inBodyMode( $token, $value, $attribs, $selfclose );
        }
 
-       # OMITTED: <select>
-       /*
        private function inSelectMode( $token, $value, $attribs = null, $selfclose = false ) {
-               Assert::invariant( false, 'Unimplemented' );
+               if ( $token === 'text' ) {
+                       $this->stack->insertText( $value );
+                       return true;
+               } elseif ( $token === 'eof' ) {
+                       return $this->inBodyMode( $token, $value, $attribs, $selfclose );
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       # OMITTED: <html>
+                       case 'option':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+                       case 'optgroup':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) {
+                                       $this->stack->pop();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+                       case 'select':
+                               $this->inSelectMode( 'endtag', $value ); // treat it like endtag
+                               return true;
+                       case 'input':
+                       case 'keygen':
+                       case 'textarea':
+                               if ( !$this->stack->inSelectScope( 'select' ) ) {
+                                       return true; // ignore token (fragment case)
+                               }
+                               $this->inSelectMode( 'endtag', 'select' );
+                               return $this->insertToken( $token, $value, $attribs, $selfclose );
+                       case 'script':
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfclose );
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'optgroup':
+                               if (
+                                       $this->stack->currentNode->isHtmlNamed( 'option' ) &&
+                                       $this->stack->length() >= 2 &&
+                                       $this->stack->node( $this->stack->length() - 2 )->isHtmlNamed( 'optgroup' )
+                               ) {
+                                       $this->stack->pop();
+                               }
+                               if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) {
+                                       $this->stack->pop();
+                               }
+                               return true;
+                       case 'option':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               return true;
+                       case 'select':
+                               if ( !$this->stack->inSelectScope( $value ) ) {
+                                       return true; // fragment case
+                               }
+                               $this->stack->popTag( $value );
+                               $this->resetInsertionMode();
+                               return true;
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfclose );
+                       }
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               }
+               // anything else: just ignore the token
+               return true;
        }
 
        private function inSelectInTableMode( $token, $value, $attribs = null, $selfclose = false ) {
-               Assert::invariant( false, 'Unimplemented' );
+               switch ( $value ) {
+               case 'caption':
+               case 'table':
+               case 'tbody':
+               case 'tfoot':
+               case 'thead':
+               case 'tr':
+               case 'td':
+               case 'th':
+                       if ( $token === 'tag' ) {
+                               $this->inSelectInTableMode( 'endtag', 'select' );
+                               return $this->insertToken( $token, $value, $attribs, $selfclose );
+                       } elseif ( $token === 'endtag' ) {
+                               if ( $this->stack->inTableScope( $value ) ) {
+                                       $this->inSelectInTableMode( 'endtag', 'select' );
+                                       return $this->insertToken( $token, $value, $attribs, $selfclose );
+                               }
+                               return true;
+                       }
+               }
+               // anything else
+               return $this->inSelectMode( $token, $value, $attribs, $selfclose );
        }
-       */
 
        private function inTemplateMode( $token, $value, $attribs = null, $selfclose = false ) {
-               if ( $token === 'text' ) {
+               if ( $token === 'text' || $token === 'comment' ) {
                        return $this->inBodyMode( $token, $value, $attribs, $selfclose );
                } elseif ( $token === 'eof' ) {
                        if ( $this->stack->indexOf( 'template' ) < 0 ) {
index f63f7c3..97a8233 100644 (file)
@@ -10,7 +10,8 @@
                        "GeekEmad",
                        "Nemo bis",
                        "Shbib Al-Subaie",
-                       "Macofe"
+                       "Macofe",
+                       "علاء"
                ]
        },
        "tog-usenewrc": ")جمّع التعديلات حسب الصفحة في أحدث التغييرات وقائمة المراقبة (يتطلب جافاسكربت",
        "rc-enhanced-hide": "أخفِ التفاصيل",
        "recentchangeslinked": "تغييرات ذات علاقة",
        "recentchangeslinked-title": "التغييرات المرتبطة ب \"$1\"",
-       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثا للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
+       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثاً للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
        "recentchangeslinked-page": "اسم الصفحة:",
-       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضا عن ذلك",
+       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك",
        "uploadlogpage": "سجل الرفع",
        "filedesc": "ملخص:",
        "license": "ترخيص:",
index 06a8abc..738cd5e 100644 (file)
        "category_header": "صفحات تصنيف «$1»",
        "subcategories": "تصنيفات فرعية",
        "category-media-header": "ملفات تصنيف \"$1\"",
-       "category-empty": "هذا التصنيف لا يحتوي حاليا على صفحات أو ملفات.",
+       "category-empty": "هذا التصنيف لا يحتوي حالياً على أي صفحات أو ملفات.",
        "hidden-categories": "{{PLURAL:$1|لا تصنيفات مخفية|تصنيف مخفي|تصنيفان مخفيان|تصنيفات مخفية}}",
        "hidden-category-category": "تصنيفات مخفية",
        "category-subcat-count": "{{PLURAL:$2|هذا التصنيف يحوي التصنيف الفرعي التالي|هذا التصنيف يحوي {{PLURAL:$1||التصنيف الفرعي|تصنيفين فرعيين|$1 تصنيفات فرعية}}، من إجمالي $2.}}",
        "recentchangeslinked-feed": "تغييرات ذات علاقة",
        "recentchangeslinked-toolbox": "تغييرات ذات علاقة",
        "recentchangeslinked-title": "التغييرات المرتبطة بصفحة «$1»",
-       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثا للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''مغلظة'''",
+       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثاً للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
        "recentchangeslinked-page": "اسم الصفحة:",
-       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضا عن ذلك",
+       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك",
        "recentchanges-page-added-to-category": "[[:$1]] أضيفت إلى التصنيف",
        "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] و{{PLURAL:$2|صفحة واحدة|صفحتان|$2 صفحات}} إلى التصنيف",
        "recentchanges-page-removed-from-category": "أزيلت [[:$1]] من التصنيف",
index 6b9776f..c47c118 100644 (file)
        "botpasswords-label-delete": "Supprimer",
        "botpasswords-label-resetpassword": "Réinitialiser le mot de passe",
        "botpasswords-label-grants": "Droits applicables :",
-       "botpasswords-help-grants": "Chaque droit accordé donne accès aux droits utilisateurs listés dont dispose déjà un compte. Voyez le [[Special:ListGrants|tableau des droits]] pour plus d’information.",
+       "botpasswords-help-grants": "Chaque droit accordé donne accès à la liste des droits utilisateurs dont l’utilisateur dispose déjà. Voyez le [[Special:ListGrants|tableau des droits]] pour plus d’informations.",
        "botpasswords-label-restrictions": "Restrictions d’utilisation :",
        "botpasswords-label-grants-column": "Accordé",
        "botpasswords-bad-appid": "Le nom de robot « $1 » n’est pas valide.",
        "showpreview": "Prévisualiser",
        "showdiff": "Voir les modifications",
        "blankarticle": "<strong>Attention :</strong> la page que vous créez est vide.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la page sera créée sans aucun contenu.",
-       "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté. Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre nom d’utilisateur, entre autres avantages.",
-       "anonpreviewwarning": "<em>Vous n’êtes pas identifié{{GENDER:||e}}. Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>",
-       "missingsummary": "<strong>Rappel :</strong> vous n’avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « {{int:savearticle}} », la publication sera faite sans nouvel avertissement.",
+       "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté. Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre nom d’utilisateur, avec d'autres avantages.",
+       "anonpreviewwarning": "<em>Vous n’êtes pas connecté{{GENDER:||e}}. Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>",
+       "missingsummary": "<strong>Rappel :</strong> vous n’avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « {{int:savearticle}} », vos modifications seront sauvegardées sans résumé.",
        "selfredirect": "<strong>Attention :</strong> vous êtes en train de rediriger la page vers elle-même.\nVous pouvez avoir spécifié la mauvaise cible pour la redirection, ou vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la redirection sera tout de même créée.",
        "missingcommenttext": "Veuillez entrer un commentaire ci-dessous.",
        "missingcommentheader": "<strong>Rappel :</strong> vous n’avez pas fourni de sujet pour ce commentaire.\nSi vous cliquez de nouveau sur « {{int:Savearticle}} », votre modification sera enregistrée sans sujet.",
        "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
        "confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].",
        "nosuchsectiontitle": "Impossible de trouver la section",
-       "nosuchsectiontext": "Vous avez essayé de modifier une section qui n’existe pas.\nElle a peut-être été déplacée ou supprimée depuis que vous avez lu cette page.",
+       "nosuchsectiontext": "Vous avez essayé de modifier une section qui n’existe pas.\nElle a peut-être été déplacée ou supprimée pendant que vous la consultiez.",
        "loginreqtitle": "Connexion nécessaire",
        "loginreqlink": "connecter",
        "loginreqpagetext": "Vous devez vous $1 pour voir les autres pages.",
        "nonunicodebrowser": "<strong>Attention : votre navigateur ne supporte pas l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII  sous forme hexadécimale dans la boîte de modification.",
        "editingold": "<strong>Attention : vous êtes en train de modifier une ancienne version de cette page.</strong>\nSi vous la publiez, toutes les modifications effectuées depuis cette version seront perdues.",
        "yourdiff": "Différences",
-       "copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). Si vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br /> Vous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public ou d’une ressource libre similaire. <strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
+       "copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). \nSi vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br /> \nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public ou d’une ressource libre similaire. \n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
        "copyrightwarning2": "Toutes les contributions à {{SITENAME}} peuvent être modifiées ou supprimées par d’autres utilisateurs. Si vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br \n/>Vous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public, ou d’une ressource libre. (voir $1 pour plus de détails).\n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !<strong>",
        "editpage-cannot-use-custom-model": "Le modèle de contenu de cette page ne peut pas être modifié.",
        "longpageerror": "<strong>Erreur : Le texte que vous avez soumis fait {{PLURAL:$1|un Kio|$1 Kio}}, ce qui dépasse la limite fixée à {{PLURAL:$2|un Kio|$2 Kio}}.</strong>\nIl ne peut pas être sauvegardé.",
        "history-edit-tags": "Modifier les balises des révisions sélectionnées",
        "rev-deleted-comment": "(résumé de modification retiré)",
        "rev-deleted-user": "(nom d'utilisateur retiré)",
-       "rev-deleted-event": "(détails de l’entrée retirés)",
+       "rev-deleted-event": "(détails de l’historique retirés)",
        "rev-deleted-user-contribs": "[nom d’utilisateur ou adresse IP retiré – modification masquée dans les contributions]",
        "rev-deleted-text-permission": "Cette version de la page a été <strong>effacée</strong>.\nDes détails sont disponibles dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "rev-suppressed-text-permission": "Cette version de la page a été <strong>masquée</strong>.\nLes détails se trouvent dans le [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} journal des masquages].",
index 655058d..2920310 100644 (file)
        "content-model-css": "CSS",
        "content-json-empty-object": "Objecto vacue",
        "content-json-empty-array": "Array vacue",
+       "deprecated-self-close-category": "Paginas que usa etiquettas HTML auto-claudite non valide",
+       "deprecated-self-close-category-desc": "Le pagina contine etiquettas HTML auto-claudite non valide, como <code>&lt;b/></code> o <code>&lt;span/></code>. Le comportamento de istes cambiara proximemente pro esser in accordo con le specification HTML5, dunque lor uso in wikitexto es obsolete.",
        "duplicate-args-warning": "<strong>Attention:</strong> [[:$1]] appella [[:$2]] con plure valores pro le parametro \"$3\". Solmente le ultime valor fornite essera usate.",
        "duplicate-args-category": "Paginas que usa parametros duplicate in appellos de patrono",
        "duplicate-args-category-desc": "Le pagina contine appellos de patrono que usa duplicatos de parametros, como per exemplo <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "pagina non existe ancora",
        "mw-widgets-titleinput-description-redirect": "redirection a $1",
-       "api-error-blacklisted": "Per favor elige un altere titulo, plus descriptive.",
        "sessionmanager-tie": "Impossibile combinar plure typos de authentication de requesta: $1.",
        "sessionprovider-generic": "sessiones $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessiones basate sur cookies",
        "log-action-filter-newusers": "Typo de creation de conto:",
        "log-action-filter-patrol": "Typo de patrulia:",
        "log-action-filter-protect": "Typo de protection:",
-       "log-action-filter-rights": "Typo de cambio de derecto",
-       "log-action-filter-suppress": "Typo de suppression",
+       "log-action-filter-rights": "Typo de cambio de derecto:",
+       "log-action-filter-suppress": "Typo de suppression:",
        "log-action-filter-upload": "Typo de incargamento:",
        "log-action-filter-all": "Toto",
        "log-action-filter-block-block": "Blocar",
index 2264e1d..fc76250 100644 (file)
        "sqlite-no-fts": "$1 (全文検索なし)",
        "logentry-delete-delete": "$1 がページ「$3」を{{GENDER:$2|削除しました}}",
        "logentry-delete-restore": "$1 がページ「$3」を{{GENDER:$2|復元しました}}",
-       "logentry-delete-event": "$1 が$3の{{PLURAL:$5|記録項目$5件}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
-       "logentry-delete-revision": "$1 がページ「$3」の{{PLURAL:$5|$5版}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
-       "logentry-delete-event-legacy": "$1 が「$3」の記録項目の閲覧レベルを{{GENDER:$2|変更しました}}",
+       "logentry-delete-event": "$1 が $3 の{{PLURAL:$5|記録項目|記録項目$5件}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
+       "logentry-delete-revision": "$1 がページ「$3」の{{PLURAL:$5|版|$5件の版}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
+       "logentry-delete-event-legacy": "$1 が $3 の記録項目の閲覧レベルを{{GENDER:$2|変更しました}}",
        "logentry-delete-revision-legacy": "$1 がページ「$3」の版の閲覧レベルを{{GENDER:$2|変更しました}}",
-       "logentry-suppress-delete": "$1 がページ「$3」を{{GENDER:$2|隠蔽しました}}",
-       "logentry-suppress-event": "$1 が$3の{{PLURAL:$5|記録項目$5件}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
-       "logentry-suppress-revision": "$1 がページ「$3」の{{PLURAL:$5|$5版}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
-       "logentry-suppress-event-legacy": "$1 が$3で記録項目の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
+       "logentry-suppress-delete": "$1 がページ「$3」を{{GENDER:$2|秘匿しました}}",
+       "logentry-suppress-event": "$1 が $3 の{{PLURAL:$5|記録項目|$5件の記録項目}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
+       "logentry-suppress-revision": "$1 がページ「$3」の{{PLURAL:$5|版|$5件の版}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
+       "logentry-suppress-event-legacy": "$1 が $3 の記録項目の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
        "logentry-suppress-revision-legacy": "$1 がページ「$3」の版の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
        "revdelete-content-hid": "本文の不可視化",
        "revdelete-summary-hid": "編集要約の不可視化",
        "logentry-tag-update-add-logentry": "$1 がページ $3 の記録項目 $5 に{{PLURAL:$7|タグ}} $6 を{{GENDER:$2|追加しました}}",
        "logentry-tag-update-remove-revision": "$1 がページ $3 の版 $4 から{{PLURAL:$9|タグ}} $8 を{{GENDER:$2|除去しました}}",
        "logentry-tag-update-remove-logentry": "$1 がページ $3 の記録項目 $5 から{{PLURAL:$9|タグ}} $8 を{{GENDER:$2|除去しました}}",
-       "logentry-tag-update-revision": "$1 ã\81\8cã\83\9aã\83¼ã\82¸ã\80\8c$3ã\80\8dã\81®ç\89\88 $4 ã\81§ã\81®ã\82¿ã\82°ã\82\92{{GENDER:$2|æ\9b´æ\96°ã\81\97ã\81¾ã\81\97ã\81\9f}} ($6 ã\81\8c{{PLURAL:$7|追å\8a }}ã\80\81$8 ã\81\8c{{PLURAL:$9|削除}})",
+       "logentry-tag-update-revision": "$1 ã\81\8cã\83\9aã\83¼ã\82¸ã\80\8c$3ã\80\8dã\81®ç\89\88 $4 ã\81®ã\82¿ã\82°ã\82\92{{GENDER:$2|æ\9b´æ\96°ã\81\97ã\81¾ã\81\97ã\81\9f}} ($6 ã\82\92{{PLURAL:$7|追å\8a }}ã\80\81$8 ã\82\92{{PLURAL:$9|削除}})",
        "logentry-tag-update-logentry": "$1 がページ「$3」の記録項目 $5 のタグを{{GENDER:$2|更新しました}} ($6 を{{PLURAL:$7|追加}}、$8 を{{PLURAL:$9|削除}})",
        "rightsnone": "(なし)",
        "revdelete-summary": "編集内容の要約",
index 208652e..e3218de 100644 (file)
        "spamprotectiontext": "스팸 필터가 문서 저장을 막았습니다.\n바깥 사이트로 연결하는 링크 중에 블랙리스트에 포함된 사이트가 있을 것입니다.",
        "spamprotectionmatch": "문제가 되는 부분은 다음과 같습니다: $1",
        "spambot_username": "미디어위키 스팸 정리",
-       "spam_reverting": "$1(을)를 포함하지 않는 최신 버전으로 되돌림",
+       "spam_reverting": "$1에 대한 링크를 포함하지 않는 최신 버전으로 되돌림",
        "spam_blanking": "모든 버전에 $1 링크를 포함하고 있어 차단함",
        "spam_deleting": "모든 버전에 $1 링크를 포함하고 있어 삭제함",
        "simpleantispam-label": "스팸 방지 검사입니다.\n이것을 입력하지 <strong>마세요</strong>!",
index de75f5e..2907aa1 100644 (file)
@@ -30,6 +30,7 @@
        "tog-watchdefault": "Azonzi e paggine e i files che modiffico a-i mæ sotta oservaçion",
        "tog-watchmoves": "Azonzi e paggine e i file che mescio a-i mæ sotta oservaçion",
        "tog-watchdeletion": "Azonzi e paggine e i files che scancello a-i mæ sotta oservaçion",
+       "tog-watchuploads": "Azonzi i noeuvi file che metto in osservaçion",
        "tog-watchrollback": "Azonzi a-i sotta osservassion e paggine dovve ho fæto un rollback",
        "tog-minordefault": "Indica de longo comme menô e modiffiche",
        "tog-previewontop": "Veddi l'anteprimma de d'äto a-o spaçio pe cangiâ",
        "databaseerror-query": "Query: $1",
        "databaseerror-function": "Fonsion: $1",
        "databaseerror-error": "Errô: $1",
+       "transaction-duration-limit-exceeded": "Pe evitâ un ato ritardo de replica, questa opiaçion a l'è stæta interotta perché a duata do tempo de scrittua ($1) a l'ha superou o limmite de $2 {{PLURAL:$2|segondo|segondi}}.\nSe t'ê aproeuvo a cangiâ tante cose inte'na votta sola, proeuva invece a cangiâ poche cose in tante votte.",
        "laggedslavemode": "'''Attension:''' a paggina a porriæ no riportâ i aggiornamenti ciù reçenti.",
        "readonly": "Database bloccòu",
        "enterlockreason": "Scrivi o motivo do blocco, e 'na stimma de quande o saiâ rimosso",
        "mypreferencesprotected": "No ti g'hæ o permisso pe modificâ e teu preferense.",
        "ns-specialprotected": "No se pœu modificâ e paggine speciali",
        "titleprotected": "A creaçion de 'na paggina con sto tittolo a l'è stæta bloccâ da [[User:$1|$1]].\nA raxon a l'è: <em>$2</em>.",
-       "filereadonlyerror": "N'ho posciuo modificâ o file \"$1\" perché o repository de file \"$2\" o l'è in modalitæ de sola lettua.\n\nL'amministratô ch'o l'ha bloccòu o l'ha fornio sta motivaçion: \"$3\".",
+       "filereadonlyerror": "N'ho posciuo modificâ o file \"$1\" perché o repository de file \"$2\" o l'è in modalitæ de sola lettua.\n\nL'amministratô de scistema ch'o l'ha bloccòu o l'ha fornio sta motivaçion: \"$3\".",
        "invalidtitle-knownnamespace": "Tittolo non vallido con namespace \"$2\" e testo \"$3\"",
        "invalidtitle-unknownnamespace": "Tittolo non vallido con namespace sconosciuo \"$1\" e testo \"$2\"",
        "exception-nologin": "No t'ê introu",
        "virus-scanfailed": "scansion fallia (codice $1)",
        "virus-unknownscanner": "antivirus sconosciuo:",
        "logouttext": "'''Sciortîa effettuâ.'''\n\nDanni a mente che gh'è de paggine che porrieivan continuâ a pai comme se a sciortîa a no foise avegnua, pe scin che no ti nettezzi a cache do to navegatô.",
+       "cannotlogoutnow-title": "Aoa no se poeu sciortî",
+       "cannotlogoutnow-text": "Quande s'adoeuvia $1 no se poeu sciortî.",
        "welcomeuser": "Benvegnuo, $1!",
        "welcomecreation-msg": "L'utensa a l'è stæta creâ correttamente.\nSe ti veu ti peu personalizzâ e [[Special:Preferences|preferençe de {{SITENAME}}]].",
        "yourname": "Nomme",
        "remembermypassword": "Aregòrda a mæ login in sto navegatô (pe in mascimo de $1 {{PLURAL:$1|giórno|giórni}})",
        "userlogin-remembermypassword": "Mantegnime collegou",
        "userlogin-signwithsecure": "Adoeuvia una conescion segua",
+       "cannotloginnow-title": "Aoa no se poeu intrâ",
+       "cannotloginnow-text": "Quande s'adoeuvia $1 no se poeu intrâ.",
        "yourdomainname": "Indirisso do scito:",
        "password-change-forbidden": "No ti peu cangiâ poula segretta in questa wiki.",
        "externaldberror": "Gh'è stæto un aro co-ol server de autenticaçion esterno, oppû no ti g'hæ i aotorizzaçioin pe aggiornâ o to accesso esterno.",
        "login": "Intra",
+       "login-security": "Veifica a to identitæ",
        "nav-login-createaccount": "Intra / Registrate",
        "userlogin": "Intra / Registrite",
        "userloginnocreate": "Intra",
        "userlogin-resetpassword-link": "T'hæ miga ascordou a teu poula segretta?",
        "userlogin-helplink2": "Agiutto pe intrâ",
        "userlogin-loggedin": "Ti t'ê zà connesso comme {{GENDER:$1|$1}}.\nUsa o formulaio sottostante pe accede comme 'n atro utente.",
+       "userlogin-reauth": "Ti g'hæ da intrâ 'n'atra votta pe veificâ che ti t'ê {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Crea 'n atra utensa",
        "createacct-emailrequired": "Addresso e-mail:",
        "createacct-emailoptional": "Adresso email (opsionale)",
        "createacct-email-ph": "Scrivi o teu adresso email",
        "createacct-another-email-ph": "Scrivi o teu adresso email",
        "createaccountmail": "Doeuvia una password temporanea abrettio e mandila a l'adresso de posta elettronica speçificou",
+       "createaccountmail-help": "O poeu ese doeuviou pe creâ un'utensa pe 'n'atra person-a sensa doveine conosce a password.",
        "createacct-realname": "Nomme reale (opçionâ)",
        "createaccountreason": "Raxon:",
        "createacct-reason": "Raxon",
        "createacct-reason-ph": "Perché t'ê apreuvo a creâ un'atra utensa",
+       "createacct-reason-help": "Messaggio vixualizou into registro da creaçion de l'utença",
        "createacct-submit": "Crea a to utensa",
        "createacct-another-submit": "Crea utensa",
+       "createacct-continue-submit": "Continnoa a creaçion de l'utença",
+       "createacct-another-continue-submit": "Continnoa a creaçion de l'utença",
        "createacct-benefit-heading": "{{SITENAME}} o l'è realizzou da de gente comme ti.",
        "createacct-benefit-body1": "{{PLURAL:$1|modiffica|modiffiche}}",
        "createacct-benefit-body2": "{{PLURAL:$1|paggina|paggine}}",
        "nocookiesnew": "L'utensa a l'è stæta creâ, ma ti no t'ê intròu. {{SITENAME}} o deuvia i cookie pe lasciâ intrâ i utenti e ti ti ghe l'hæ disattivæ.\nRipreuva a intrâ co-o to nomme utente e poula segretta apen-a creæ doppo avei attivòu i cookie.",
        "nocookieslogin": "Pe intrâ in {{SITENAME}} bezeugna aveighe i cookie attivæ. Ti ti ghe l'hæ disattivæ. Pe piaxei attîvili e preuva torna a intrâ.",
        "nocookiesfornew": "L'utensa a no l'è stæta creâ, perché n'emmo posciuo confermâ a so sorgente.\nAsseguite d'avei attivòu i cookie, recarrega sta paggina e ripreuva.",
+       "createacct-loginerror": "L'utença a l'è stæta creaa correttamente, ma no l'è stæto poscibbile fate accede in moddo aotomattico. Procedi co l'[[Special:UserLogin|accesso manoâ]].",
        "noname": "O nomme d'ûtente o l'è sballiòu.",
        "loginsuccesstitle": "Accesso effettuòu",
        "loginsuccess": "'''O collegamento a-o server de {{SITENAME}} co-o nomme d'ûtente \"$1\" o l'è attivo.'''",
        "noemail": "No gh'è nisciûn indirisso e-mail registròu pe l'ûtente \"$1\".",
        "noemailcreate": "Ti devi dâ un addresso e-mail vallido.",
        "passwordsent": "Ûnn-a nêuva paròlla d'ordine a l'è stæta inviâa a l'indirisso e-mail registròu pe l'ûtente \"$1\".\nPe piaxei, fa 'n accesso appenn-a ti a ghe reçeivi.",
-       "blocked-mailpassword": "O teu addresso IP o l'è bloccòu, e pe sta raxon non se peu usâ a funsion de recuppero da pòula segretta, pe prevegnî di abuxi.",
+       "blocked-mailpassword": "O teu addresso IP o l'è bloccòu a-e modiffiche, pe prevegnî di abuxi non se peu doeuviâ a fonçion de recuppero da pòula segretta da queseto addresso IP.",
        "eauthentsent": "Un messaggio e-mail de conferma o l'è stæto inviòu a l'addresso indicòu.\nPe abilitâ l'invîo de messaggi e-mail pe quest'utensa, se deve seguî e instrussioin indicæ, pe confermâ che ti t'ê o legittimo propietâio de l'utensa.",
        "throttled-mailpassword": "Un'e-mail de reimpostassione da poula segretta a l'è zà stæta inviâ da meno de {{PLURAL:$1|1 oa|$1 oe}}.\nPe prevegnî di abuxi, a fonsion de reimpostassion da poula segretta a peu vese deuviâ solo che 'na votta ogni {{PLURAL:$1|oa|$1 oe}}.",
        "mailerror": "Errô inte l'invio do messaggio: $1",
        "createacct-another-realname-tip": "O nomme veo o l'è opçionâ.\nSe ti çerni de inseilo, o saiâ deuviòu pe attribuî a l'aotô a paternitæ di contengnui inviæ.",
        "pt-login": "Intra",
        "pt-login-button": "Intra",
+       "pt-login-continue-button": "Continoa l'accesso",
        "pt-createaccount": "Registrite",
        "pt-userlogout": "Sciorti",
        "php-mail-error-unknown": "Errô sconosciuo intaa funçion PHP mail()",
        "newpassword": "Neuva poula segretta",
        "retypenew": "Ripette a nêuva paròlla d'ordine:",
        "resetpass_submit": "Çerni a poula segretta e intra",
-       "changepassword-success": "O cangio de password o l'é anæto ben!",
+       "changepassword-success": "A to password a l'è stæta cangiâ!",
        "changepassword-throttled": "T'hæ çercòu de intrâ troppe votte tutt'assemme.\nPe piaxei aspeta $1 primma de provâ torna.",
+       "botpasswords": "Password bot",
+       "botpasswords-summary": "<em>Password bot</em> o consente l'accesso a un'utença tramite API sença doeuviâ e credençiæ d'accesso prinçipæ de l'utença. I driti utente disponibili quande s'è effettuou l'accesso co-ina password bot poeuan ese limitæ.\n\nSe no ti conosci o motivo pe-o quæ ti porriesci voeilo fâ, alloa foscia no ti doviesci fâlo. Nisciun doviæ mai domandâte de generâ un de questi e dapoeu dâlo a liatri.",
+       "botpasswords-disabled": "E password bot son disabilitæ.",
+       "botpasswords-no-central-id": "Pe doeuviâ una password bot, l'è necessaio accede a un'utença çentralizzâ.",
+       "botpasswords-existing": "Password bot in existença",
+       "botpasswords-createnew": "Crea una noeuva password bot",
+       "botpasswords-editexisting": "Modifica una password bot existente",
+       "botpasswords-label-appid": "Nomme bot:",
+       "botpasswords-label-create": "Crea",
+       "botpasswords-label-update": "Aggiorna",
+       "botpasswords-label-cancel": "Anulla",
+       "botpasswords-label-delete": "Scassa",
+       "botpasswords-label-resetpassword": "Reimposta a poula segretta",
+       "botpasswords-label-grants": "Assegnaçioin applicabile:",
+       "botpasswords-help-grants": "Ogni assegnaçion a dà accesso a-i driti utente elencæ che un'utença a g'ha zà. Amia a [[Special:ListGrants|tabella d'e assegnaçioin]] pe de ulteioî informaçioin.",
+       "botpasswords-label-restrictions": "Restriçioin d'utilizzo:",
+       "botpasswords-label-grants-column": "Assegnaçioin",
+       "botpasswords-bad-appid": "O nomme bot \"$1\" o no l'è vallido.",
+       "botpasswords-insert-failed": "Imposcibile azonze o nomme bot \"$1\". O l'è za stæto azonto?",
+       "botpasswords-update-failed": "Imposcibile aggiornâ o nomme bot \"$1\". O l'è stæto scassou?",
+       "botpasswords-created-title": "Password bot creâ",
+       "botpasswords-created-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta creâ.",
+       "botpasswords-updated-title": "Password bot aggiornâ",
+       "botpasswords-updated-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta aggiornâ.",
+       "botpasswords-deleted-title": "Password bot scassâ",
+       "botpasswords-deleted-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta scassâ.",
+       "botpasswords-newpassword": "A noeuva password pe accede con <strong>$1</strong> a l'è <strong>$2</strong>. <em>Marchitelo pe rifeimento futuo.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider o no l'è disponibbile.",
+       "botpasswords-restriction-failed": "E restriçioin de password bot impediscian questo accesso.",
+       "botpasswords-invalid-name": "O nomme utente indicou o no conten o separatô pe-o password bot (\"$1\").",
+       "botpasswords-not-exist": "L'utente \"$1\" o no dispon-e de 'na password bot ciamâ \"$2\".",
        "resetpass_forbidden": "No l'é poscìbile cangiâ e paròlle segrétte",
+       "resetpass_forbidden-reason": "No l'é poscìbile cangiâ e paole segrette: $1",
        "resetpass-no-info": "Pe anâ direttamente a sta paggina, primma ti g'hæ da intrâ .",
        "resetpass-submit-loggedin": "Cangia a password",
        "resetpass-submit-cancel": "Anulla",
        "passwordreset-email": "Addresso e-mail:",
        "passwordreset-emailtitle": "Dettaggi account sciu {{SITENAME}}",
        "passwordreset-emailtext-ip": "Quarcun (probabilmente ti, con adresso IP $1) o l'ha domandòu l'invio de 'na neuva poula segretta per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associòu|I utenti associæ}} a sto addresso e-mail son:\n\n$2\n\n{{PLURAL:$3|Questa poula segretta temporannia a descazziâ|Queste poule segrette temporannie descazzian}} doppo {{PLURAL:$5|un giorno|$5 giorni}}.\nTi doviesci accede e çerne una neuva poula segretta oua. \n\nSe no t'ê stæto ti a fâ a domanda, ò se ti t'hæ aregordòu a poula segretta originale e no ti veu ciù cangiâla, ti peu ignorâ sto messaggio e continuâ a deuviâ a teu vegia poula segretta.",
+       "passwordreset-emailtext-user": "L'utente $1 de {{SITENAME}} o l'ha domandou l'inandio de 'na noeuva password pe l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associou|I utenti associæ}} a questo indriçço email son:\n\n$2\n\n{{PLURAL:$3|Questa password temporannia a descazziâ|Queste password temporannie descazian}} doppo {{PLURAL:$5|un giorno|$5 giorni}}.\nTi doviesci accede e çerne una noeuva password aoa. \n\nSe no t'ê stæto ti a fâ a recesta, o se ti t'ê aregordou a password originâ e no ti voeu ciu cangiala, ti poeu ignorâ sto messaggio e continoâ a doeuviâ a to vegia password.",
        "passwordreset-emailelement": "Nomme utente: \n$1\n\nPoula segretta temporannia: \n$2",
-       "passwordreset-emailsentemail": "Se questo o l'è un addresso de posta elettronnica registròu pe-a teu utensa, alloa saiâ inviâ un'e-mail pe reimpostâ a poula segretta.",
+       "passwordreset-emailsentemail": "Se questo addresso de posta elettronnica o l'è associou a-a teu utença, alloa saiâ inviou un'e-mail pe rempostâ a poula segretta.",
+       "passwordreset-emailsentusername": "Se gh'è un adreçço de posta elettronica associou con questo nomme utente, alloa saiâ inviou una email pe rempostâ a password.",
        "passwordreset-emailsent-capture": "L'è stæto inviòu un'e-mail de reimpostaçion da poula segretta, o contegnuo o l'è riportòu chì appreuvo.",
        "passwordreset-emailerror-capture": "L'è stæto generòu un'e-mail de reimpostaçion da poula segretta, riportà chì appreuvo. L'invio {{GENDER:$2|a l'utente}} o no l'è ariêscîo: $1",
+       "passwordreset-emailsent-capture2": "L'email de rempostaçion da password {{PLURAL:$1|a l'è stæta inviâ|son stæte inviæ}}. {{PLURAL:$1|O nomme|L'elenco di nommi}} utente e password o l'è mostrou chì de sotta.",
+       "passwordreset-emailerror-capture2": "Invio de email {{GENDER:$2|a l'utente}} non ariescio: $1. {{PLURAL:$3|O nomme|L'elenco di nommi}} utente e password o l'è mostrou chì de sotta.",
+       "passwordreset-nocaller": "Un chi ciamma ti g'hæ da dâlo",
+       "passwordreset-nosuchcaller": "O ciamante o no l'existe: $1",
+       "passwordreset-ignored": "A reimpostaçion da password a no l'è stæta gestia. Foscia n'è stæto configuou nisciun provider ?",
+       "passwordreset-invalideamil": "Addresso e-mail non vallido",
+       "passwordreset-nodata": "No è stæto fornio ni un nomme utente ni un adreçço de posta elettronica",
        "changeemail": "Cangia o elimmina l'adresso e-mail",
        "changeemail-header": "Completa sto formulaio pe cangiâ o to adresso e-mail. Se ti veu rimeuve l'associaçion de quasesegge addresso e-mail da-a teu utensa, lascia io neuvo addresso e-mail veuo quande ti invii o formulaio.",
        "changeemail-passwordrequired": "Saiâ necessaio insei a poula segretta pe confermâ a modiffica.",
        "minoredit": "Cangiamento minô (m)",
        "watchthis": "Metti sotta oservaçion",
        "savearticle": "Sarva a pàgina",
+       "savechanges": "Sarva e modiffiche",
+       "publishpage": "Pubbrica a paggina",
+       "publishchanges": "Pubbrica e modiffiche",
        "preview": "Anteprìmma",
        "showpreview": "Veddi l'anteprimma",
        "showdiff": "Veddi i cangiamenti",
        "accmailtext": "Una poula segretta generâ abrettio pe [[User talk:$1|$1]] a l'è stæta mandâ a $2.\n\nSta poula segretta a peu ese cangiâ inta paggina pe ''[[Special:ChangePassword|cangiâ a poula segretta]]'' subbito doppo l'accesso.",
        "newarticle": "(Nêuvo)",
        "newarticletext": "Sto colegaménto o corisponde a 'na pàgina ch'a no l'existe ancon.\n\nSe se vêu creâ a pàgina òua, se pêu comensâ a scrive into spàçio chì sotta.\n(amia e [$1 paggine d'agiûtto] pe ciû informaçioìn).\n\nSe t'ê intròu chì pe sballio,  sciacca '''Inderê''' into navegatô.",
-       "anontalkpagetext": "----\n''Sta chì a l'è a paggina de discuscion de un utente anonnimo, ch'o no l'ha ancon creou un'utensa o comunque o no a doeuvia oua. Pe identificâlo l'è quindi necessaio doeuviâ o nummero do so adresso IP. I adresci IP poeuan però ese condivixi da ciù utenti. Se t'ê un utente anonimo e ti ritegni che i commenti inte sta pagina no se riferiscian a ti, [[Special:CreateAccount|crea una noeuva utensa]] o donque [[Special:UserLogin|intra con quella che ti g'hæ za]] pe evitâ de chì avanti de ese confuzo con di atri utenti anonnimi .''",
-       "noarticletext": "Po-u momento a pagina çercâ a l'è vêua. L'è poscibbile [[Special:Search/{{PAGENAME}}|çercâ 'sto tittolo]] inte âtre pagine do scîto opû [{{fullurl:{{FULLPAGENAME}}|action=edit}} cangiâ a pagina òua].",
+       "anontalkpagetext": "----\n<em>Sta chì a l'è a paggina de discuscion de un utente anonnimo, ch'o no l'ha ancon creou un'utensa o comunque o no a doeuvia oua.</em> Pe identificâlo l'è quindi necessaio doeuviâ o nummero do so adresso IP. I adresci IP poeuan però ese condivixi da ciù utenti. Se t'ê un utente anonnimo e ti ritegni che i commenti inte sta pagina no se riferiscian a ti, [[Special:CreateAccount|crea una noeuva utensa]] o donque [[Special:UserLogin|intra con quella che ti g'hæ za]] pe evitâ de chì avanti de ese confuzo con di atri utenti anonnimi .",
+       "noarticletext": "Po-u momento a pagina çercâ a l'è vêua. Ti poeu [[Special:Search/{{PAGENAME}}|çercâ sto tittolo]] inti atre pagine do scito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} çercâ inti registri correlæ] oppû [{{fullurl:{{FULLPAGENAME}}|action=edit}} creâ questa pagina]</span>.",
        "noarticletext-nopermission": "Òua a pàgina çercâ a l'è vêua. L'è poscìbile [[Special:Search/{{PAGENAME}}|çercâ sto tìtolo]] inte di âtre pàgine do scîto o <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} çercâ inti registri corelæ]</span>, ma no ti gh'hæ i outorizzaçioin pe creâ sta paggina.",
        "missing-revision": "La verscion #$1 da paggina \"{{FULLPAGENAME}}\" a no l'esiste.\n\nQuesto succede solitamente se inta stoia ti sciacchi un vegio ingancio a una paggina scassâ.\n\nI dettaggi peuan ese attrovæ into [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de scançellaçioin].",
        "userpage-userdoesnotexist": "L'utensa \"$1\" a no corisponde a un utente registròu.\nTi veu davei creâ o modificâ sta paggina?",
        "previewnote": "'''Questa chì a l'è solo 'n'anteprimma; i cangiamenti no son ancon stæti sarvæ!'''",
        "continue-editing": "Vanni a l'area de modiffica",
        "previewconflict": "L'anteprimma a mostra o scrito presente inta casella de modiffica de d'ato coscì comme o l'apaiâ se ti çerni de sarvalo òua.",
-       "session_fail_preview": "'''N'emmo posciuo elaborâ a modiffica a caosa da pèrdia di dæti relativi a-a sescion.'''\nRitenta.\nSe-o problema o persciste, preuva a [[Special:UserLogout|sciortî]] e a intrâ torna.'''",
+       "session_fail_preview": "Spiaxenti. No è støto poscibile elaboâ a modifica perché son andæti persci i dæti relativi a-a sescion.\n\nFoscia t'ê stæto disconnesso. <strong>Verifica d'ese ancon collegou e riproeuva</strong>.\nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegate]] e effettuâ un nuoeuvo accesso, controllando che o to browser o l'açette i cookie da questo scito.",
+       "session_fail_preview_html": "Spiaxenti. No è stæto poscibbile elaboâ a modifica perché son anæti persci i dæti relativi a-a sescion.\n\n<em>Scicomme {{SITENAME}} o g'ha de l'HTML sgroeuzzo attivou e gh'è stæto una perdia di dæti da sescion, l'anteprimma a l'è ascosa comme precaoçion contra i attacchi JavaScript.</em>\n\n<strong>Se se tratta de un normale tentativo d'anteprimma, riproeuva.</strong> \nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegati]] e effettuâ un noeuvo accesso, controllando che o to browser o l'açette i cookie da questo scito.",
+       "token_suffix_mismatch": "'''A modiffica a no l'è stæta sarvâ perché o to client o l'ha mostrou de gestî in moddo errou i carattei de puntezatua into token associou a-a mæxima. Pe evitâ una poscibile corruçion do testo da pagina, l'è stæto refuou l'intrega modiffica. Questa scituaçion a poeu veificase, de votte, quande s'adoeuvia di serviççi de proxy anonnimi via web che presentan di bug.'''",
        "edit_form_incomplete": "'''De parte do formulaio de modiffica n'han razonto o server; controlla che e modiffiche seggian intatte e ripreuva.'''",
        "editing": "Modiffica de $1",
        "creating": "T'ê apreuvo a creâ $1",
        "editingold": "<strong>Attençion: t'ê apreuvo a modificâ una verscion non aggiornâ da paggina.</strong>\nSarvandola coscì, tutti i cangi fæti doppo sta verscion saian sorvescriti.",
        "yourdiff": "Differense",
        "copyrightwarning": "Nota: Tùtte e contribuçioìn a {{SITENAME}} van conscideræ comme rilasciæ drento a-i termini da licensa d'ûso $2 (veddi $1 pe savéine de ciù).\nSe no ti veu che i testi teu pêuan esse modificæ da quarchedùn sensa limitaçioìn, no mandâli a {{SITENAME}}.<br />\nInviando o testo ti diciâri, sott'a teu responsabilitæ, ch'o l'é stæto scrîto da ti personalmente oppure ch'o l'é stæto piggiòu da 'na fonte de pùbrico domìnio òu anàlogamente lìbea.<br />\n'''NO INVI MATERIÂLE COVERTO DA DRÎTI D'AUTÔ SENSA OUTORIZAÇION!'''",
+       "copyrightwarning2": "Pe piaxei tegni presente che tutti i contributi a {{SITENAME}} poeuan ese modificæ, alteræ ò scassæ da di atri contributoî.\nSe no ti voeu che se faççe ravaxo di to testi, alloa no stali manco a mette.<br />\nInviando o testo ti deciæi ascì, sotta a to responsabilitæ, ch'o l'è stæto scrito da ti personalmente oppù ch'o l'è stæto copiou da 'na fonte de pubbrico dominnio o scimilemente libera (vedi $1 pe dettaggi).\n'''No stanni a inviâ do mateiâ protetto da-o drito d'aotô sença aotorizzaçion!'''",
        "editpage-cannot-use-custom-model": "O modello do contegnuo de sta paggina o no peu ese modificòu.",
        "longpageerror": "'''Errô: o scrito inviou o l'è longo {{PLURAL:$1|1|$1}} kilobyte, ciu che-o mascimo consentio de ({{PLURAL:$2|1|$2}} kilobyte).'''\nNo se peu sarvâ.",
+       "readonlywarning": "<strong>Attençion</strong>: o database o l'è bloccou pe manutençion, no l'è momentaniamente poscibile sarvâ e modifiche effettuæ.\nPe no perdile, coppile in te'n file de testo e sarvilo in atteisa do sbrocco do database.\n\nL'amministratô de scistema ch'o l'ha misso l'abrocco o l'ha fornio questa spiegaçion: $1.",
        "protectedpagewarning": "'''Attençion: questa paggina a l'è stæta bloccâ de moddo che solo i utenti co-i privileggi d'amministratô possan modificala.'''\nL'urtimo elemento do registro o l'è riportou chì appreuvo pe referença:",
        "semiprotectedpagewarning": "'''Notta:''' Questa paggina a l'è stæta bloccä de moddo che solo i utenti registræ possan modificâla.\nL'urtimo elemento do registro o l'è riportou chì appreuvo pe referensa:",
        "cascadeprotectedwarning": "'''Attençion:''' Questa paggina a l'è stæta bloccâ de moddo che solo i utenti co-i privileggi d'amministratô possan modificala da-o momento ch'a l'é inclusa seleçionando a proteçion \"ricorsciva\" {{PLURAL:$1|inta paggina|inte paggine}}:",
        "permissionserrors": "No ti g'hæ o permisso",
        "permissionserrorstext": "No ti g'hæ i permissi pe fâlo, pe  {{PLURAL:$1|questa raxon|queste raxoin}}:",
        "permissionserrorstext-withaction": "No ti g'hæ i permìssi pe $2 pe {{PLURAL:$1|sta raxon|ste raxoìn}}:",
-       "contentmodelediterror": "No ti peu modificâ sta verscion da-o momento che o so modello de contegnuo o l'è <code>$1</code>, mentre o corrente modello de contegnuo da paggina o l'è <code>$2</code>.",
+       "contentmodelediterror": "No ti peu modificâ sta verscion da-o momento che o so modello de contegnuo o l'è <code>$1</code>, ch'o diffeisce da-o corrente modello de contegnuo da paggina <code>$2</code>.",
        "recreate-moveddeleted-warn": "Atençión: ti stæ pe ricreâ 'na pàgina zà scancelâ into passòu.'''\n\nConsciddera se l'è o caxo de continoâ  a cangiâ 'sta pàgina.\nPe comoditæ e cancellaçioìn e i stramui son pubricæ chì sotta:",
        "moveddeleted-notice": "Sta pàgina a l'é stæta scancelâ.\nA lista de scancelaçioìn e di stramui son riportæ chi de sotta pe informaçión.",
        "moveddeleted-notice-recent": "Spiaxenti, sta paggina a l'è stæta scassâ reçentemente (inte urtime 24 oe).\n\nE açioin de cançellaçion e spostamento pe questa paggina son disponibile chì appreuvo pe referença.",
        "content-model-javascript": "JavaScript",
        "content-json-empty-object": "Ogetto veuo",
        "content-json-empty-array": "Array veuo",
+       "deprecated-self-close-category": "Paggine ch'adoeuvian di tag HTML aoto-ciosi non vallidi",
+       "deprecated-self-close-category-desc": "A pagina a conten di tag HTML aoto-ciosi non vallidi, comme <code>&lt;b/></code> o <code>&lt;span/></code>. O comportamento de questi fito o cangiâ pe ese coerente co-e specifiche HTML5, pe questo o so utilizzo into wikitesto o l'è deprecou.",
        "duplicate-args-warning": "<strong>Avvertensa:</strong> [[:$1]] o ciamma [[:$2]] con ciù de un valô pe-o parammetro \"$3\".  Solo l'urtimo valô fornio o saiâ deuviou.",
        "duplicate-args-category": "Paggine che ciamman i template deuviando di parammetri duplicæ",
        "duplicate-args-category-desc": "A paggina a conten de ciamæ a di template ch'adeuvian di argomenti duplicæ, comme presempio <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ò <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "revdelete-selected-text": "{{PLURAL:$1|Verscion seleçionâ|Verscioin seleçionæ}} de [[:$2]]:",
        "revdelete-selected-file": "{{PLURAL:$1|Verscion seleçionata|Verscioin seleçionæ}} do file [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Evento do registro seleçionou|Eventi do registro seleçionou}}:",
+       "revdelete-text-text": "E verscioin scassæ apparian ancon inta cronologia da pagina, ma parte do so contegnuo o saiâ inaccescibile a-o pubbrico.",
+       "revdelete-text-file": "E verscioin di file scassæ apparian ancon inta cronologia do file, ma parte do so contegnuo a saiâ inaccescibile a-o pubbrico.",
+       "logdelete-text": "I eventi scassæ apparian ancon inti registri, ma 'na parte do so contegnuo a saiâ inaccescibile a-o pubbrico.",
+       "revdelete-text-others": "Di atri amministratoî saian ancon in grou de accede a-i contegnui ascoxi e porian ripristinali, se no l'è stæto impostou de restriçioin azontive.",
+       "revdelete-confirm": "Pe piaxei conferma che questo l'è quante t'intendi fâ, che t'accapisci e conseguençe, e che ti o fæ into rispetto de [[{{MediaWiki:Policy-url}}|linnie guidda]].",
+       "revdelete-suppress-text": "A rimoçion a saiæ da doeuviâ '''solo''' che inti seguenti caxi:\n* informaçioin potençialmente diffamatoie\n* dæti personæ inoportun\n*: ''indiriççi, nummeri de teleffono, coddiçi fiscali, ecc.''",
        "revdelete-legend": "Imposta e limitaçioin de vixibilitæ:",
        "revdelete-hide-text": "Testo da verscion",
        "revdelete-hide-image": "Ascondi i contegnui do file",
        "revdelete-unsuppress": "Elimmina e limitaçioin in scê verscioin ripristinæ",
        "revdelete-log": "Raxon:",
        "revdelete-submit": "Applica {{PLURAL:$1|a-a verscion seleçionâ|a-e verscioin seleçionæ}}",
-       "revdelete-success": "'''Vixibilitæ da verscion agiornâ con successo.'''",
+       "revdelete-success": "'''Vixibilitæ da verscion agiornâ.'''",
        "revdelete-failure": "'''A vixibilitæ da verscion a no peu ese agiornâ:'''\n$1",
-       "logdelete-success": "'''Registro de vixibilitæ impostou con successo.'''",
+       "logdelete-success": "'''Registro de vixibilitæ impostou.'''",
        "logdelete-failure": "No s'è posciuo impostâ o registro de vixibilitæ: $1",
        "revdel-restore": "càngia a vixibilitæ",
        "pagehist": "Stoia da paggina",
        "revdelete-modify-no-access": "Imposcibbile modificâ l'ogetto con dæta $1 $2 in quanto o l'è stæto identificou comme \"riservou\" e no se dispon-e do relativo accesso.",
        "revdelete-modify-missing": "Imposcibbile modificâ l'ogetto con ID $1: into database o no gh'è!",
        "revdelete-no-change": "'''Attençion:''' l'ogetto con dæta $1 $2 o l'aveiva zà e impostaçioin de vixibilitæ domandæ.",
+       "revdelete-concurrent-change": "Imposcibile modificâ l'ogetto con dæta $1, $2: pâ che o so stato o segge stæto modificou da un atro utente dementre che ti ti çercavi de modificalo.",
+       "revdelete-only-restricted": "Errô inte l'asconde l'ogetto datou $1, $2: no ti poeu asconde i ogetti a-i amministratoî sença seleçionâ un-a di atre opçioin de vixibilitæ.",
        "revdelete-reason-dropdown": "* Raxoin ciù comun-e pe-o scassamento\n** Violaçion do drito d'outô\n** Commenti ò informaçioin personæ inappropiæ\n** Nomme utente inappropiou\n** Informaçion potençialmente diffamatoia",
        "revdelete-otherreason": "Un atro motivo:",
        "revdelete-reasonotherlist": "Un'atra raxon",
        "revdelete-edit-reasonlist": "Modiffica e raxoin do scassamento",
        "revdelete-offender": "Aotô da verscion:",
        "suppressionlog": "Registro de sopprescioin",
+       "suppressionlogtext": "De sotta gh'è 'na lista de scassatue e di brocchi con do contegnuo ascoso a-i amministratoî.\nAmia a [[Special:BlockList|lista di blocchi]] pe l'elenco di bandi e di blocchi attivi a-o momento.",
        "mergehistory": "Union de stoie da paggina",
+       "mergehistory-header": "Questa pagina a consente de unî e verscioin d'a cronologia de 'na pagina sorgente a 'na pagina ciù reçente.\nAsseguite che sto cangio o mantegne a continuitæ storica da pagina.",
        "mergehistory-box": "Unisci a stoia de doe paggine:",
        "mergehistory-from": "Paggina d'origgine:",
        "mergehistory-into": "Paggina de destinaçion:",
        "mergehistory-empty": "Nisciun-a verscion da unî.",
        "mergehistory-done": "{{PLURAL:$3|Una verscion de $1 a l'è stæta unia|$3 vercsioin de $1 son stæte unie}} a-a stoia de [[:$2]].",
        "mergehistory-fail": "Imposcibbile unî e stoie. Verificâ a paggina e i parammetri temporali.",
+       "mergehistory-fail-bad-timestamp": "O timestamp o no l'è vallido.",
+       "mergehistory-fail-invalid-source": "A pagina sorgente a no l'è vallida.",
+       "mergehistory-fail-invalid-dest": "A pagina de destinaçion a no l'è vallida.",
+       "mergehistory-fail-no-change": "L'union de cronologie a no l'ha unio arcun-a verscion. Ricontrolla e pagine e i parametri temporali.",
+       "mergehistory-fail-permission": "Aotorizzaçioin insuffixente pe unî e cronologie.",
+       "mergehistory-fail-self-merge": "A paggina d'origgine e quella de destinaçion son a mæxima",
+       "mergehistory-fail-timestamps-overlap": "E verscioin d'origine se sorvepon-an ò vegnan doppo e verscioin de destinaçion.",
        "mergehistory-fail-toobig": "Imposcibbile eseguî l'union da stoia essendoghe ciu che o limmite de $1 {{PLURAL:$1|verscion|verscioin}} da mesciâ.",
        "mergehistory-no-source": "A paggina d'origgine $1 a no l'existe.",
        "mergehistory-no-destination": "A paggina de destinaçion $1 a no l'existe.",
        "powersearch-togglenone": "Nisciun",
        "powersearch-remember": "Aregordite a seleçion pe-e proscime riçerche",
        "search-external": "Riçerca esterna",
-       "searchdisabled": "La riçerca de {{SITENAME}} a no l'è attiva. Into fra tempo ti peu çercâ in sce Google. \nNotta che i seu indexi di contegnui de {{SITENAME}} porrieivan no ese aggiornæ.)",
+       "searchdisabled": "A riçerca de {{SITENAME}} a no l'è attiva. Into fratempo ti peu çercâ in sce Google. \nNotta che i so indexi di contegnui de {{SITENAME}} porrieivan no ese aggiornæ.",
        "search-error": "S'è verificou 'n errô durante a riçerca: $1",
        "preferences": "Preferençe",
        "mypreferences": "Preferençe",
        "recentchangesdays-max": "Mascimo $1 {{PLURAL:$1|giorno|giorni}}",
        "recentchangescount": "Nummero de modiffiche da mostrâ pe difetto:",
        "prefs-help-recentchangescount": "Comprende i urtime modiffiche, paggine de stoie e registri.",
+       "prefs-help-watchlist-token2": "Questa a l'è a ciave segretta pe-o feed web di to oservæ.\nChiunque a conosce saiâ in graddo de leze i to oservæ, quindi no stanni a condividdila. [[Special:ResetTokens|Clicca chi se ti g'hæ de besoeugno de rempostâla]].",
        "savedprefs": "E teu preferençe son stæte sarvæ.",
        "savedrights": "I driti utente de {{GENDER:$1|$1}} son stæti sarvæ.",
        "timezonelegend": "Fuso oraio:",
        "prefs-custom-css": "CSS personalizzou",
        "prefs-custom-js": "JavaScript personalizzou",
        "prefs-common-css-js": "CSS/JavaScript condiviso pe tutte e pelle:",
+       "prefs-reset-intro": "L'è poscibile doeuviâ sta pagina pe rimpostâ e propie preferençe a quelle predefinie do scito.\nL'opiaçion a no poeu ese annullâ.",
        "prefs-emailconfirm-label": "Conferma de l'e-mail:",
        "youremail": "Indirìsso email:",
        "username": "{{GENDER:$1|Nomme utente}}:",
        "yourvariant": "Variante da lengoa do contegnuo:",
        "prefs-help-variant": "A variante o grafia co-a quæ ti prefeisci che e paggine do wiki seggian mostræ.",
        "yournick": "Nomiagio:",
+       "prefs-help-signature": "I commenti inte pagine de discuscion devan ese firmæ con \"<nowiki>~~~~</nowiki>\" ch'o saiâ convertio inta proppia firma seguia da-a dæta.",
        "badsig": "Errô in ta firma; controlla i comandi HTML.",
        "badsiglength": "A firma scelta a l'è troppo longa.\nA non deve passâ $1 {{PLURAL:$1|carattere|caratteri}}.",
+       "yourgender": "Comme rifeise a ti?",
+       "gender-unknown": "Into mençunâte, o software o l'adoeuviâ de paole de genere noeütro ogni votta che saiâ poscibile",
        "gender-male": "O l'è registrou insce {{SITENAME}}",
        "gender-female": "A l'è registrâ insce {{SITENAME}}",
        "prefs-help-gender": "L'impostassion de sta preferensa a l'è opsionâ.\nO software o deuvia sto valô pe addressâse a ti e mensunate a-i atri deuviando o gennere grammaticale apropiou.\nQuesta informassion a saiâ pubblica.",
        "userrights": "Manezzo di driti di utenti",
        "userrights-lookup-user": "Gestisci i gruppi di utenti",
        "userrights-user-editname": "Scrivi o teu nomme utente:",
-       "editusergroup": "Modiffica i gruppi di utenti",
+       "editusergroup": "Modiffica groppi {{GENDER:$1|utente}}",
        "editinguser": "Apreuvo a cangiâ i driti de l'{{GENDER:$1|utente}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Modiffica i gruppi di utenti",
-       "saveusergroups": "Sarva i gruppi di utenti",
+       "saveusergroups": "Sarva groppi {{GENDER:$1|utente}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Membro impliçito de:",
        "userrights-reason": "Raxon:",
        "userrights-changeable-col": "Gruppi che ti peu modificâ",
        "userrights-unchangeable-col": "Gruppi che no ti peu modificâ",
        "userrights-conflict": "Conflito de modiffica di driti utente! Pe piaxei controlla e conferma e teu modiffiche.",
-       "userrights-removed-self": "T'hæ rimosso con successo i teu driti. E quindi, no ti saiæ ciù in grou de accede a questa paggina.",
+       "userrights-removed-self": "T'hæ rimosso i teu driti. E quindi, no ti saiæ ciù in grou de accede a questa paggina.",
        "group": "Gruppo:",
        "group-user": "Ûtenti",
        "group-autoconfirmed": "Utenti aotoconfermæ",
        "grouppage-sysop": "{{ns:project}}:Amministratoî",
        "grouppage-bureaucrat": "{{ns:project}}:Buroccrati",
        "grouppage-suppress": "{{ns:project}}:Soppressoî",
+       "right-read": "Leze paggine",
+       "right-edit": "Modiffica paggine",
+       "right-createpage": "Crea paggine (escluse e pagine de discuscion)",
+       "right-createtalk": "Crea pagine de discuscion",
+       "right-createaccount": "Crea noeuve utençe",
+       "right-autocreateaccount": "Accede aotomaticamente con un'utença esterna",
+       "right-minoredit": "Marca e modifiche comme menoî",
        "right-move": "Mescia e paggine",
+       "right-move-subpages": "Mescia e pagine insemme a-e relative sottopagine",
+       "right-move-rootuserpages": "Mescia e pagine prinçipæ di utenti",
+       "right-move-categorypages": "Mescia e categorie",
+       "right-movefile": "Mescia i file",
+       "right-suppressredirect": "O no crea un redirect aotomatico quande se mescia una pagina",
+       "right-upload": "Carrega file",
+       "right-reupload": "Sorvescrive un file existente",
+       "right-reupload-own": "Sorvescrive un file existente caregou da-o mæximo utente",
+       "right-reupload-shared": "Sorvescrive localmente di file presenti inte l'archivio condiviso",
+       "right-upload_by_url": "Carega un file da un addreçço URL",
+       "right-purge": "Nettezza a cache do scito sença conferma",
+       "right-autoconfirmed": "Non soggetto a-o limmite de açioin pe IP",
+       "right-bot": "Da trattâ comme processo aotomatico",
+       "right-nominornewtalk": "Fa scì che e modiffiche menoî a-e pagine de discuscion no faççan comparî l'avviso de noeuvo messaggio",
+       "right-apihighlimits": "Doeuvia di limmiti ciù erti pe-e interrogaçioin API",
        "right-writeapi": "Deuvia l'API in scrittua",
+       "right-delete": "Scassa e paggine",
+       "right-bigdelete": "Scassa e pagine con cronologie longhe",
+       "right-deletelogentry": "Scassa e ripristina voxe de registro specifiche",
+       "right-deleterevision": "Scassa e ripristina de verscion specifiche de pagine",
+       "right-deletedhistory": "Vixualizza e verscioin da cronologia scassæ, sença o so testo associou",
+       "right-deletedtext": "Vixualizza o testo scassou e-e modifiche fra de verscioin scassæ",
+       "right-browsearchive": "Çerca inte pagine scassæ",
+       "right-undelete": "Recuppera una paggina",
+       "right-suppressrevision": "Vedde, asconde e ripristina de verscioin specifiche de pagine a quæ-se-segge utente",
+       "right-viewsuppressed": "Vedde de verscioin ascose a qua-se-sæ utente",
+       "right-suppressionlog": "Vixualizza i registri privæ",
+       "right-block": "Blocca e modifiche di atri utenti",
+       "right-blockemail": "Impedisce a un utente de mandâ de email",
+       "right-hideuser": "Blocca un nomme utente, ascondendolo a-o pubbrico",
+       "right-ipblock-exempt": "Ignora i blocchi di IP, i blocchi aotomatichi e i blocchi de range de IP",
+       "right-unblockself": "Sblocca lê mæximo",
+       "right-protect": "Camgia i livelli de proteçion e modifica e pagine protette ricorscivamente",
+       "right-editprotected": "Modifica e pagine protette con \"{{int:protect-level-sysop}}\"",
+       "right-editsemiprotected": "Modifica e pagine protette con \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Modifica o modello de contegnuo de 'na paggina",
+       "right-editinterface": "Modiffica l'interfaccia utente",
+       "right-editusercssjs": "Modifica i file CSS e JS di atri utenti",
+       "right-editusercss": "Modifica i file CSS di atri utenti",
+       "right-edituserjs": "Modiffica i file JS di atri utenti",
+       "right-editmyusercss": "Modifica o file CSS do proppio utente",
+       "right-editmyuserjs": "Modifica o file JavaScript do proppio utente",
+       "right-viewmywatchlist": "Vixualizza i proppi osservæ speçiali",
+       "right-editmywatchlist": "Modifica i to osservæ speçiali. Da notâ che arcun-e açioin porian ancon azonze de pagine anche sença questo drito.",
+       "right-viewmyprivateinfo": "Vixualizza i proppi dæti personæ (presempio: adreçço email, nomme veo)",
+       "right-editmyprivateinfo": "Modiffica i proppi dæti personæ (presempio: addreçço email, nomme veo)",
+       "right-editmyoptions": "Modiffica e proppie preferençe",
+       "right-rollback": "Annulla rapidamente e modifiche de l'urtimo utente ch'o l'ha modificou una pagina in particolâ",
+       "right-markbotedits": "Marca e modifiche soggette a rollback comme effettuæ da bot",
+       "right-noratelimit": "No soggetto a-o limmite de açioin",
+       "right-import": "Importa de pagine da di atri wiki",
+       "right-importupload": "Importa paggine da un upload de file",
+       "right-patrol": "Marca e modifiche di altri utenti comme controllæ",
+       "right-autopatrol": "Marca aotomaticamente e proppie modifiche comme controllæ",
+       "right-patrolmarks": "Doeuvia a fonçion de verifica di urtime modiffiche",
+       "right-unwatchedpages": "Vixualizza una lista de pagine non öservæ",
+       "right-mergehistory": "Unisce a cronologia de paggine",
+       "right-userrights": "Modiffica tutti i driti de l'utente",
+       "right-userrights-interwiki": "Modiffica i driti di utenti insce di atre wiki",
+       "right-siteadmin": "Abbrocca e sbrocca o database",
+       "right-override-export-depth": "Esporta e paggine includendo e pagine collegæ scin a 'na profonditæ de 5",
+       "right-sendemail": "Manda de email a di atri utenti",
+       "right-passwordreset": "Vedde i messaggi de rempostaçion da password",
+       "right-managechangetags": "Crea e attiva/disattiva i [[Special:Tags|etichette]]",
+       "right-applychangetags": "Apprica di [[Special:Tags|etichette]] a-e proppie modiffiche",
+       "right-changetags": "Azonze e leva de specifiche [[Special:Tags|etichette]] insce scingole verscioin o voxe de registro",
        "newuserlogpage": "Nêuvi utenti",
        "rightslog": "Diritti d'ûtente",
        "action-edit": "càngia sta pàgina",
        "whatlinkshere-next": "{{PLURAL:$1|sûccescivo|sûccescivi $1}}",
        "whatlinkshere-links": "← colegaménti",
        "whatlinkshere-hideredirs": "$1 i rendirissamenti",
-       "whatlinkshere-hidetrans": "$1 Incluxon",
+       "whatlinkshere-hidetrans": "$1 Incluxoin",
        "whatlinkshere-hidelinks": "$1 colegaménti",
        "whatlinkshere-hideimages": "$1 colegaménti di file",
        "whatlinkshere-filters": "Filtri",
        "thumbnail_error": "Errô inta creassion da miniatûa: $1",
        "thumbnail_invalid_params": "Parametri da a imàginetta non validi",
        "importlogpage": "Importassioîn",
-       "tooltip-pt-userpage": "A teu pagina utilizatô",
-       "tooltip-pt-mytalk": "E mæ discûscioîn",
-       "tooltip-pt-preferences": "E mæ preferense",
+       "tooltip-pt-userpage": "A {{GENDER:|to}} pagina utente",
+       "tooltip-pt-mytalk": "A {{GENDER:|to}} paggina de discuscion",
+       "tooltip-pt-preferences": "E {{GENDER:|to}} preferençe",
        "tooltip-pt-watchlist": "A lista de pagine che ti g'hæ sotta osservaçion",
-       "tooltip-pt-mycontris": "E mæ contribuçioìn",
+       "tooltip-pt-mycontris": "A lista de {{GENDER:|to}} contribuçioin",
        "tooltip-pt-login": "Consegemmo a registraçión, ma a no l'è obrigatoia.",
        "tooltip-pt-logout": "Sciorti",
        "tooltip-pt-createaccount": "Se conseggia de registrase e de intrâ, sciben che no segge obligatoio",
        "tooltip-t-whatlinkshere": "Lista de tùtte e pagine che son colegæ a sta chì.",
        "tooltip-t-recentchangeslinked": "Ùrtimi càngi de pàgine colegæ a quésta",
        "tooltip-feed-atom": "Feed Atom pe sta pàgina",
-       "tooltip-t-contributions": "Lista de contribûssioîn de quest'utente",
+       "tooltip-t-contributions": "Lista de contribûssioin de {{GENDER:$1|questo|questa}} utente",
        "tooltip-t-emailuser": "Invia 'n messaggio e-mail a quest'utente",
        "tooltip-t-upload": "Carrega di file murtimediali",
        "tooltip-t-specialpages": "Lista de tùtte e pagine speçiâli",
index 0909fd5..93bed08 100644 (file)
        "protect-otherreason": "Anna/ytterlegare årsak:",
        "protect-otherreason-op": "Anna årsak",
        "protect-dropdown": "*Vanlege verneårsaker\n** Gjenteke hærverk\n** Gjenteke spam\n** Endringskrig\n** Side med mange vitjande",
-       "protect-edit-reasonlist": "Endrar verneårsaker",
+       "protect-edit-reasonlist": "Endra verneårsaker",
        "protect-expiry-options": "1 time:1 hour,1 dag:1 day,1 veke:1 week,2 veker:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,endelaus:infinite",
        "restriction-type": "Tilgang:",
        "restriction-level": "Avgrensingsnivå:",
index c710e22..50d1937 100644 (file)
        "notvisiblerev": "Версия была удалена",
        "watchlist-details": "В вашем списке наблюдения $1 {{PLURAL:$1|страница|страницы|страниц}}, не считая страниц обсуждений.",
        "wlheader-enotif": "Уведомления по эл. почте включены.",
-       "wlheader-showupdated": "Страницы, изменившиеся с вашего последнего их посещения, выделены '''жирным''' шрифтом.",
+       "wlheader-showupdated": "СÑ\82Ñ\80аниÑ\86Ñ\8b, Ð¸Ð·Ð¼ÐµÐ½Ð¸Ð²Ñ\88иеÑ\81Ñ\8f Ñ\81 Ð²Ð°Ñ\88его Ð¿Ð¾Ñ\81леднего Ð¸Ñ\85 Ð¿Ð¾Ñ\81еÑ\89ениÑ\8f, Ð²Ñ\8bделенÑ\8b '''полÑ\83жиÑ\80нÑ\8bм''' Ñ\88Ñ\80иÑ\84Ñ\82ом.",
        "wlnote": "Ниже {{PLURAL:$1|показано последнее изменение|показаны <strong>$1</strong> последние изменения|показаны <strong>$1</strong> последних изменений}} за {{PLURAL:$2|последний час|последние <strong>$2</strong> часа|последние <strong>$2</strong> часов}}, по состоянию на $3 $4.",
        "wlshowlast": "Показать за последние $1 часов $2 дней",
        "watchlist-hide": "Скрыть",
index df4b1a5..7c78a48 100644 (file)
        "grant-createeditmovepage": "Прављење, уређивање и премештање страница",
        "grant-delete": "Брисање страница, измена и уноса у дневницима",
        "grant-editinterface": "Уређивање Медијавики именског простора и корисничких CSS/JavaScript страница",
+       "grant-editmycssjs": "Уређивање вашег CSS/JavaScript-а",
        "grant-editmyoptions": "Уређивање ваших подешавања",
        "grant-editmywatchlist": "Уређивање вашег списка надгледања",
        "grant-editpage": "Уређивање постојећих страница",
        "grant-editprotected": "Уређивање заштићених страница",
+       "grant-patrol": "Патролирање измена",
        "grant-protect": "Закључавање и откључавање страница",
        "grant-rollback": "Враћање измена",
+       "grant-sendemail": "Слање имејлова другим корисницима",
        "grant-uploadeditmovefile": "Отпремање, замена и премештање датотека",
        "grant-uploadfile": "Слање нових датотека",
        "grant-basic": "Основна права",
index 3c01f7e..4edc5d8 100644 (file)
        "enhancedrc-history": "歷史",
        "recentchanges": "近段辰光个改动",
        "recentchanges-legend": "近段辰光个改动选项",
-       "recentchanges-summary": "登该个页面浪跟踪最近对维基百科个改动。",
+       "recentchanges-summary": "登该个页面浪跟踪最近对本站个改动。",
        "recentchanges-feed-description": "跟踪此订阅垃拉 wiki 高头个最近更改。",
        "recentchanges-label-newpage": "箇编辑建立着新页",
        "recentchanges-label-minor": "箇是小编写",
index dab710e..42f8f05 100644 (file)
        "content-model-css": "CSS",
        "content-json-empty-object": "空物件",
        "content-json-empty-array": "空陣列",
+       "deprecated-self-close-category": "使用無效 Self-closed HTML 標籤的頁面",
+       "deprecated-self-close-category-desc": "頁面包含無效的 Self-closed HTML 標籤,如 <code>&lt;b/></code> or <code>&lt;span/></code>。這些標籤的模式將會更改為與 HTML5 規格一致,因此 wikitext 的這種用法已停用。",
        "duplicate-args-warning": "<strong>警告:</strong> [[:$1]] 呼叫 [[:$2]] 的 \"$3\" 參數使用了超過一次,僅會使用提供的最後一個參數值。",
        "duplicate-args-category": "模板呼叫時使用重複的參數的頁面",
        "duplicate-args-category-desc": "該頁面包含重複使用參數的模板呼叫,如 <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> 或 <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>。",
index f4a03d8..d6d2b29 100644 (file)
@@ -27126,3 +27126,17 @@ Empty LI (T49673)
 <li>b</li>
 </ul>
 !! end
+
+!! test
+Thumbnail output
+!! wikitext
+[[File:Thumb.png|thumb]]
+!! html/php+tidy
+<div class="thumb tright">
+<div class="thumbinner" style="width:137px;"><a href="/wiki/File:Thumb.png" class="image"><img alt="Thumb.png" src="http://example.com/images/e/ea/Thumb.png" width="135" height="135" class="thumbimage" /></a>
+<div class="thumbcaption">
+<div class="magnify"><a href="/wiki/File:Thumb.png" class="internal" title="Enlarge"></a></div>
+</div>
+</div>
+</div>
+!! end
index 4721793..e44db83 100644 (file)
@@ -738,6 +738,16 @@ class HtmlTest extends MediaWikiTestCase {
                                '1x.png 1x, 1_5x.png 1.5x, 2x.png 2x',
                                'pixel depth keys may omit a trailing "x"'
                        ],
+                       [
+                               [ '1'  => 'small.png', '1.5' => 'large.png', '2'  => 'large.png' ],
+                               'small.png 1x, large.png 1.5x',
+                               'omit larger duplicates'
+                       ],
+                       [
+                               [ '1'  => 'small.png', '2'  => 'large.png', '1.5' => 'large.png' ],
+                               'small.png 1x, large.png 1.5x',
+                               'omit larger duplicates in irregular order'
+                       ],
                ];
        }
 
index 2d2e726..030d9d5 100644 (file)
@@ -2444,10 +2444,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        public function testUpdateNotificationTimestamp_watchersExist() {
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->with(
-                               [ 'watchlist' ],
-                               [ 'wl_user' ],
+                               'watchlist',
+                               'wl_user',
                                [
                                        'wl_user != 1',
                                        'wl_namespace' => 0,
@@ -2455,18 +2455,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                        'wl_notificationtimestamp IS NULL'
                                ]
                        )
-                       ->will(
-                               $this->returnValue( [
-                                       $this->getFakeRow( [ 'wl_user' => '2' ] ),
-                                       $this->getFakeRow( [ 'wl_user' => '3' ] )
-                               ] )
-                       );
-               $mockDb->expects( $this->once() )
-                       ->method( 'onTransactionIdle' )
-                       ->with( $this->isType( 'callable' ) )
-                       ->will( $this->returnCallback( function( $callable ) {
-                               $callable();
-                       } ) );
+                       ->will( $this->returnValue( [ '2', '3' ] ) );
                $mockDb->expects( $this->once() )
                        ->method( 'update' )
                        ->with(
@@ -2502,10 +2491,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        public function testUpdateNotificationTimestamp_noWatchers() {
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->with(
-                               [ 'watchlist' ],
-                               [ 'wl_user' ],
+                               'watchlist',
+                               'wl_user',
                                [
                                        'wl_user != 1',
                                        'wl_namespace' => 0,
@@ -2516,8 +2505,6 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        ->will(
                                $this->returnValue( [] )
                        );
-               $mockDb->expects( $this->never() )
-                       ->method( 'onTransactionIdle' );
                $mockDb->expects( $this->never() )
                        ->method( 'update' );
 
@@ -2551,19 +2538,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
                        ) );
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->will(
-                               $this->returnValue( [
-                                       $this->getFakeRow( [ 'wl_user' => '2' ] ),
-                                       $this->getFakeRow( [ 'wl_user' => '3' ] )
-                               ] )
+                               $this->returnValue( [ '2', '3' ] )
                        );
-               $mockDb->expects( $this->once() )
-                       ->method( 'onTransactionIdle' )
-                       ->with( $this->isType( 'callable' ) )
-                       ->will( $this->returnCallback( function( $callable ) {
-                               $callable();
-                       } ) );
                $mockDb->expects( $this->once() )
                        ->method( 'update' );
 
index 80efcb3..7f9a772 100644 (file)
@@ -30,6 +30,32 @@ class XmlTypeCheckTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( $testXML->wellFormed );
        }
 
+       /**
+        * Verify we check for recursive entity DOS
+        *
+        * (If the DOS isn't properly handled, the test runner will probably go OOM...)
+        */
+       public function testRecursiveEntity() {
+               $xml = <<<'XML'
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE foo [
+       <!ENTITY test "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
+       <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+       <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+       <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+       <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+       <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+       <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+       <!ENTITY g "-00000000000000000000000000000000000000000000000000000000000000000000000-">
+]>
+<foo>
+<bar>&test;</bar>
+</foo>
+XML;
+               $check = XmlTypeCheck::newFromString( $xml );
+               $this->assertFalse( $check->wellFormed );
+       }
+
        /**
         * @covers XMLTypeCheck::processingInstructionHandler
         */
index ea86535..be7fe91 100644 (file)
@@ -108,7 +108,7 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ExtensionProcessor::extractConfig
+        * @covers ExtensionProcessor::extractConfig1
         */
        public function testExtractConfig() {
                $processor = new ExtensionProcessor;
index 7b94e40..aa43ac7 100644 (file)
@@ -15,6 +15,7 @@ class BalancerTest extends MediaWikiTestCase {
                        'strict' => false, /* not strict */
                        'allowedHtmlElements' => null, /* no sanitization */
                        'tidyCompat' => false, /* standard parser */
+                       'allowComments' => true, /* comment parsing */
                ] );
        }
 
@@ -70,9 +71,12 @@ class BalancerTest extends MediaWikiTestCase {
                                // Normalize case of SVG attributes.
                                $html = str_replace( 'foreignObject', 'foreignobject', $html );
 
-                               if ( isset( $case['document']['props']['comment'] ) ) {
-                                       // Skip tests which include HTML comments, which
-                                       // the balancer requires to have been stripped.
+                               if (
+                                       isset( $case['document']['props']['comment'] ) &&
+                                       preg_match( ',<!--[^>]*<,', $html )
+                               ) {
+                                       // Skip tests which include HTML comments containing
+                                       // the < character, which we don't support.
                                        continue;
                                }
                                if ( strpos( $case['data'], '<![CDATA[' ) !== false ) {
@@ -83,18 +87,16 @@ class BalancerTest extends MediaWikiTestCase {
                                        // Skip tests involving doctypes.
                                        continue;
                                }
-                               if ( preg_match( ',</?(html|head|body|frame|plaintext)>|<rdar:,i', $case['data'] ) ) {
+                               if ( preg_match( ',</?(html|head|body|frame|plaintext)>|<rdar:|<isindex,i', $case['data'] ) ) {
                                        // Skip tests involving some literal tags, which are
                                        // unsupported but don't show up in the expected output.
                                        continue;
                                }
                                if (
-                                       isset( $case['document']['props']['tags']['form'] ) ||
                                        isset( $case['document']['props']['tags']['iframe'] ) ||
                                        isset( $case['document']['props']['tags']['noembed'] ) ||
                                        isset( $case['document']['props']['tags']['noscript'] ) ||
                                        isset( $case['document']['props']['tags']['script'] ) ||
-                                       isset( $case['document']['props']['tags']['select'] ) ||
                                        isset( $case['document']['props']['tags']['svg script'] ) ||
                                        isset( $case['document']['props']['tags']['svg title'] ) ||
                                        isset( $case['document']['props']['tags']['textarea'] ) ||