Merge "ParserTests: Add a video file to parser test suite"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 9 Jan 2016 07:30:54 +0000 (07:30 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 9 Jan 2016 07:30:54 +0000 (07:30 +0000)
86 files changed:
RELEASE-NOTES-1.27
autoload.php
composer.local.json-sample [new file with mode: 0644]
includes/Xml.php
includes/api/i18n/de.json
includes/api/i18n/te.json
includes/import/WikiRevision.php
includes/installer/Installer.php
includes/installer/WebInstallerComplete.php [new file with mode: 0644]
includes/installer/WebInstallerCopying.php [new file with mode: 0644]
includes/installer/WebInstallerDBConnect.php [new file with mode: 0644]
includes/installer/WebInstallerDBSettings.php [new file with mode: 0644]
includes/installer/WebInstallerDocument.php [new file with mode: 0644]
includes/installer/WebInstallerExistingWiki.php [new file with mode: 0644]
includes/installer/WebInstallerInstall.php [new file with mode: 0644]
includes/installer/WebInstallerLanguage.php [new file with mode: 0644]
includes/installer/WebInstallerName.php [new file with mode: 0644]
includes/installer/WebInstallerOptions.php [new file with mode: 0644]
includes/installer/WebInstallerPage.php
includes/installer/WebInstallerReadme.php [new file with mode: 0644]
includes/installer/WebInstallerReleaseNotes.php [new file with mode: 0644]
includes/installer/WebInstallerRestart.php [new file with mode: 0644]
includes/installer/WebInstallerUpgrade.php [new file with mode: 0644]
includes/installer/WebInstallerUpgradeDoc.php [new file with mode: 0644]
includes/installer/WebInstallerWelcome.php [new file with mode: 0644]
includes/installer/i18n/cs.json
includes/installer/i18n/de.json
includes/installer/i18n/en.json
includes/installer/i18n/qqq.json
includes/installer/i18n/ru.json
includes/installer/i18n/tt-cyrl.json
includes/installer/i18n/zh-hans.json
includes/libs/composer/ComposerJson.php
includes/registration/ExtensionProcessor.php
languages/FakeConverter.php
languages/Language.php
languages/LanguageConverter.php
languages/i18n/ast.json
languages/i18n/azb.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/fa.json
languages/i18n/fr.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/it.json
languages/i18n/kk-cyrl.json
languages/i18n/la.json
languages/i18n/lb.json
languages/i18n/lki.json
languages/i18n/mai.json
languages/i18n/mk.json
languages/i18n/mr.json
languages/i18n/my.json
languages/i18n/nap.json
languages/i18n/ne.json
languages/i18n/olo.json
languages/i18n/pl.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/te.json
languages/i18n/tt-cyrl.json
languages/i18n/zh-hans.json
maintenance/convertExtensionToRegistration.php
maintenance/importTextFiles.php [new file with mode: 0644]
package.json
resources/Resources.php
resources/lib/jquery/jquery.validate.js [deleted file]
resources/src/mediawiki.widgets.datetime/CalendarWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/CalendarWidget.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js [new file with mode: 0644]
resources/src/mediawiki/page/ready.js
tests/parser/parserTest.inc

index 9b12b72..32f2939 100644 (file)
@@ -53,10 +53,11 @@ production.
 * (T48998) $wgArticlePath must now be either a full url, or start with a "/".
 * $wgRateLimitLog was removed; use $wgDebugLogGroups['ratelimit'] instead.
 * Deprecated API formats dbg, txt, and yaml have been removed.
-* CLDRPluralRule* classes have been replaced with wikimedia/cldr-plural-rule-parser.
+* CLDRPluralRule* classes have been replaced with
+  wikimedia/cldr-plural-rule-parser.
 * Removed $wgProfilePerHost, $wgUDPProfilerHost, $wgUDPProfilerPort,
-  $wgUDPProfilerFormatString, $wgStatsMethod, $wgAggregateStatsID, $wgStatsFormatString,
-  and $wgProfileCallTree (deprecated since 1.20).
+  $wgUDPProfilerFormatString, $wgStatsMethod, $wgAggregateStatsID,
+  $wgStatsFormatString, and $wgProfileCallTree (deprecated since 1.20).
 * For proper operation of LocalIdLookup with shared user tables, ensure that
   $wgSharedDB and $wgSharedTables are properly set even on the "central" wiki
   that all others are sharing from and that $wgLocalDatabases is set to the
@@ -96,13 +97,18 @@ production.
   authentication extensions.
 * $wgMaxUserDBWriteDuration added to limit huge user-generated transactions.
   Regular web request transactions that takes longer than this are aborted.
-* Added a new hook, 'TitleMoveCompleting', which runs before a page move is committed.
+* Added a new hook, 'TitleMoveCompleting', which runs before a page move is
+  committed.
 * $wgCdnReboundPurgeDelay was added to provide secondary delayed purges of URLs
   from CDN to mitigate DB replication lag and WAN cache purge lag.
 * (T49162) Installer will default to setting CACHE_ACCEL as the main cache type
   if it is available.
+* It is now possible to patrol file uploads (both for new files and new versions
+  of existing files). Special:NewFiles has gained an option to filter by patrol
+  status. This functionality can be disabled using $wgUseFilePatrol.
 
 === External library changes in 1.27 ===
+
 ==== Upgraded external libraries ====
 * Updated oojs/oojs-ui from v0.12.12 to v0.13.3.
 * Updated composer/semver from v1.0.0 to v1.2.0.
@@ -163,14 +169,15 @@ changes to languages because of Phabricator reports.
   ignore the 2nd and 3rd arguments (formerly $id and $commit).
 * Removed "loaderScripts" option from ResourceLoaderFileModule class.
 * Removed ORM-like wrapper added in 1.20.
-* LinkCache::getGoodLinks and LinkCache::getBadLinks were removed (deprecated in 1.26).
+* LinkCache::getGoodLinks and LinkCache::getBadLinks were removed
+  (deprecated in 1.26).
 * WikiPage::doQuickEdit() was removed (deprecated since 1.21).
 * Removed SiteObject and SiteArray classes (deprecated in 1.21).
 * MessageBlobStore::getInstance() was removed (deprecated since 1.25).
 * (T84937) Free external links ("autolinked" urls) will now be terminated
   by &nbsp; and HTML entity encodings of &nbsp, <, and >.
-* (T36948) The default file revert message's timestamp is now in $wgLocaltimezone,
-  instead of UTC.
+* (T36948) The default file revert message's timestamp is now in
+  $wgLocaltimezone, instead of UTC.
 * The default name of the 'suppress' group page has been changed from
   'Project:Oversight' to 'Project:Suppress'.
 * DatabaseBase::resultObject() is now protected (use outside Database classes
@@ -179,7 +186,8 @@ changes to languages because of Phabricator reports.
   ResourceLoaderContext instance is deprecated.
 * ResourceLoader::getLessCompiler() now takes an optional parameter of
   additional LESS variables to set for the compiler.
-* wfBaseConvert() marked as deprecated, use Wikimedia\base_convert() directly instead.
+* wfBaseConvert() marked as deprecated, use Wikimedia\base_convert() directly
+  instead.
 * Obsolete maintenance scripts clearCacheStats.php and showCacheStats.php
   were removed. The underlying data is sent to StatsD (see $wgStatsdServer).
 * Removed msg_resource_links database table and associated code.
@@ -227,6 +235,10 @@ changes to languages because of Phabricator reports.
 * wfMsgHtml() was removed (deprecated since 1.18).
 * wfMsgWikiHtml() was removed (deprecated since 1.18).
 * wfMsgExt() was removed (deprecated since 1.18).
+* Language::armourMath() was removed (deprecated since 1.22).
+* LanguageConverter::armourMath() was removed (deprecated since 1.22).
+* FakeConverter::armourMath() was removed (deprecated since 1.22).
+* The unused jquery.validate ResourceLoader module was removed.
 
 == Compatibility ==
 
index 49a3faf..1f8c93a 100644 (file)
@@ -575,6 +575,7 @@ $wgAutoloadLocalClasses = array(
        'ImportSource' => __DIR__ . '/includes/import/ImportSource.php',
        'ImportStreamSource' => __DIR__ . '/includes/import/ImportStreamSource.php',
        'ImportStringSource' => __DIR__ . '/includes/import/ImportStringSource.php',
+       'ImportTextFiles' => __DIR__ . '/maintenance/importTextFiles.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
        'IncludableSpecialPage' => __DIR__ . '/includes/specialpage/IncludableSpecialPage.php',
        'IndexPager' => __DIR__ . '/includes/pager/IndexPager.php',
@@ -1364,24 +1365,24 @@ $wgAutoloadLocalClasses = array(
        'WatchedItem' => __DIR__ . '/includes/WatchedItem.php',
        'WatchlistCleanup' => __DIR__ . '/maintenance/cleanupWatchlist.php',
        'WebInstaller' => __DIR__ . '/includes/installer/WebInstaller.php',
-       'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerPage.php',
+       'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerComplete.php',
+       'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerCopying.php',
+       'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerDBConnect.php',
+       'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerDBSettings.php',
+       'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerDocument.php',
+       'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerExistingWiki.php',
+       'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerInstall.php',
+       'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerLanguage.php',
+       'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerName.php',
+       'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerOptions.php',
        'WebInstallerOutput' => __DIR__ . '/includes/installer/WebInstallerOutput.php',
        'WebInstallerPage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerPage.php',
+       'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerReadme.php',
+       'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerReleaseNotes.php',
+       'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerRestart.php',
+       'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerUpgrade.php',
+       'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerUpgradeDoc.php',
+       'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerWelcome.php',
        'WebPHandler' => __DIR__ . '/includes/media/WebP.php',
        'WebRequest' => __DIR__ . '/includes/WebRequest.php',
        'WebRequestUpload' => __DIR__ . '/includes/WebRequestUpload.php',
diff --git a/composer.local.json-sample b/composer.local.json-sample
new file mode 100644 (file)
index 0000000..1315afc
--- /dev/null
@@ -0,0 +1,9 @@
+{
+       "extra": {
+               "merge-plugin": {
+                       "include": [
+                               "extensions/example/composer.json"
+                       ]
+               }
+       }
+}
\ No newline at end of file
index 11f14db..63301ac 100644 (file)
@@ -91,7 +91,7 @@ class Xml {
        public static function elementClean( $element, $attribs = array(), $contents = '' ) {
                global $wgContLang;
                if ( $attribs ) {
-                       $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
+                       $attribs = array_map( array( 'UtfNormal\Validator', 'cleanUp' ), $attribs );
                }
                if ( $contents ) {
                        $contents = $wgContLang->normalize( $contents );
index d745baa..d86a1db 100644 (file)
        "apihelp-query+categorymembers-param-endsortkey": "Stattdessen $1endhexsortkey verwenden.",
        "apihelp-query+categorymembers-example-simple": "Rufe die ersten 10 Seiten von <kbd>Category:Physics</kbd> ab.",
        "apihelp-query+categorymembers-example-generator": "Rufe die Seiteninformationen zu den ersten 10 Seiten von<kbd>Category:Physics</kbd> ab.",
+       "apihelp-query+contributors-description": "Rufe die Liste der angemeldeten Bearbeiter und die Zahl anonymer Bearbeiter einer Seite ab.",
        "apihelp-query+contributors-param-limit": "Wie viele Spender zurückgegeben werden sollen.",
        "apihelp-query+contributors-example-simple": "Zeige Mitwirkende der Seite <kbd>Main Page</kbd>.",
        "apihelp-query+deletedrevisions-param-tag": "Listet nur Bearbeitungen auf, die die angegebene Markierung haben.",
        "apihelp-query+deletedrevisions-param-user": "Nur Versionen von diesem Benutzer auflisten.",
        "apihelp-query+deletedrevisions-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
+       "apihelp-query+deletedrevisions-example-revids": "Liste Informationen zur gelöschten Bearbeitung <kbd>123456</kbd>.",
+       "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Modus|Modi}}: $2",
        "apihelp-query+deletedrevs-param-start": "Der Zeitstempel bei dem die Auflistung beginnen soll.",
        "apihelp-query+deletedrevs-param-end": "Der Zeitstempel bei dem die Auflistung enden soll.",
        "apihelp-query+deletedrevs-param-from": "Auflistung bei diesem Titel beginnen.",
        "apihelp-query+deletedrevs-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
        "apihelp-query+deletedrevs-param-namespace": "Nur Seiten dieses Namensraums auflisten.",
        "apihelp-query+deletedrevs-param-limit": "Die maximale Anzahl aufzulistendender Bearbeitungen.",
+       "apihelp-query+deletedrevs-example-mode2": "Liste die letzten 50 gelöschten Beiträge von <kbd>Bob</kbd> auf (Modus 2).",
+       "apihelp-query+deletedrevs-example-mode3-main": "Liste die ersten 50 gelöschten Bearbeitungen im Hauptnamensraum (Modus 3).",
        "apihelp-query+disabled-description": "Dieses Abfrage-Modul wurde deaktiviert.",
        "apihelp-query+duplicatefiles-description": "Liste alle Dateien auf die, basierend auf der Prüfsumme, Duplikate der angegebenen Dateien sind.",
        "apihelp-query+duplicatefiles-param-limit": "Wie viele doppelte Dateien zurückgeben.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
        "apihelp-query+filearchive-param-limit": "Wie viele Bilder insgesamt zurückgegeben werden sollen.",
        "apihelp-query+filearchive-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+filearchive-param-prop": "Welche Bildinformationen abgerufen werden sollen:",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "Ergänzt die SHA-1-Prüfsumme für das Bild.",
+       "apihelp-query+filearchive-paramvalue-prop-timestamp": "Fügt einen Zeitstempel für die hochgeladene Version hinzu.",
+       "apihelp-query+filearchive-paramvalue-prop-user": "Fügt den Benutzer hinzu, der die Bildversion hochgeladen hat.",
+       "apihelp-query+filearchive-paramvalue-prop-size": "Fügt die Größe des Bilde in Bytes sowie die Höhe, Breite und (falls zutreffend) die Seitenzahl hinzu.",
        "apihelp-query+filearchive-paramvalue-prop-dimensions": "Alias für die Größe.",
+       "apihelp-query+filearchive-paramvalue-prop-description": "Fügt die Beschreibung der Bildversion hinzu.",
+       "apihelp-query+filearchive-paramvalue-prop-parseddescription": "Analysiert die Beschreibung der Version.",
+       "apihelp-query+filearchive-paramvalue-prop-mime": "Fügt den MIME-Typ des Bildes hinzu.",
        "apihelp-query+filearchive-paramvalue-prop-mediatype": "Ergänzt den Medientyp des Bildes.",
+       "apihelp-query+filearchive-paramvalue-prop-metadata": "Listet die Exif-Metadaten dieser Bildversion auf.",
        "apihelp-query+filearchive-paramvalue-prop-bitdepth": "Ergänzt die Bittiefe der Version.",
        "apihelp-query+filearchive-example-simple": "Eine Liste aller gelöschten Dateien auflisten",
        "apihelp-query+filerepoinfo-example-simple": "Ruft Informationen über Dateirepositorien ab.",
+       "apihelp-query+fileusage-param-prop": "Zurückzugebende Eigenschaften:",
        "apihelp-query+fileusage-paramvalue-prop-pageid": "Seitenkennung jeder Seite.",
        "apihelp-query+fileusage-paramvalue-prop-title": "Titel jeder Seite.",
+       "apihelp-query+fileusage-paramvalue-prop-redirect": "Markieren, falls die Seite eine Weiterleitung ist.",
+       "apihelp-query+fileusage-param-namespace": "Nur Seiten dieser Namensräume einbinden.",
        "apihelp-query+fileusage-param-limit": "Wie viel zurückgegeben werden soll.",
        "apihelp-query+imageinfo-description": "Gibt Informationen und alle Versionen der Datei zurück.",
        "apihelp-query+imageinfo-param-prop": "Welche Dateiinformationen abgerufen werden sollen:",
+       "apihelp-query+imageinfo-paramvalue-prop-timestamp": "Fügt einen Zeitstempel für die hochgeladene Version hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-user": "Fügt den Benutzer zu jeder hochgeladenen Dateiversion hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-userid": "Füge die ID des Benutzers zu jeder hochgeladenen Dateiversion hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-comment": "Kommentar zu der Version.",
+       "apihelp-query+imageinfo-paramvalue-prop-parsedcomment": "Analysiere den Kommentar zu dieser Version.",
+       "apihelp-query+imageinfo-paramvalue-prop-dimensions": "Alias für die Größe.",
+       "apihelp-query+imageinfo-paramvalue-prop-mime": "Fügt den MIME-Typ dieser Datei hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-mediatype": "Fügt den Medientyp dieser Datei hinzu.",
        "apihelp-query+imageinfo-param-limit": "Wie viele Dateiversionen pro Datei zurückgegeben werden sollen.",
        "apihelp-query+imageinfo-param-start": "Zeitstempel, von dem die Liste beginnen soll.",
        "apihelp-query+imageinfo-param-end": "Zeitstempel, an dem die Liste enden soll.",
        "apihelp-query+userinfo-paramvalue-prop-editcount": "Ergänzt den Bearbeitungszähler des aktuellen Benutzers.",
        "apihelp-query+userinfo-paramvalue-prop-realname": "Fügt den bürgerlichen Namen des Benutzers hinzu.",
        "apihelp-query+userinfo-example-simple": "Informationen über den aktuellen Benutzer abrufen",
+       "apihelp-query+userinfo-example-data": "Ruft zusätzliche Informationen über den aktuellen Benutzer ab.",
        "apihelp-query+users-description": "Informationen über eine Liste von Benutzern abrufen.",
+       "apihelp-query+users-param-prop": "Welche Informationsteile einbezogen werden sollen:",
        "apihelp-query+users-paramvalue-prop-blockinfo": "Markiert, ob der Benutzer gesperrt ist, von wem und aus welchem Grund.",
+       "apihelp-query+users-paramvalue-prop-groups": "Listet alle Gruppen auf, zu denen jeder Benutzer gehört.",
+       "apihelp-query+users-paramvalue-prop-implicitgroups": "Listet alle Gruppen auf, bei denen der Benutzer automatisch Mitglied ist.",
+       "apihelp-query+users-paramvalue-prop-rights": "Listet alle Rechte auf, die jeder Benutzer hat.",
+       "apihelp-query+users-paramvalue-prop-editcount": "Ergänzt den Bearbeitungszähler des Benutzers.",
        "apihelp-query+users-example-simple": "Gibt Informationen für den Benutzer <kbd>Example</kbd> zurück.",
        "apihelp-rsd-description": "Ein RSD-Schema (Really Simple Discovery) exportieren.",
        "apihelp-rsd-example-simple": "Das RSD-Schema exportieren",
index d727c8a..96a47fc 100644 (file)
@@ -2,9 +2,18 @@
        "@metadata": {
                "authors": [
                        "Veeven",
-                       "HAUSANRIK"
+                       "HAUSANRIK",
+                       "Ravichandra"
                ]
        },
+       "apihelp-block-description": "ఓ వాడుకరిని నిరోధించండి.",
+       "apihelp-block-param-reason": "నిరోధానికి కారణం.",
+       "apihelp-block-param-nocreate": "ఖాతా సృష్టింపుని నివారించు",
+       "apihelp-createaccount-param-name": "వాడుకరి పేరు:",
+       "apihelp-delete-description": "ఓ పేజీని తొలగించు.",
+       "apihelp-edit-param-minor": "చిన్న మార్పు",
+       "apihelp-edit-example-edit": "ఓ పేజీని మార్చు.",
+       "apihelp-emailuser-description": "వాడుకరికి ఈమెయిలు పంపించండి.",
        "apihelp-feedrecentchanges-example-simple": "ఇటీవలి మార్పులను చూడండి",
-       "apihelp-rawfm-description": "బయà°\9fà°\95à±\81 à°µà°\9aà±\8dà°\9aà°¿à°¨ à°¸à°®à°¾à°\9aారo, à°¡à±\80à°¬à°\97à±\8dà°\97à°¿à°\82à°\97à±\8d à°\85à°\82శమà±\81à°¤à±\8a à°\95లిపి, JSON à°ªà°¦à±\8dధతిలà±\8a (HTMLలో అందంగా-ముద్రించు)"
+       "apihelp-rawfm-description": "బయà°\9fà°\95à±\81 à°µà°\9aà±\8dà°\9aà°¿à°¨ à°¸à°®à°¾à°\9aారo, à°¡à±\80à°¬à°\97à±\8dà°\97à°¿à°\82à°\97à±\8d à°\85à°\82శమà±\81à°¤à±\8b à°\95లిపి, JSON à°ªà°¦à±\8dధతిలà±\8b (HTMLలో అందంగా-ముద్రించు)"
 }
index 8705bb0..d2cf7f6 100644 (file)
@@ -51,6 +51,9 @@ class WikiRevision {
        /** @var string */
        public $user_text = "";
 
+       /** @var User */
+       public $userObj = null;
+
        /** @var string */
        public $model = null;
 
@@ -154,6 +157,13 @@ class WikiRevision {
                $this->user_text = $user;
        }
 
+       /**
+        * @param User $user
+        */
+       function setUserObj( $user ) {
+               $this->userObj = $user;
+       }
+
        /**
         * @param string $ip
         */
@@ -296,6 +306,13 @@ class WikiRevision {
                return $this->user_text;
        }
 
+       /**
+        * @return User
+        */
+       function getUserObj() {
+               return $this->userObj;
+       }
+
        /**
         * @return string
         *
@@ -446,15 +463,14 @@ class WikiRevision {
                $dbw = wfGetDB( DB_MASTER );
 
                # Sneak a single revision into place
-               $user = User::newFromName( $this->getUser() );
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
                if ( $user ) {
                        $userId = intval( $user->getId() );
                        $userText = $user->getName();
-                       $userObj = $user;
                } else {
                        $userId = 0;
                        $userText = $this->getUser();
-                       $userObj = new User;
+                       $user = new User;
                }
 
                // avoid memory leak...?
@@ -534,7 +550,7 @@ class WikiRevision {
                        // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
                        $page->doEditUpdates(
                                $revision,
-                               $userObj,
+                               $user,
                                array( 'created' => $created, 'oldcountable' => 'no-change' )
                        );
                }
@@ -545,7 +561,7 @@ class WikiRevision {
        function importLogItem() {
                $dbw = wfGetDB( DB_MASTER );
 
-               $user = User::newFromName( $this->getUser() );
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
                if ( $user ) {
                        $userId = intval( $user->getId() );
                        $userText = $user->getName();
@@ -644,7 +660,7 @@ class WikiRevision {
                        return false;
                }
 
-               $user = User::newFromName( $this->user_text );
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
 
                # Do the actual upload
                if ( $archiveName ) {
index b863adc..ea700c7 100644 (file)
@@ -883,7 +883,13 @@ abstract class Installer {
                }
 
                if ( !$caches ) {
-                       $this->showMessage( 'config-no-cache' );
+                       $key = 'config-no-cache';
+                       // PHP >=5.5 is called APCu, earlier versions use APC (T61998).
+                       if ( !wfIsHHVM() && version_compare( PHP_VERSION, '5.5', '>=' ) ) {
+                               // config-no-cache-apcu
+                               $key .= '-apcu';
+                       }
+                       $this->showMessage( $key );
                }
 
                $this->setVar( '_Caches', $caches );
diff --git a/includes/installer/WebInstallerComplete.php b/includes/installer/WebInstallerComplete.php
new file mode 100644 (file)
index 0000000..a54264c
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerComplete extends WebInstallerPage {
+
+       public function execute() {
+               // Pop up a dialog box, to make it difficult for the user to forget
+               // to download the file
+               $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) );
+               if ( isset( $_SERVER['HTTP_USER_AGENT'] ) &&
+                       strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false
+               ) {
+                       // JS appears to be the only method that works consistently with IE7+
+                       $this->addHtml( "\n<script>jQuery( function () { location.href = " .
+                               Xml::encodeJsVar( $lsUrl ) . "; } );</script>\n" );
+               } else {
+                       $this->parent->request->response()->header( "Refresh: 0;url=$lsUrl" );
+               }
+
+               $this->startForm();
+               $this->parent->disableLinkPopups();
+               $this->addHTML(
+                       $this->parent->getInfoBox(
+                               wfMessage( 'config-install-done',
+                                       $lsUrl,
+                                       $this->getVar( 'wgServer' ) .
+                                       $this->getVar( 'wgScriptPath' ) . '/index.php',
+                                       '<downloadlink/>'
+                               )->plain(), 'tick-32.png'
+                       )
+               );
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-extension-link' )->text() ) );
+
+               $this->parent->restoreLinkPopups();
+               $this->endForm( false, false );
+       }
+
+}
+
+
diff --git a/includes/installer/WebInstallerCopying.php b/includes/installer/WebInstallerCopying.php
new file mode 100644 (file)
index 0000000..c539ed2
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerCopying extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'COPYING';
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerDBConnect.php b/includes/installer/WebInstallerDBConnect.php
new file mode 100644 (file)
index 0000000..bf1787f
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerDBConnect extends WebInstallerPage {
+
+       /**
+        * @return string|null When string, "skip" or "continue"
+        */
+       public function execute() {
+               if ( $this->getVar( '_ExistingDBSettings' ) ) {
+                       return 'skip';
+               }
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $status = $this->submit();
+
+                       if ( $status->isGood() ) {
+                               $this->setVar( '_UpgradeDone', false );
+
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                       }
+               }
+
+               $this->startForm();
+
+               $types = "<ul class=\"config-settings-block\">\n";
+               $settings = '';
+               $defaultType = $this->getVar( 'wgDBtype' );
+
+               // Messages: config-dbsupport-mysql, config-dbsupport-postgres, config-dbsupport-oracle,
+               // config-dbsupport-sqlite, config-dbsupport-mssql
+               $dbSupport = '';
+               foreach ( Installer::getDBTypes() as $type ) {
+                       $dbSupport .= wfMessage( "config-dbsupport-$type" )->plain() . "\n";
+               }
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-support-info', trim( $dbSupport ) )->text() ) );
+
+               // It's possible that the library for the default DB type is not compiled in.
+               // In that case, instead select the first supported DB type in the list.
+               $compiledDBs = $this->parent->getCompiledDBs();
+               if ( !in_array( $defaultType, $compiledDBs ) ) {
+                       $defaultType = $compiledDBs[0];
+               }
+
+               foreach ( $compiledDBs as $type ) {
+                       $installer = $this->parent->getDBInstaller( $type );
+                       $types .=
+                               '<li>' .
+                               Xml::radioLabel(
+                                       $installer->getReadableName(),
+                                       'DBType',
+                                       $type,
+                                       "DBType_$type",
+                                       $type == $defaultType,
+                                       array( 'class' => 'dbRadio', 'rel' => "DB_wrapper_$type" )
+                               ) .
+                               "</li>\n";
+
+                       // Messages: config-header-mysql, config-header-postgres, config-header-oracle,
+                       // config-header-sqlite
+                       $settings .= Html::openElement(
+                                       'div',
+                                       array(
+                                               'id' => 'DB_wrapper_' . $type,
+                                               'class' => 'dbWrapper'
+                                       )
+                               ) .
+                               Html::element( 'h3', array(), wfMessage( 'config-header-' . $type )->text() ) .
+                               $installer->getConnectForm() .
+                               "</div>\n";
+               }
+
+               $types .= "</ul><br style=\"clear: left\"/>\n";
+
+               $this->addHTML( $this->parent->label( 'config-db-type', false, $types ) . $settings );
+               $this->endForm();
+
+               return null;
+       }
+
+       /**
+        * @return Status
+        */
+       public function submit() {
+               $r = $this->parent->request;
+               $type = $r->getVal( 'DBType' );
+               if ( !$type ) {
+                       return Status::newFatal( 'config-invalid-db-type' );
+               }
+               $this->setVar( 'wgDBtype', $type );
+               $installer = $this->parent->getDBInstaller( $type );
+               if ( !$installer ) {
+                       return Status::newFatal( 'config-invalid-db-type' );
+               }
+
+               return $installer->submitConnectForm();
+       }
+
+}
+
+
diff --git a/includes/installer/WebInstallerDBSettings.php b/includes/installer/WebInstallerDBSettings.php
new file mode 100644 (file)
index 0000000..9b95c0a
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerDBSettings extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               $installer = $this->parent->getDBInstaller( $this->getVar( 'wgDBtype' ) );
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $status = $installer->submitSettingsForm();
+                       if ( $status === false ) {
+                               return 'skip';
+                       } elseif ( $status->isGood() ) {
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                       }
+               }
+
+               $form = $installer->getSettingsForm();
+               if ( $form === false ) {
+                       return 'skip';
+               }
+
+               $this->startForm();
+               $this->addHTML( $form );
+               $this->endForm();
+
+               return null;
+       }
+
+}
+
+
diff --git a/includes/installer/WebInstallerDocument.php b/includes/installer/WebInstallerDocument.php
new file mode 100644 (file)
index 0000000..2b35bc6
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+abstract class WebInstallerDocument extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       abstract protected function getFileName();
+
+       public function execute() {
+               $text = $this->getFileContents();
+               $text = InstallDocFormatter::format( $text );
+               $this->parent->output->addWikiText( $text );
+               $this->startForm();
+               $this->endForm( false );
+       }
+
+       /**
+        * @return string
+        */
+       public function getFileContents() {
+               $file = __DIR__ . '/../../' . $this->getFileName();
+               if ( !file_exists( $file ) ) {
+                       return wfMessage( 'config-nofile', $file )->plain();
+               }
+
+               return file_get_contents( $file );
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerExistingWiki.php b/includes/installer/WebInstallerExistingWiki.php
new file mode 100644 (file)
index 0000000..ebe0ead
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerExistingWiki extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               // If there is no LocalSettings.php, continue to the installer welcome page
+               $vars = Installer::getExistingLocalSettings();
+               if ( !$vars ) {
+                       return 'skip';
+               }
+
+               // Check if the upgrade key supplied to the user has appeared in LocalSettings.php
+               if ( $vars['wgUpgradeKey'] !== false
+                       && $this->getVar( '_UpgradeKeySupplied' )
+                       && $this->getVar( 'wgUpgradeKey' ) === $vars['wgUpgradeKey']
+               ) {
+                       // It's there, so the user is authorized
+                       $status = $this->handleExistingUpgrade( $vars );
+                       if ( $status->isOK() ) {
+                               return 'skip';
+                       } else {
+                               $this->startForm();
+                               $this->parent->showStatusBox( $status );
+                               $this->endForm( 'continue' );
+
+                               return 'output';
+                       }
+               }
+
+               // If there is no $wgUpgradeKey, tell the user to add one to LocalSettings.php
+               if ( $vars['wgUpgradeKey'] === false ) {
+                       if ( $this->getVar( 'wgUpgradeKey', false ) === false ) {
+                               $secretKey = $this->getVar( 'wgSecretKey' ); // preserve $wgSecretKey
+                               $this->parent->generateKeys();
+                               $this->setVar( 'wgSecretKey', $secretKey );
+                               $this->setVar( '_UpgradeKeySupplied', true );
+                       }
+                       $this->startForm();
+                       $this->addHTML( $this->parent->getInfoBox(
+                               wfMessage( 'config-upgrade-key-missing', "<pre dir=\"ltr\">\$wgUpgradeKey = '" .
+                                       $this->getVar( 'wgUpgradeKey' ) . "';</pre>" )->plain()
+                       ) );
+                       $this->endForm( 'continue' );
+
+                       return 'output';
+               }
+
+               // If there is an upgrade key, but it wasn't supplied, prompt the user to enter it
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $key = $r->getText( 'config_wgUpgradeKey' );
+                       if ( !$key || $key !== $vars['wgUpgradeKey'] ) {
+                               $this->parent->showError( 'config-localsettings-badkey' );
+                               $this->showKeyForm();
+
+                               return 'output';
+                       }
+                       // Key was OK
+                       $status = $this->handleExistingUpgrade( $vars );
+                       if ( $status->isOK() ) {
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                               $this->showKeyForm();
+
+                               return 'output';
+                       }
+               } else {
+                       $this->showKeyForm();
+
+                       return 'output';
+               }
+       }
+
+       /**
+        * Show the "enter key" form
+        */
+       protected function showKeyForm() {
+               $this->startForm();
+               $this->addHTML(
+                       $this->parent->getInfoBox( wfMessage( 'config-localsettings-upgrade' )->plain() ) .
+                       '<br />' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgUpgradeKey',
+                               'label' => 'config-localsettings-key',
+                               'attribs' => array( 'autocomplete' => 'off' ),
+                       ) )
+               );
+               $this->endForm( 'continue' );
+       }
+
+       /**
+        * @param string[] $names
+        * @param mixed[] $vars
+        *
+        * @return Status
+        */
+       protected function importVariables( $names, $vars ) {
+               $status = Status::newGood();
+               foreach ( $names as $name ) {
+                       if ( !isset( $vars[$name] ) ) {
+                               $status->fatal( 'config-localsettings-incomplete', $name );
+                       }
+                       $this->setVar( $name, $vars[$name] );
+               }
+
+               return $status;
+       }
+
+       /**
+        * Initiate an upgrade of the existing database
+        *
+        * @param mixed[] $vars Variables from LocalSettings.php
+        *
+        * @return Status
+        */
+       protected function handleExistingUpgrade( $vars ) {
+               // Check $wgDBtype
+               if ( !isset( $vars['wgDBtype'] ) ||
+                       !in_array( $vars['wgDBtype'], Installer::getDBTypes() )
+               ) {
+                       return Status::newFatal( 'config-localsettings-connection-error', '' );
+               }
+
+               // Set the relevant variables from LocalSettings.php
+               $requiredVars = array( 'wgDBtype' );
+               $status = $this->importVariables( $requiredVars, $vars );
+               $installer = $this->parent->getDBInstaller();
+               $status->merge( $this->importVariables( $installer->getGlobalNames(), $vars ) );
+               if ( !$status->isOK() ) {
+                       return $status;
+               }
+
+               if ( isset( $vars['wgDBadminuser'] ) ) {
+                       $this->setVar( '_InstallUser', $vars['wgDBadminuser'] );
+               } else {
+                       $this->setVar( '_InstallUser', $vars['wgDBuser'] );
+               }
+               if ( isset( $vars['wgDBadminpassword'] ) ) {
+                       $this->setVar( '_InstallPassword', $vars['wgDBadminpassword'] );
+               } else {
+                       $this->setVar( '_InstallPassword', $vars['wgDBpassword'] );
+               }
+
+               // Test the database connection
+               $status = $installer->getConnection();
+               if ( !$status->isOK() ) {
+                       // Adjust the error message to explain things correctly
+                       $status->replaceMessage( 'config-connection-error',
+                               'config-localsettings-connection-error' );
+
+                       return $status;
+               }
+
+               // All good
+               $this->setVar( '_ExistingDBSettings', true );
+
+               return $status;
+       }
+
+}
+
+
diff --git a/includes/installer/WebInstallerInstall.php b/includes/installer/WebInstallerInstall.php
new file mode 100644 (file)
index 0000000..261a661
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerInstall extends WebInstallerPage {
+
+       /**
+        * @return bool Always true.
+        */
+       public function isSlow() {
+               return true;
+       }
+
+       /**
+        * @return string|bool
+        */
+       public function execute() {
+               if ( $this->getVar( '_UpgradeDone' ) ) {
+                       return 'skip';
+               } elseif ( $this->getVar( '_InstallDone' ) ) {
+                       return 'continue';
+               } elseif ( $this->parent->request->wasPosted() ) {
+                       $this->startForm();
+                       $this->addHTML( "<ul>" );
+                       $results = $this->parent->performInstallation(
+                               array( $this, 'startStage' ),
+                               array( $this, 'endStage' )
+                       );
+                       $this->addHTML( "</ul>" );
+                       // PerformInstallation bails on a fatal, so make sure the last item
+                       // completed before giving 'next.' Likewise, only provide back on failure
+                       $lastStep = end( $results );
+                       $continue = $lastStep->isOK() ? 'continue' : false;
+                       $back = $lastStep->isOK() ? false : 'back';
+                       $this->endForm( $continue, $back );
+               } else {
+                       $this->startForm();
+                       $this->addHTML( $this->parent->getInfoBox( wfMessage( 'config-install-begin' )->plain() ) );
+                       $this->endForm();
+               }
+
+               return true;
+       }
+
+       /**
+        * @param string $step
+        */
+       public function startStage( $step ) {
+               // Messages: config-install-database, config-install-tables, config-install-interwiki,
+               // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage
+               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() .
+                       wfMessage( 'ellipsis' )->escaped() );
+
+               if ( $step == 'extension-tables' ) {
+                       $this->startLiveBox();
+               }
+       }
+
+       /**
+        * @param string $step
+        * @param Status $status
+        */
+       public function endStage( $step, $status ) {
+               if ( $step == 'extension-tables' ) {
+                       $this->endLiveBox();
+               }
+               $msg = $status->isOk() ? 'config-install-step-done' : 'config-install-step-failed';
+               $html = wfMessage( 'word-separator' )->escaped() . wfMessage( $msg )->escaped();
+               if ( !$status->isOk() ) {
+                       $html = "<span class=\"error\">$html</span>";
+               }
+               $this->addHTML( $html . "</li>\n" );
+               if ( !$status->isGood() ) {
+                       $this->parent->showStatusBox( $status );
+               }
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerLanguage.php b/includes/installer/WebInstallerLanguage.php
new file mode 100644 (file)
index 0000000..ba2745c
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerLanguage extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               global $wgLang;
+               $r = $this->parent->request;
+               $userLang = $r->getVal( 'uselang' );
+               $contLang = $r->getVal( 'ContLang' );
+
+               $languages = Language::fetchLanguageNames();
+               $lifetime = intval( ini_get( 'session.gc_maxlifetime' ) );
+               if ( !$lifetime ) {
+                       $lifetime = 1440; // PHP default
+               }
+
+               if ( $r->wasPosted() ) {
+                       # Do session test
+                       if ( $this->parent->getSession( 'test' ) === null ) {
+                               $requestTime = $r->getVal( 'LanguageRequestTime' );
+                               if ( !$requestTime ) {
+                                       // The most likely explanation is that the user was knocked back
+                                       // from another page on POST due to session expiry
+                                       $msg = 'config-session-expired';
+                               } elseif ( time() - $requestTime > $lifetime ) {
+                                       $msg = 'config-session-expired';
+                               } else {
+                                       $msg = 'config-no-session';
+                               }
+                               $this->parent->showError( $msg, $wgLang->formatTimePeriod( $lifetime ) );
+                       } else {
+                               if ( isset( $languages[$userLang] ) ) {
+                                       $this->setVar( '_UserLang', $userLang );
+                               }
+                               if ( isset( $languages[$contLang] ) ) {
+                                       $this->setVar( 'wgLanguageCode', $contLang );
+                               }
+
+                               return 'continue';
+                       }
+               } elseif ( $this->parent->showSessionWarning ) {
+                       # The user was knocked back from another page to the start
+                       # This probably indicates a session expiry
+                       $this->parent->showError( 'config-session-expired',
+                               $wgLang->formatTimePeriod( $lifetime ) );
+               }
+
+               $this->parent->setSession( 'test', true );
+
+               if ( !isset( $languages[$userLang] ) ) {
+                       $userLang = $this->getVar( '_UserLang', 'en' );
+               }
+               if ( !isset( $languages[$contLang] ) ) {
+                       $contLang = $this->getVar( 'wgLanguageCode', 'en' );
+               }
+               $this->startForm();
+               $s = Html::hidden( 'LanguageRequestTime', time() ) .
+                       $this->getLanguageSelector( 'uselang', 'config-your-language', $userLang,
+                               $this->parent->getHelpBox( 'config-your-language-help' ) ) .
+                       $this->getLanguageSelector( 'ContLang', 'config-wiki-language', $contLang,
+                               $this->parent->getHelpBox( 'config-wiki-language-help' ) );
+               $this->addHTML( $s );
+               $this->endForm( 'continue', false );
+
+               return null;
+       }
+
+       /**
+        * Get a "<select>" for selecting languages.
+        *
+        * @param string $name
+        * @param string $label
+        * @param string $selectedCode
+        * @param string $helpHtml
+        *
+        * @return string
+        */
+       public function getLanguageSelector( $name, $label, $selectedCode, $helpHtml = '' ) {
+               global $wgDummyLanguageCodes;
+
+               $output = $helpHtml;
+
+               $select = new XmlSelect( $name, $name, $selectedCode );
+               $select->setAttribute( 'tabindex', $this->parent->nextTabIndex() );
+
+               $languages = Language::fetchLanguageNames();
+               ksort( $languages );
+               foreach ( $languages as $code => $lang ) {
+                       if ( isset( $wgDummyLanguageCodes[$code] ) ) {
+                               continue;
+                       }
+                       $select->addOption( "$code - $lang", $code );
+               }
+
+               $output .= $select->getHTML();
+               return $this->parent->label( $label, $name, $output );
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerName.php b/includes/installer/WebInstallerName.php
new file mode 100644 (file)
index 0000000..7d0073c
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerName extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       if ( $this->submit() ) {
+                               return 'continue';
+                       }
+               }
+
+               $this->startForm();
+
+               // Encourage people to not name their site 'MediaWiki' by blanking the
+               // field. I think that was the intent with the original $GLOBALS['wgSitename']
+               // but these two always were the same so had the effect of making the
+               // installer forget $wgSitename when navigating back to this page.
+               if ( $this->getVar( 'wgSitename' ) == 'MediaWiki' ) {
+                       $this->setVar( 'wgSitename', '' );
+               }
+
+               // Set wgMetaNamespace to something valid before we show the form.
+               // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
+               $metaNS = $this->getVar( 'wgMetaNamespace' );
+               $this->setVar(
+                       'wgMetaNamespace',
+                       wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
+               );
+
+               $this->addHTML(
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgSitename',
+                               'label' => 'config-site-name',
+                               'help' => $this->parent->getHelpBox( 'config-site-name-help' )
+                       ) ) .
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-ns-site-name, config-ns-generic, config-ns-other
+                       $this->parent->getRadioSet( array(
+                               'var' => '_NamespaceType',
+                               'label' => 'config-project-namespace',
+                               'itemLabelPrefix' => 'config-ns-',
+                               'values' => array( 'site-name', 'generic', 'other' ),
+                               'commonAttribs' => array( 'class' => 'enableForOther',
+                                       'rel' => 'config_wgMetaNamespace' ),
+                               'help' => $this->parent->getHelpBox( 'config-project-namespace-help' )
+                       ) ) .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgMetaNamespace',
+                               'label' => '', // @todo Needs a label?
+                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' )
+                       ) ) .
+                       $this->getFieldSetStart( 'config-admin-box' ) .
+                       $this->parent->getTextBox( array(
+                               'var' => '_AdminName',
+                               'label' => 'config-admin-name',
+                               'help' => $this->parent->getHelpBox( 'config-admin-help' )
+                       ) ) .
+                       $this->parent->getPasswordBox( array(
+                               'var' => '_AdminPassword',
+                               'label' => 'config-admin-password',
+                       ) ) .
+                       $this->parent->getPasswordBox( array(
+                               'var' => '_AdminPasswordConfirm',
+                               'label' => 'config-admin-password-confirm'
+                       ) ) .
+                       $this->parent->getTextBox( array(
+                               'var' => '_AdminEmail',
+                               'attribs' => array(
+                                       'dir' => 'ltr',
+                               ),
+                               'label' => 'config-admin-email',
+                               'help' => $this->parent->getHelpBox( 'config-admin-email-help' )
+                       ) ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => '_Subscribe',
+                               'label' => 'config-subscribe',
+                               'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
+                       ) ) .
+                       $this->getFieldSetEnd() .
+                       $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->text() ) .
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-optional-continue, config-optional-skip
+                       $this->parent->getRadioSet( array(
+                               'var' => '_SkipOptional',
+                               'itemLabelPrefix' => 'config-optional-',
+                               'values' => array( 'continue', 'skip' )
+                       ) )
+               );
+
+               // Restore the default value
+               $this->setVar( 'wgMetaNamespace', $metaNS );
+
+               $this->endForm();
+
+               return 'output';
+       }
+
+       /**
+        * @return bool
+        */
+       public function submit() {
+               global $wgPasswordPolicy;
+
+               $retVal = true;
+               $this->parent->setVarsFromRequest( array( 'wgSitename', '_NamespaceType',
+                       '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
+                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace' ) );
+
+               // Validate site name
+               if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
+                       $this->parent->showError( 'config-site-name-blank' );
+                       $retVal = false;
+               }
+
+               // Fetch namespace
+               $nsType = $this->getVar( '_NamespaceType' );
+               if ( $nsType == 'site-name' ) {
+                       $name = $this->getVar( 'wgSitename' );
+                       // Sanitize for namespace
+                       // This algorithm should match the JS one in WebInstallerOutput.php
+                       $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
+                       $name = str_replace( '&', '&amp;', $name );
+                       $name = preg_replace( '/__+/', '_', $name );
+                       $name = ucfirst( trim( $name, '_' ) );
+               } elseif ( $nsType == 'generic' ) {
+                       $name = wfMessage( 'config-ns-generic' )->text();
+               } else { // other
+                       $name = $this->getVar( 'wgMetaNamespace' );
+               }
+
+               // Validate namespace
+               if ( strpos( $name, ':' ) !== false ) {
+                       $good = false;
+               } else {
+                       // Title-style validation
+                       $title = Title::newFromText( $name );
+                       if ( !$title ) {
+                               $good = $nsType == 'site-name';
+                       } else {
+                               $name = $title->getDBkey();
+                               $good = true;
+                       }
+               }
+               if ( !$good ) {
+                       $this->parent->showError( 'config-ns-invalid', $name );
+                       $retVal = false;
+               }
+
+               // Make sure it won't conflict with any existing namespaces
+               global $wgContLang;
+               $nsIndex = $wgContLang->getNsIndex( $name );
+               if ( $nsIndex !== false && $nsIndex !== NS_PROJECT ) {
+                       $this->parent->showError( 'config-ns-conflict', $name );
+                       $retVal = false;
+               }
+
+               $this->setVar( 'wgMetaNamespace', $name );
+
+               // Validate username for creation
+               $name = $this->getVar( '_AdminName' );
+               if ( strval( $name ) === '' ) {
+                       $this->parent->showError( 'config-admin-name-blank' );
+                       $cname = $name;
+                       $retVal = false;
+               } else {
+                       $cname = User::getCanonicalName( $name, 'creatable' );
+                       if ( $cname === false ) {
+                               $this->parent->showError( 'config-admin-name-invalid', $name );
+                               $retVal = false;
+                       } else {
+                               $this->setVar( '_AdminName', $cname );
+                       }
+               }
+
+               // Validate password
+               $msg = false;
+               $pwd = $this->getVar( '_AdminPassword' );
+               $user = User::newFromName( $cname );
+               if ( $user ) {
+                       $upp = new UserPasswordPolicy(
+                               $wgPasswordPolicy['policies'],
+                               $wgPasswordPolicy['checks']
+                       );
+                       $status = $upp->checkUserPasswordForGroups(
+                               $user,
+                               $pwd,
+                               array( 'bureaucrat', 'sysop' )  // per Installer::createSysop()
+                       );
+                       $valid = $status->isGood() ? true : $status->getMessage();
+               } else {
+                       $valid = 'config-admin-name-invalid';
+               }
+               if ( strval( $pwd ) === '' ) {
+                       // Provide a more specific and helpful message if password field is left blank
+                       $msg = 'config-admin-password-blank';
+               } elseif ( $pwd !== $this->getVar( '_AdminPasswordConfirm' ) ) {
+                       $msg = 'config-admin-password-mismatch';
+               } elseif ( $valid !== true ) {
+                       $msg = $valid;
+               }
+               if ( $msg !== false ) {
+                       call_user_func( array( $this->parent, 'showError' ), $msg );
+                       $this->setVar( '_AdminPassword', '' );
+                       $this->setVar( '_AdminPasswordConfirm', '' );
+                       $retVal = false;
+               }
+
+               // Validate e-mail if provided
+               $email = $this->getVar( '_AdminEmail' );
+               if ( $email && !Sanitizer::validateEmail( $email ) ) {
+                       $this->parent->showError( 'config-admin-error-bademail' );
+                       $retVal = false;
+               }
+               // If they asked to subscribe to mediawiki-announce but didn't give
+               // an e-mail, show an error. Bug 29332
+               if ( !$email && $this->getVar( '_Subscribe' ) ) {
+                       $this->parent->showError( 'config-subscribe-noemail' );
+                       $retVal = false;
+               }
+
+               return $retVal;
+       }
+
+}
+
+
diff --git a/includes/installer/WebInstallerOptions.php b/includes/installer/WebInstallerOptions.php
new file mode 100644 (file)
index 0000000..30ea10e
--- /dev/null
@@ -0,0 +1,461 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerOptions extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
+                       $this->submitSkins();
+                       return 'skip';
+               }
+               if ( $this->parent->request->wasPosted() ) {
+                       if ( $this->submit() ) {
+                               return 'continue';
+                       }
+               }
+
+               $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
+               $this->startForm();
+               $this->addHTML(
+                       # User Rights
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
+                       $this->parent->getRadioSet( array(
+                               'var' => '_RightsProfile',
+                               'label' => 'config-profile',
+                               'itemLabelPrefix' => 'config-profile-',
+                               'values' => array_keys( $this->parent->rightsProfiles ),
+                       ) ) .
+                       $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
+
+                       # Licensing
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
+                       // config-license-cc-0, config-license-pd, config-license-gfdl,
+                       // config-license-none, config-license-cc-choose
+                       $this->parent->getRadioSet( array(
+                               'var' => '_LicenseCode',
+                               'label' => 'config-license',
+                               'itemLabelPrefix' => 'config-license-',
+                               'values' => array_keys( $this->parent->licenses ),
+                               'commonAttribs' => array( 'class' => 'licenseRadio' ),
+                       ) ) .
+                       $this->getCCChooser() .
+                       $this->parent->getHelpBox( 'config-license-help' ) .
+
+                       # E-mail
+                       $this->getFieldSetStart( 'config-email-settings' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableEmail',
+                               'label' => 'config-enable-email',
+                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ),
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-enable-email-help' ) .
+                       "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgPasswordSender',
+                               'label' => 'config-email-sender'
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-sender-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableUserEmail',
+                               'label' => 'config-email-user',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-user-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnotifUserTalk',
+                               'label' => 'config-email-usertalk',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnotifWatchlist',
+                               'label' => 'config-email-watchlist',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEmailAuthentication',
+                               'label' => 'config-email-auth',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-auth-help' ) .
+                       "</div>" .
+                       $this->getFieldSetEnd()
+               );
+
+               $skins = $this->parent->findExtensions( 'skins' );
+               $skinHtml = $this->getFieldSetStart( 'config-skins' );
+
+               $skinNames = array_map( 'strtolower', $skins );
+               $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
+
+               if ( $skins ) {
+                       $radioButtons = $this->parent->getRadioElements( array(
+                               'var' => 'wgDefaultSkin',
+                               'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
+                               'values' => $skinNames,
+                               'value' => $chosenSkinName,
+                       ) );
+
+                       foreach ( $skins as $skin ) {
+                               $skinHtml .=
+                                       '<div class="config-skins-item">' .
+                                       $this->parent->getCheckBox( array(
+                                               'var' => "skin-$skin",
+                                               'rawtext' => $skin,
+                                               'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
+                                       ) ) .
+                                       '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
+                                       '</div>';
+                       }
+               } else {
+                       $skinHtml .=
+                               $this->parent->getWarningBox( wfMessage( 'config-skins-missing' )->plain() ) .
+                               Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
+               }
+
+               $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
+                       $this->getFieldSetEnd();
+               $this->addHTML( $skinHtml );
+
+               $extensions = $this->parent->findExtensions();
+
+               if ( $extensions ) {
+                       $extHtml = $this->getFieldSetStart( 'config-extensions' );
+
+                       foreach ( $extensions as $ext ) {
+                               $extHtml .= $this->parent->getCheckBox( array(
+                                       'var' => "ext-$ext",
+                                       'rawtext' => $ext,
+                               ) );
+                       }
+
+                       $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
+                               $this->getFieldSetEnd();
+                       $this->addHTML( $extHtml );
+               }
+
+               // Having / in paths in Windows looks funny :)
+               $this->setVar( 'wgDeletedDirectory',
+                       str_replace(
+                               '/', DIRECTORY_SEPARATOR,
+                               $this->getVar( 'wgDeletedDirectory' )
+                       )
+               );
+
+               $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
+               $this->addHTML(
+                       # Uploading
+                       $this->getFieldSetStart( 'config-upload-settings' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableUploads',
+                               'label' => 'config-upload-enable',
+                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ),
+                               'help' => $this->parent->getHelpBox( 'config-upload-help' )
+                       ) ) .
+                       '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgDeletedDirectory',
+                               'label' => 'config-upload-deleted',
+                               'attribs' => array( 'dir' => 'ltr' ),
+                               'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
+                       ) ) .
+                       '</div>' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgLogo',
+                               'label' => 'config-logo',
+                               'attribs' => array( 'dir' => 'ltr' ),
+                               'help' => $this->parent->getHelpBox( 'config-logo-help' )
+                       ) )
+               );
+               $this->addHTML(
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgUseInstantCommons',
+                               'label' => 'config-instantcommons',
+                               'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
+                       ) ) .
+                       $this->getFieldSetEnd()
+               );
+
+               $caches = array( 'none' );
+               $cachevalDefault = 'none';
+
+               if ( count( $this->getVar( '_Caches' ) ) ) {
+                       // A CACHE_ACCEL implementation is available
+                       $caches[] = 'accel';
+                       $cachevalDefault = 'accel';
+               }
+               $caches[] = 'memcached';
+
+               // We'll hide/show this on demand when the value changes, see config.js.
+               $cacheval = $this->getVar( '_MainCacheType' );
+               if ( !$cacheval ) {
+                       // We need to set a default here; but don't hardcode it
+                       // or we lose it every time we reload the page for validation
+                       // or going back!
+                       $cacheval = $cachevalDefault;
+               }
+               $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
+               $this->addHTML(
+                       # Advanced settings
+                       $this->getFieldSetStart( 'config-advanced-settings' ) .
+                       # Object cache settings
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-cache-none, config-cache-accel, config-cache-memcached
+                       $this->parent->getRadioSet( array(
+                               'var' => '_MainCacheType',
+                               'label' => 'config-cache-options',
+                               'itemLabelPrefix' => 'config-cache-',
+                               'values' => $caches,
+                               'value' => $cacheval,
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-cache-help' ) .
+                       "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
+                       $this->parent->getTextArea( array(
+                               'var' => '_MemCachedServers',
+                               'label' => 'config-memcached-servers',
+                               'help' => $this->parent->getHelpBox( 'config-memcached-help' )
+                       ) ) .
+                       '</div>' .
+                       $this->getFieldSetEnd()
+               );
+               $this->endForm();
+
+               return null;
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCPartnerUrl() {
+               $server = $this->getVar( 'wgServer' );
+               $exitUrl = $server . $this->parent->getUrl( array(
+                       'page' => 'Options',
+                       'SubmitCC' => 'indeed',
+                       'config__LicenseCode' => 'cc',
+                       'config_wgRightsUrl' => '[license_url]',
+                       'config_wgRightsText' => '[license_name]',
+                       'config_wgRightsIcon' => '[license_button]',
+               ) );
+               $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
+                       '/mw-config/config-cc.css';
+               $iframeUrl = '//creativecommons.org/license/?' .
+                       wfArrayToCgi( array(
+                               'partner' => 'MediaWiki',
+                               'exit_url' => $exitUrl,
+                               'lang' => $this->getVar( '_UserLang' ),
+                               'stylesheet' => $styleUrl,
+                       ) );
+
+               return $iframeUrl;
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCChooser() {
+               $iframeAttribs = array(
+                       'class' => 'config-cc-iframe',
+                       'name' => 'config-cc-iframe',
+                       'id' => 'config-cc-iframe',
+                       'frameborder' => 0,
+                       'width' => '100%',
+                       'height' => '100%',
+               );
+               if ( $this->getVar( '_CCDone' ) ) {
+                       $iframeAttribs['src'] = $this->parent->getUrl( array( 'ShowCC' => 'yes' ) );
+               } else {
+                       $iframeAttribs['src'] = $this->getCCPartnerUrl();
+               }
+               $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
+
+               return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
+                       Html::element( 'iframe', $iframeAttribs, '', false /* not short */ ) .
+                       "</div>\n";
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCDoneBox() {
+               $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
+               // If you change this height, also change it in config.css
+               $expandJs = str_replace( '$1', '54em', $js );
+               $reduceJs = str_replace( '$1', '70px', $js );
+
+               return '<p>' .
+                       Html::element( 'img', array( 'src' => $this->getVar( 'wgRightsIcon' ) ) ) .
+                       '&#160;&#160;' .
+                       htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
+                       "</p>\n" .
+                       "<p style=\"text-align: center;\">" .
+                       Html::element( 'a',
+                               array(
+                                       'href' => $this->getCCPartnerUrl(),
+                                       'onclick' => $expandJs,
+                               ),
+                               wfMessage( 'config-cc-again' )->text()
+                       ) .
+                       "</p>\n" .
+                       "<script>\n" .
+                       # Reduce the wrapper div height
+                       htmlspecialchars( $reduceJs ) .
+                       "\n" .
+                       "</script>\n";
+       }
+
+       public function submitCC() {
+               $newValues = $this->parent->setVarsFromRequest(
+                       array( 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ) );
+               if ( count( $newValues ) != 3 ) {
+                       $this->parent->showError( 'config-cc-error' );
+
+                       return;
+               }
+               $this->setVar( '_CCDone', true );
+               $this->addHTML( $this->getCCDoneBox() );
+       }
+
+       /**
+        * If the user skips this installer page, we still need to set up the default skins, but ignore
+        * everything else.
+        *
+        * @return bool
+        */
+       public function submitSkins() {
+               $skins = $this->parent->findExtensions( 'skins' );
+               $this->parent->setVar( '_Skins', $skins );
+
+               if ( $skins ) {
+                       $skinNames = array_map( 'strtolower', $skins );
+                       $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
+               }
+
+               return true;
+       }
+
+       /**
+        * @return bool
+        */
+       public function submit() {
+               $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
+                       'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', 'wgLogo',
+                       'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
+                       'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
+                       'wgUseInstantCommons', 'wgDefaultSkin' ) );
+
+               $retVal = true;
+
+               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
+                       reset( $this->parent->rightsProfiles );
+                       $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
+               }
+
+               $code = $this->getVar( '_LicenseCode' );
+               if ( $code == 'cc-choose' ) {
+                       if ( !$this->getVar( '_CCDone' ) ) {
+                               $this->parent->showError( 'config-cc-not-chosen' );
+                               $retVal = false;
+                       }
+               } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
+                       // Messages:
+                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
+                       // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
+                       // config-license-cc-choose
+                       $entry = $this->parent->licenses[$code];
+                       if ( isset( $entry['text'] ) ) {
+                               $this->setVar( 'wgRightsText', $entry['text'] );
+                       } else {
+                               $this->setVar( 'wgRightsText', wfMessage( 'config-license-' . $code )->text() );
+                       }
+                       $this->setVar( 'wgRightsUrl', $entry['url'] );
+                       $this->setVar( 'wgRightsIcon', $entry['icon'] );
+               } else {
+                       $this->setVar( 'wgRightsText', '' );
+                       $this->setVar( 'wgRightsUrl', '' );
+                       $this->setVar( 'wgRightsIcon', '' );
+               }
+
+               $skinsAvailable = $this->parent->findExtensions( 'skins' );
+               $skinsToInstall = array();
+               foreach ( $skinsAvailable as $skin ) {
+                       $this->parent->setVarsFromRequest( array( "skin-$skin" ) );
+                       if ( $this->getVar( "skin-$skin" ) ) {
+                               $skinsToInstall[] = $skin;
+                       }
+               }
+               $this->parent->setVar( '_Skins', $skinsToInstall );
+
+               if ( !$skinsToInstall && $skinsAvailable ) {
+                       $this->parent->showError( 'config-skins-must-enable-some' );
+                       $retVal = false;
+               }
+               $defaultSkin = $this->getVar( 'wgDefaultSkin' );
+               $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
+               if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
+                       $this->parent->showError( 'config-skins-must-enable-default' );
+                       $retVal = false;
+               }
+
+               $extsAvailable = $this->parent->findExtensions();
+               $extsToInstall = array();
+               foreach ( $extsAvailable as $ext ) {
+                       $this->parent->setVarsFromRequest( array( "ext-$ext" ) );
+                       if ( $this->getVar( "ext-$ext" ) ) {
+                               $extsToInstall[] = $ext;
+                       }
+               }
+               $this->parent->setVar( '_Extensions', $extsToInstall );
+
+               if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
+                       $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
+                       if ( !$memcServers ) {
+                               $this->parent->showError( 'config-memcache-needservers' );
+                               $retVal = false;
+                       }
+
+                       foreach ( $memcServers as $server ) {
+                               $memcParts = explode( ":", $server, 2 );
+                               if ( !isset( $memcParts[0] )
+                                       || ( !IP::isValid( $memcParts[0] )
+                                               && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
+                               ) {
+                                       $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
+                                       $retVal = false;
+                               } elseif ( !isset( $memcParts[1] ) ) {
+                                       $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
+                                       $retVal = false;
+                               } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
+                                       $this->parent->showError( 'config-memcache-badport', 1, 65535 );
+                                       $retVal = false;
+                               }
+                       }
+               }
+
+               return $retVal;
+       }
+
+}
+
index 2ad2e11..a529939 100644 (file)
@@ -205,1409 +205,3 @@ abstract class WebInstallerPage {
        }
 
 }
-
-class WebInstallerLanguage extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               global $wgLang;
-               $r = $this->parent->request;
-               $userLang = $r->getVal( 'uselang' );
-               $contLang = $r->getVal( 'ContLang' );
-
-               $languages = Language::fetchLanguageNames();
-               $lifetime = intval( ini_get( 'session.gc_maxlifetime' ) );
-               if ( !$lifetime ) {
-                       $lifetime = 1440; // PHP default
-               }
-
-               if ( $r->wasPosted() ) {
-                       # Do session test
-                       if ( $this->parent->getSession( 'test' ) === null ) {
-                               $requestTime = $r->getVal( 'LanguageRequestTime' );
-                               if ( !$requestTime ) {
-                                       // The most likely explanation is that the user was knocked back
-                                       // from another page on POST due to session expiry
-                                       $msg = 'config-session-expired';
-                               } elseif ( time() - $requestTime > $lifetime ) {
-                                       $msg = 'config-session-expired';
-                               } else {
-                                       $msg = 'config-no-session';
-                               }
-                               $this->parent->showError( $msg, $wgLang->formatTimePeriod( $lifetime ) );
-                       } else {
-                               if ( isset( $languages[$userLang] ) ) {
-                                       $this->setVar( '_UserLang', $userLang );
-                               }
-                               if ( isset( $languages[$contLang] ) ) {
-                                       $this->setVar( 'wgLanguageCode', $contLang );
-                               }
-
-                               return 'continue';
-                       }
-               } elseif ( $this->parent->showSessionWarning ) {
-                       # The user was knocked back from another page to the start
-                       # This probably indicates a session expiry
-                       $this->parent->showError( 'config-session-expired',
-                               $wgLang->formatTimePeriod( $lifetime ) );
-               }
-
-               $this->parent->setSession( 'test', true );
-
-               if ( !isset( $languages[$userLang] ) ) {
-                       $userLang = $this->getVar( '_UserLang', 'en' );
-               }
-               if ( !isset( $languages[$contLang] ) ) {
-                       $contLang = $this->getVar( 'wgLanguageCode', 'en' );
-               }
-               $this->startForm();
-               $s = Html::hidden( 'LanguageRequestTime', time() ) .
-                       $this->getLanguageSelector( 'uselang', 'config-your-language', $userLang,
-                               $this->parent->getHelpBox( 'config-your-language-help' ) ) .
-                       $this->getLanguageSelector( 'ContLang', 'config-wiki-language', $contLang,
-                               $this->parent->getHelpBox( 'config-wiki-language-help' ) );
-               $this->addHTML( $s );
-               $this->endForm( 'continue', false );
-
-               return null;
-       }
-
-       /**
-        * Get a "<select>" for selecting languages.
-        *
-        * @param string $name
-        * @param string $label
-        * @param string $selectedCode
-        * @param string $helpHtml
-        *
-        * @return string
-        */
-       public function getLanguageSelector( $name, $label, $selectedCode, $helpHtml = '' ) {
-               global $wgDummyLanguageCodes;
-
-               $output = $helpHtml;
-
-               $select = new XmlSelect( $name, $name, $selectedCode );
-               $select->setAttribute( 'tabindex', $this->parent->nextTabIndex() );
-
-               $languages = Language::fetchLanguageNames();
-               ksort( $languages );
-               foreach ( $languages as $code => $lang ) {
-                       if ( isset( $wgDummyLanguageCodes[$code] ) ) {
-                               continue;
-                       }
-                       $select->addOption( "$code - $lang", $code );
-               }
-
-               $output .= $select->getHTML();
-               return $this->parent->label( $label, $name, $output );
-       }
-
-}
-
-class WebInstallerExistingWiki extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               // If there is no LocalSettings.php, continue to the installer welcome page
-               $vars = Installer::getExistingLocalSettings();
-               if ( !$vars ) {
-                       return 'skip';
-               }
-
-               // Check if the upgrade key supplied to the user has appeared in LocalSettings.php
-               if ( $vars['wgUpgradeKey'] !== false
-                       && $this->getVar( '_UpgradeKeySupplied' )
-                       && $this->getVar( 'wgUpgradeKey' ) === $vars['wgUpgradeKey']
-               ) {
-                       // It's there, so the user is authorized
-                       $status = $this->handleExistingUpgrade( $vars );
-                       if ( $status->isOK() ) {
-                               return 'skip';
-                       } else {
-                               $this->startForm();
-                               $this->parent->showStatusBox( $status );
-                               $this->endForm( 'continue' );
-
-                               return 'output';
-                       }
-               }
-
-               // If there is no $wgUpgradeKey, tell the user to add one to LocalSettings.php
-               if ( $vars['wgUpgradeKey'] === false ) {
-                       if ( $this->getVar( 'wgUpgradeKey', false ) === false ) {
-                               $secretKey = $this->getVar( 'wgSecretKey' ); // preserve $wgSecretKey
-                               $this->parent->generateKeys();
-                               $this->setVar( 'wgSecretKey', $secretKey );
-                               $this->setVar( '_UpgradeKeySupplied', true );
-                       }
-                       $this->startForm();
-                       $this->addHTML( $this->parent->getInfoBox(
-                               wfMessage( 'config-upgrade-key-missing', "<pre dir=\"ltr\">\$wgUpgradeKey = '" .
-                                       $this->getVar( 'wgUpgradeKey' ) . "';</pre>" )->plain()
-                       ) );
-                       $this->endForm( 'continue' );
-
-                       return 'output';
-               }
-
-               // If there is an upgrade key, but it wasn't supplied, prompt the user to enter it
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $key = $r->getText( 'config_wgUpgradeKey' );
-                       if ( !$key || $key !== $vars['wgUpgradeKey'] ) {
-                               $this->parent->showError( 'config-localsettings-badkey' );
-                               $this->showKeyForm();
-
-                               return 'output';
-                       }
-                       // Key was OK
-                       $status = $this->handleExistingUpgrade( $vars );
-                       if ( $status->isOK() ) {
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                               $this->showKeyForm();
-
-                               return 'output';
-                       }
-               } else {
-                       $this->showKeyForm();
-
-                       return 'output';
-               }
-       }
-
-       /**
-        * Show the "enter key" form
-        */
-       protected function showKeyForm() {
-               $this->startForm();
-               $this->addHTML(
-                       $this->parent->getInfoBox( wfMessage( 'config-localsettings-upgrade' )->plain() ) .
-                       '<br />' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgUpgradeKey',
-                               'label' => 'config-localsettings-key',
-                               'attribs' => array( 'autocomplete' => 'off' ),
-                       ) )
-               );
-               $this->endForm( 'continue' );
-       }
-
-       /**
-        * @param string[] $names
-        * @param mixed[] $vars
-        *
-        * @return Status
-        */
-       protected function importVariables( $names, $vars ) {
-               $status = Status::newGood();
-               foreach ( $names as $name ) {
-                       if ( !isset( $vars[$name] ) ) {
-                               $status->fatal( 'config-localsettings-incomplete', $name );
-                       }
-                       $this->setVar( $name, $vars[$name] );
-               }
-
-               return $status;
-       }
-
-       /**
-        * Initiate an upgrade of the existing database
-        *
-        * @param mixed[] $vars Variables from LocalSettings.php
-        *
-        * @return Status
-        */
-       protected function handleExistingUpgrade( $vars ) {
-               // Check $wgDBtype
-               if ( !isset( $vars['wgDBtype'] ) ||
-                       !in_array( $vars['wgDBtype'], Installer::getDBTypes() )
-               ) {
-                       return Status::newFatal( 'config-localsettings-connection-error', '' );
-               }
-
-               // Set the relevant variables from LocalSettings.php
-               $requiredVars = array( 'wgDBtype' );
-               $status = $this->importVariables( $requiredVars, $vars );
-               $installer = $this->parent->getDBInstaller();
-               $status->merge( $this->importVariables( $installer->getGlobalNames(), $vars ) );
-               if ( !$status->isOK() ) {
-                       return $status;
-               }
-
-               if ( isset( $vars['wgDBadminuser'] ) ) {
-                       $this->setVar( '_InstallUser', $vars['wgDBadminuser'] );
-               } else {
-                       $this->setVar( '_InstallUser', $vars['wgDBuser'] );
-               }
-               if ( isset( $vars['wgDBadminpassword'] ) ) {
-                       $this->setVar( '_InstallPassword', $vars['wgDBadminpassword'] );
-               } else {
-                       $this->setVar( '_InstallPassword', $vars['wgDBpassword'] );
-               }
-
-               // Test the database connection
-               $status = $installer->getConnection();
-               if ( !$status->isOK() ) {
-                       // Adjust the error message to explain things correctly
-                       $status->replaceMessage( 'config-connection-error',
-                               'config-localsettings-connection-error' );
-
-                       return $status;
-               }
-
-               // All good
-               $this->setVar( '_ExistingDBSettings', true );
-
-               return $status;
-       }
-
-}
-
-class WebInstallerWelcome extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               if ( $this->parent->request->wasPosted() ) {
-                       if ( $this->getVar( '_Environment' ) ) {
-                               return 'continue';
-                       }
-               }
-               $this->parent->output->addWikiText( wfMessage( 'config-welcome' )->plain() );
-               $status = $this->parent->doEnvironmentChecks();
-               if ( $status->isGood() ) {
-                       $this->parent->output->addHTML( '<span class="success-message">' .
-                               wfMessage( 'config-env-good' )->escaped() . '</span>' );
-                       $this->parent->output->addWikiText( wfMessage( 'config-copyright',
-                               SpecialVersion::getCopyrightAndAuthorList() )->plain() );
-                       $this->startForm();
-                       $this->endForm();
-               } else {
-                       $this->parent->showStatusMessage( $status );
-               }
-
-               return '';
-       }
-
-}
-
-class WebInstallerDBConnect extends WebInstallerPage {
-
-       /**
-        * @return string|null When string, "skip" or "continue"
-        */
-       public function execute() {
-               if ( $this->getVar( '_ExistingDBSettings' ) ) {
-                       return 'skip';
-               }
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $status = $this->submit();
-
-                       if ( $status->isGood() ) {
-                               $this->setVar( '_UpgradeDone', false );
-
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                       }
-               }
-
-               $this->startForm();
-
-               $types = "<ul class=\"config-settings-block\">\n";
-               $settings = '';
-               $defaultType = $this->getVar( 'wgDBtype' );
-
-               // Messages: config-dbsupport-mysql, config-dbsupport-postgres, config-dbsupport-oracle,
-               // config-dbsupport-sqlite, config-dbsupport-mssql
-               $dbSupport = '';
-               foreach ( Installer::getDBTypes() as $type ) {
-                       $dbSupport .= wfMessage( "config-dbsupport-$type" )->plain() . "\n";
-               }
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-support-info', trim( $dbSupport ) )->text() ) );
-
-               // It's possible that the library for the default DB type is not compiled in.
-               // In that case, instead select the first supported DB type in the list.
-               $compiledDBs = $this->parent->getCompiledDBs();
-               if ( !in_array( $defaultType, $compiledDBs ) ) {
-                       $defaultType = $compiledDBs[0];
-               }
-
-               foreach ( $compiledDBs as $type ) {
-                       $installer = $this->parent->getDBInstaller( $type );
-                       $types .=
-                               '<li>' .
-                               Xml::radioLabel(
-                                       $installer->getReadableName(),
-                                       'DBType',
-                                       $type,
-                                       "DBType_$type",
-                                       $type == $defaultType,
-                                       array( 'class' => 'dbRadio', 'rel' => "DB_wrapper_$type" )
-                               ) .
-                               "</li>\n";
-
-                       // Messages: config-header-mysql, config-header-postgres, config-header-oracle,
-                       // config-header-sqlite
-                       $settings .= Html::openElement(
-                                       'div',
-                                       array(
-                                               'id' => 'DB_wrapper_' . $type,
-                                               'class' => 'dbWrapper'
-                                       )
-                               ) .
-                               Html::element( 'h3', array(), wfMessage( 'config-header-' . $type )->text() ) .
-                               $installer->getConnectForm() .
-                               "</div>\n";
-               }
-
-               $types .= "</ul><br style=\"clear: left\"/>\n";
-
-               $this->addHTML( $this->parent->label( 'config-db-type', false, $types ) . $settings );
-               $this->endForm();
-
-               return null;
-       }
-
-       /**
-        * @return Status
-        */
-       public function submit() {
-               $r = $this->parent->request;
-               $type = $r->getVal( 'DBType' );
-               if ( !$type ) {
-                       return Status::newFatal( 'config-invalid-db-type' );
-               }
-               $this->setVar( 'wgDBtype', $type );
-               $installer = $this->parent->getDBInstaller( $type );
-               if ( !$installer ) {
-                       return Status::newFatal( 'config-invalid-db-type' );
-               }
-
-               return $installer->submitConnectForm();
-       }
-
-}
-
-class WebInstallerUpgrade extends WebInstallerPage {
-
-       /**
-        * @return bool Always true.
-        */
-       public function isSlow() {
-               return true;
-       }
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               if ( $this->getVar( '_UpgradeDone' ) ) {
-                       // Allow regeneration of LocalSettings.php, unless we are working
-                       // from a pre-existing LocalSettings.php file and we want to avoid
-                       // leaking its contents
-                       if ( $this->parent->request->wasPosted() && !$this->getVar( '_ExistingDBSettings' ) ) {
-                               // Done message acknowledged
-                               return 'continue';
-                       } else {
-                               // Back button click
-                               // Show the done message again
-                               // Make them click back again if they want to do the upgrade again
-                               $this->showDoneMessage();
-
-                               return 'output';
-                       }
-               }
-
-               // wgDBtype is generally valid here because otherwise the previous page
-               // (connect) wouldn't have declared its happiness
-               $type = $this->getVar( 'wgDBtype' );
-               $installer = $this->parent->getDBInstaller( $type );
-
-               if ( !$installer->needsUpgrade() ) {
-                       return 'skip';
-               }
-
-               if ( $this->parent->request->wasPosted() ) {
-                       $installer->preUpgrade();
-
-                       $this->startLiveBox();
-                       $result = $installer->doUpgrade();
-                       $this->endLiveBox();
-
-                       if ( $result ) {
-                               // If they're going to possibly regenerate LocalSettings, we
-                               // need to create the upgrade/secret keys. Bug 26481
-                               if ( !$this->getVar( '_ExistingDBSettings' ) ) {
-                                       $this->parent->generateKeys();
-                               }
-                               $this->setVar( '_UpgradeDone', true );
-                               $this->showDoneMessage();
-
-                               return 'output';
-                       }
-               }
-
-               $this->startForm();
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-can-upgrade', $GLOBALS['wgVersion'] )->plain() ) );
-               $this->endForm();
-
-               return null;
-       }
-
-       public function showDoneMessage() {
-               $this->startForm();
-               $regenerate = !$this->getVar( '_ExistingDBSettings' );
-               if ( $regenerate ) {
-                       $msg = 'config-upgrade-done';
-               } else {
-                       $msg = 'config-upgrade-done-no-regenerate';
-               }
-               $this->parent->disableLinkPopups();
-               $this->addHTML(
-                       $this->parent->getInfoBox(
-                               wfMessage( $msg,
-                                       $this->getVar( 'wgServer' ) .
-                                       $this->getVar( 'wgScriptPath' ) . '/index.php'
-                               )->plain(), 'tick-32.png'
-                       )
-               );
-               $this->parent->restoreLinkPopups();
-               $this->endForm( $regenerate ? 'regenerate' : false, false );
-       }
-
-}
-
-class WebInstallerDBSettings extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               $installer = $this->parent->getDBInstaller( $this->getVar( 'wgDBtype' ) );
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $status = $installer->submitSettingsForm();
-                       if ( $status === false ) {
-                               return 'skip';
-                       } elseif ( $status->isGood() ) {
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                       }
-               }
-
-               $form = $installer->getSettingsForm();
-               if ( $form === false ) {
-                       return 'skip';
-               }
-
-               $this->startForm();
-               $this->addHTML( $form );
-               $this->endForm();
-
-               return null;
-       }
-
-}
-
-class WebInstallerName extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       if ( $this->submit() ) {
-                               return 'continue';
-                       }
-               }
-
-               $this->startForm();
-
-               // Encourage people to not name their site 'MediaWiki' by blanking the
-               // field. I think that was the intent with the original $GLOBALS['wgSitename']
-               // but these two always were the same so had the effect of making the
-               // installer forget $wgSitename when navigating back to this page.
-               if ( $this->getVar( 'wgSitename' ) == 'MediaWiki' ) {
-                       $this->setVar( 'wgSitename', '' );
-               }
-
-               // Set wgMetaNamespace to something valid before we show the form.
-               // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
-               $metaNS = $this->getVar( 'wgMetaNamespace' );
-               $this->setVar(
-                       'wgMetaNamespace',
-                       wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
-               );
-
-               $this->addHTML(
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgSitename',
-                               'label' => 'config-site-name',
-                               'help' => $this->parent->getHelpBox( 'config-site-name-help' )
-                       ) ) .
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-ns-site-name, config-ns-generic, config-ns-other
-                       $this->parent->getRadioSet( array(
-                               'var' => '_NamespaceType',
-                               'label' => 'config-project-namespace',
-                               'itemLabelPrefix' => 'config-ns-',
-                               'values' => array( 'site-name', 'generic', 'other' ),
-                               'commonAttribs' => array( 'class' => 'enableForOther',
-                                       'rel' => 'config_wgMetaNamespace' ),
-                               'help' => $this->parent->getHelpBox( 'config-project-namespace-help' )
-                       ) ) .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgMetaNamespace',
-                               'label' => '', // @todo Needs a label?
-                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' )
-                       ) ) .
-                       $this->getFieldSetStart( 'config-admin-box' ) .
-                       $this->parent->getTextBox( array(
-                               'var' => '_AdminName',
-                               'label' => 'config-admin-name',
-                               'help' => $this->parent->getHelpBox( 'config-admin-help' )
-                       ) ) .
-                       $this->parent->getPasswordBox( array(
-                               'var' => '_AdminPassword',
-                               'label' => 'config-admin-password',
-                       ) ) .
-                       $this->parent->getPasswordBox( array(
-                               'var' => '_AdminPasswordConfirm',
-                               'label' => 'config-admin-password-confirm'
-                       ) ) .
-                       $this->parent->getTextBox( array(
-                               'var' => '_AdminEmail',
-                               'attribs' => array(
-                                       'dir' => 'ltr',
-                               ),
-                               'label' => 'config-admin-email',
-                               'help' => $this->parent->getHelpBox( 'config-admin-email-help' )
-                       ) ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => '_Subscribe',
-                               'label' => 'config-subscribe',
-                               'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
-                       ) ) .
-                       $this->getFieldSetEnd() .
-                       $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->text() ) .
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-optional-continue, config-optional-skip
-                       $this->parent->getRadioSet( array(
-                               'var' => '_SkipOptional',
-                               'itemLabelPrefix' => 'config-optional-',
-                               'values' => array( 'continue', 'skip' )
-                       ) )
-               );
-
-               // Restore the default value
-               $this->setVar( 'wgMetaNamespace', $metaNS );
-
-               $this->endForm();
-
-               return 'output';
-       }
-
-       /**
-        * @return bool
-        */
-       public function submit() {
-               global $wgPasswordPolicy;
-
-               $retVal = true;
-               $this->parent->setVarsFromRequest( array( 'wgSitename', '_NamespaceType',
-                       '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
-                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace' ) );
-
-               // Validate site name
-               if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
-                       $this->parent->showError( 'config-site-name-blank' );
-                       $retVal = false;
-               }
-
-               // Fetch namespace
-               $nsType = $this->getVar( '_NamespaceType' );
-               if ( $nsType == 'site-name' ) {
-                       $name = $this->getVar( 'wgSitename' );
-                       // Sanitize for namespace
-                       // This algorithm should match the JS one in WebInstallerOutput.php
-                       $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
-                       $name = str_replace( '&', '&amp;', $name );
-                       $name = preg_replace( '/__+/', '_', $name );
-                       $name = ucfirst( trim( $name, '_' ) );
-               } elseif ( $nsType == 'generic' ) {
-                       $name = wfMessage( 'config-ns-generic' )->text();
-               } else { // other
-                       $name = $this->getVar( 'wgMetaNamespace' );
-               }
-
-               // Validate namespace
-               if ( strpos( $name, ':' ) !== false ) {
-                       $good = false;
-               } else {
-                       // Title-style validation
-                       $title = Title::newFromText( $name );
-                       if ( !$title ) {
-                               $good = $nsType == 'site-name';
-                       } else {
-                               $name = $title->getDBkey();
-                               $good = true;
-                       }
-               }
-               if ( !$good ) {
-                       $this->parent->showError( 'config-ns-invalid', $name );
-                       $retVal = false;
-               }
-
-               // Make sure it won't conflict with any existing namespaces
-               global $wgContLang;
-               $nsIndex = $wgContLang->getNsIndex( $name );
-               if ( $nsIndex !== false && $nsIndex !== NS_PROJECT ) {
-                       $this->parent->showError( 'config-ns-conflict', $name );
-                       $retVal = false;
-               }
-
-               $this->setVar( 'wgMetaNamespace', $name );
-
-               // Validate username for creation
-               $name = $this->getVar( '_AdminName' );
-               if ( strval( $name ) === '' ) {
-                       $this->parent->showError( 'config-admin-name-blank' );
-                       $cname = $name;
-                       $retVal = false;
-               } else {
-                       $cname = User::getCanonicalName( $name, 'creatable' );
-                       if ( $cname === false ) {
-                               $this->parent->showError( 'config-admin-name-invalid', $name );
-                               $retVal = false;
-                       } else {
-                               $this->setVar( '_AdminName', $cname );
-                       }
-               }
-
-               // Validate password
-               $msg = false;
-               $pwd = $this->getVar( '_AdminPassword' );
-               $user = User::newFromName( $cname );
-               if ( $user ) {
-                       $upp = new UserPasswordPolicy(
-                               $wgPasswordPolicy['policies'],
-                               $wgPasswordPolicy['checks']
-                       );
-                       $status = $upp->checkUserPasswordForGroups(
-                               $user,
-                               $pwd,
-                               array( 'bureaucrat', 'sysop' )  // per Installer::createSysop()
-                       );
-                       $valid = $status->isGood() ? true : $status->getMessage();
-               } else {
-                       $valid = 'config-admin-name-invalid';
-               }
-               if ( strval( $pwd ) === '' ) {
-                       // Provide a more specific and helpful message if password field is left blank
-                       $msg = 'config-admin-password-blank';
-               } elseif ( $pwd !== $this->getVar( '_AdminPasswordConfirm' ) ) {
-                       $msg = 'config-admin-password-mismatch';
-               } elseif ( $valid !== true ) {
-                       $msg = $valid;
-               }
-               if ( $msg !== false ) {
-                       call_user_func( array( $this->parent, 'showError' ), $msg );
-                       $this->setVar( '_AdminPassword', '' );
-                       $this->setVar( '_AdminPasswordConfirm', '' );
-                       $retVal = false;
-               }
-
-               // Validate e-mail if provided
-               $email = $this->getVar( '_AdminEmail' );
-               if ( $email && !Sanitizer::validateEmail( $email ) ) {
-                       $this->parent->showError( 'config-admin-error-bademail' );
-                       $retVal = false;
-               }
-               // If they asked to subscribe to mediawiki-announce but didn't give
-               // an e-mail, show an error. Bug 29332
-               if ( !$email && $this->getVar( '_Subscribe' ) ) {
-                       $this->parent->showError( 'config-subscribe-noemail' );
-                       $retVal = false;
-               }
-
-               return $retVal;
-       }
-
-}
-
-class WebInstallerOptions extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
-                       $this->submitSkins();
-                       return 'skip';
-               }
-               if ( $this->parent->request->wasPosted() ) {
-                       if ( $this->submit() ) {
-                               return 'continue';
-                       }
-               }
-
-               $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
-               $this->startForm();
-               $this->addHTML(
-                       # User Rights
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
-                       $this->parent->getRadioSet( array(
-                               'var' => '_RightsProfile',
-                               'label' => 'config-profile',
-                               'itemLabelPrefix' => 'config-profile-',
-                               'values' => array_keys( $this->parent->rightsProfiles ),
-                       ) ) .
-                       $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
-
-                       # Licensing
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
-                       // config-license-cc-0, config-license-pd, config-license-gfdl,
-                       // config-license-none, config-license-cc-choose
-                       $this->parent->getRadioSet( array(
-                               'var' => '_LicenseCode',
-                               'label' => 'config-license',
-                               'itemLabelPrefix' => 'config-license-',
-                               'values' => array_keys( $this->parent->licenses ),
-                               'commonAttribs' => array( 'class' => 'licenseRadio' ),
-                       ) ) .
-                       $this->getCCChooser() .
-                       $this->parent->getHelpBox( 'config-license-help' ) .
-
-                       # E-mail
-                       $this->getFieldSetStart( 'config-email-settings' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableEmail',
-                               'label' => 'config-enable-email',
-                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ),
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-enable-email-help' ) .
-                       "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgPasswordSender',
-                               'label' => 'config-email-sender'
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-sender-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableUserEmail',
-                               'label' => 'config-email-user',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-user-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnotifUserTalk',
-                               'label' => 'config-email-usertalk',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnotifWatchlist',
-                               'label' => 'config-email-watchlist',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEmailAuthentication',
-                               'label' => 'config-email-auth',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-auth-help' ) .
-                       "</div>" .
-                       $this->getFieldSetEnd()
-               );
-
-               $skins = $this->parent->findExtensions( 'skins' );
-               $skinHtml = $this->getFieldSetStart( 'config-skins' );
-
-               $skinNames = array_map( 'strtolower', $skins );
-               $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
-
-               if ( $skins ) {
-                       $radioButtons = $this->parent->getRadioElements( array(
-                               'var' => 'wgDefaultSkin',
-                               'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
-                               'values' => $skinNames,
-                               'value' => $chosenSkinName,
-                       ) );
-
-                       foreach ( $skins as $skin ) {
-                               $skinHtml .=
-                                       '<div class="config-skins-item">' .
-                                       $this->parent->getCheckBox( array(
-                                               'var' => "skin-$skin",
-                                               'rawtext' => $skin,
-                                               'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
-                                       ) ) .
-                                       '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
-                                       '</div>';
-                       }
-               } else {
-                       $skinHtml .=
-                               $this->parent->getWarningBox( wfMessage( 'config-skins-missing' )->plain() ) .
-                               Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
-               }
-
-               $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
-                       $this->getFieldSetEnd();
-               $this->addHTML( $skinHtml );
-
-               $extensions = $this->parent->findExtensions();
-
-               if ( $extensions ) {
-                       $extHtml = $this->getFieldSetStart( 'config-extensions' );
-
-                       foreach ( $extensions as $ext ) {
-                               $extHtml .= $this->parent->getCheckBox( array(
-                                       'var' => "ext-$ext",
-                                       'rawtext' => $ext,
-                               ) );
-                       }
-
-                       $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
-                               $this->getFieldSetEnd();
-                       $this->addHTML( $extHtml );
-               }
-
-               // Having / in paths in Windows looks funny :)
-               $this->setVar( 'wgDeletedDirectory',
-                       str_replace(
-                               '/', DIRECTORY_SEPARATOR,
-                               $this->getVar( 'wgDeletedDirectory' )
-                       )
-               );
-
-               $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
-               $this->addHTML(
-                       # Uploading
-                       $this->getFieldSetStart( 'config-upload-settings' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableUploads',
-                               'label' => 'config-upload-enable',
-                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ),
-                               'help' => $this->parent->getHelpBox( 'config-upload-help' )
-                       ) ) .
-                       '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgDeletedDirectory',
-                               'label' => 'config-upload-deleted',
-                               'attribs' => array( 'dir' => 'ltr' ),
-                               'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
-                       ) ) .
-                       '</div>' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgLogo',
-                               'label' => 'config-logo',
-                               'attribs' => array( 'dir' => 'ltr' ),
-                               'help' => $this->parent->getHelpBox( 'config-logo-help' )
-                       ) )
-               );
-               $this->addHTML(
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgUseInstantCommons',
-                               'label' => 'config-instantcommons',
-                               'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
-                       ) ) .
-                       $this->getFieldSetEnd()
-               );
-
-               $caches = array( 'none' );
-               $cachevalDefault = 'none';
-
-               if ( count( $this->getVar( '_Caches' ) ) ) {
-                       // A CACHE_ACCEL implementation is available
-                       $caches[] = 'accel';
-                       $cachevalDefault = 'accel';
-               }
-               $caches[] = 'memcached';
-
-               // We'll hide/show this on demand when the value changes, see config.js.
-               $cacheval = $this->getVar( '_MainCacheType' );
-               if ( !$cacheval ) {
-                       // We need to set a default here; but don't hardcode it
-                       // or we lose it every time we reload the page for validation
-                       // or going back!
-                       $cacheval = $cachevalDefault;
-               }
-               $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
-               $this->addHTML(
-                       # Advanced settings
-                       $this->getFieldSetStart( 'config-advanced-settings' ) .
-                       # Object cache settings
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-cache-none, config-cache-accel, config-cache-memcached
-                       $this->parent->getRadioSet( array(
-                               'var' => '_MainCacheType',
-                               'label' => 'config-cache-options',
-                               'itemLabelPrefix' => 'config-cache-',
-                               'values' => $caches,
-                               'value' => $cacheval,
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-cache-help' ) .
-                       "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
-                       $this->parent->getTextArea( array(
-                               'var' => '_MemCachedServers',
-                               'label' => 'config-memcached-servers',
-                               'help' => $this->parent->getHelpBox( 'config-memcached-help' )
-                       ) ) .
-                       '</div>' .
-                       $this->getFieldSetEnd()
-               );
-               $this->endForm();
-
-               return null;
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCPartnerUrl() {
-               $server = $this->getVar( 'wgServer' );
-               $exitUrl = $server . $this->parent->getUrl( array(
-                       'page' => 'Options',
-                       'SubmitCC' => 'indeed',
-                       'config__LicenseCode' => 'cc',
-                       'config_wgRightsUrl' => '[license_url]',
-                       'config_wgRightsText' => '[license_name]',
-                       'config_wgRightsIcon' => '[license_button]',
-               ) );
-               $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
-                       '/mw-config/config-cc.css';
-               $iframeUrl = '//creativecommons.org/license/?' .
-                       wfArrayToCgi( array(
-                               'partner' => 'MediaWiki',
-                               'exit_url' => $exitUrl,
-                               'lang' => $this->getVar( '_UserLang' ),
-                               'stylesheet' => $styleUrl,
-                       ) );
-
-               return $iframeUrl;
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCChooser() {
-               $iframeAttribs = array(
-                       'class' => 'config-cc-iframe',
-                       'name' => 'config-cc-iframe',
-                       'id' => 'config-cc-iframe',
-                       'frameborder' => 0,
-                       'width' => '100%',
-                       'height' => '100%',
-               );
-               if ( $this->getVar( '_CCDone' ) ) {
-                       $iframeAttribs['src'] = $this->parent->getUrl( array( 'ShowCC' => 'yes' ) );
-               } else {
-                       $iframeAttribs['src'] = $this->getCCPartnerUrl();
-               }
-               $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
-
-               return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
-                       Html::element( 'iframe', $iframeAttribs, '', false /* not short */ ) .
-                       "</div>\n";
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCDoneBox() {
-               $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
-               // If you change this height, also change it in config.css
-               $expandJs = str_replace( '$1', '54em', $js );
-               $reduceJs = str_replace( '$1', '70px', $js );
-
-               return '<p>' .
-                       Html::element( 'img', array( 'src' => $this->getVar( 'wgRightsIcon' ) ) ) .
-                       '&#160;&#160;' .
-                       htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
-                       "</p>\n" .
-                       "<p style=\"text-align: center;\">" .
-                       Html::element( 'a',
-                               array(
-                                       'href' => $this->getCCPartnerUrl(),
-                                       'onclick' => $expandJs,
-                               ),
-                               wfMessage( 'config-cc-again' )->text()
-                       ) .
-                       "</p>\n" .
-                       "<script>\n" .
-                       # Reduce the wrapper div height
-                       htmlspecialchars( $reduceJs ) .
-                       "\n" .
-                       "</script>\n";
-       }
-
-       public function submitCC() {
-               $newValues = $this->parent->setVarsFromRequest(
-                       array( 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ) );
-               if ( count( $newValues ) != 3 ) {
-                       $this->parent->showError( 'config-cc-error' );
-
-                       return;
-               }
-               $this->setVar( '_CCDone', true );
-               $this->addHTML( $this->getCCDoneBox() );
-       }
-
-       /**
-        * If the user skips this installer page, we still need to set up the default skins, but ignore
-        * everything else.
-        *
-        * @return bool
-        */
-       public function submitSkins() {
-               $skins = $this->parent->findExtensions( 'skins' );
-               $this->parent->setVar( '_Skins', $skins );
-
-               if ( $skins ) {
-                       $skinNames = array_map( 'strtolower', $skins );
-                       $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
-               }
-
-               return true;
-       }
-
-       /**
-        * @return bool
-        */
-       public function submit() {
-               $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
-                       'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', 'wgLogo',
-                       'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
-                       'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
-                       'wgUseInstantCommons', 'wgDefaultSkin' ) );
-
-               $retVal = true;
-
-               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
-                       reset( $this->parent->rightsProfiles );
-                       $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
-               }
-
-               $code = $this->getVar( '_LicenseCode' );
-               if ( $code == 'cc-choose' ) {
-                       if ( !$this->getVar( '_CCDone' ) ) {
-                               $this->parent->showError( 'config-cc-not-chosen' );
-                               $retVal = false;
-                       }
-               } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
-                       // Messages:
-                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
-                       // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
-                       // config-license-cc-choose
-                       $entry = $this->parent->licenses[$code];
-                       if ( isset( $entry['text'] ) ) {
-                               $this->setVar( 'wgRightsText', $entry['text'] );
-                       } else {
-                               $this->setVar( 'wgRightsText', wfMessage( 'config-license-' . $code )->text() );
-                       }
-                       $this->setVar( 'wgRightsUrl', $entry['url'] );
-                       $this->setVar( 'wgRightsIcon', $entry['icon'] );
-               } else {
-                       $this->setVar( 'wgRightsText', '' );
-                       $this->setVar( 'wgRightsUrl', '' );
-                       $this->setVar( 'wgRightsIcon', '' );
-               }
-
-               $skinsAvailable = $this->parent->findExtensions( 'skins' );
-               $skinsToInstall = array();
-               foreach ( $skinsAvailable as $skin ) {
-                       $this->parent->setVarsFromRequest( array( "skin-$skin" ) );
-                       if ( $this->getVar( "skin-$skin" ) ) {
-                               $skinsToInstall[] = $skin;
-                       }
-               }
-               $this->parent->setVar( '_Skins', $skinsToInstall );
-
-               if ( !$skinsToInstall && $skinsAvailable ) {
-                       $this->parent->showError( 'config-skins-must-enable-some' );
-                       $retVal = false;
-               }
-               $defaultSkin = $this->getVar( 'wgDefaultSkin' );
-               $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
-               if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
-                       $this->parent->showError( 'config-skins-must-enable-default' );
-                       $retVal = false;
-               }
-
-               $extsAvailable = $this->parent->findExtensions();
-               $extsToInstall = array();
-               foreach ( $extsAvailable as $ext ) {
-                       $this->parent->setVarsFromRequest( array( "ext-$ext" ) );
-                       if ( $this->getVar( "ext-$ext" ) ) {
-                               $extsToInstall[] = $ext;
-                       }
-               }
-               $this->parent->setVar( '_Extensions', $extsToInstall );
-
-               if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
-                       $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
-                       if ( !$memcServers ) {
-                               $this->parent->showError( 'config-memcache-needservers' );
-                               $retVal = false;
-                       }
-
-                       foreach ( $memcServers as $server ) {
-                               $memcParts = explode( ":", $server, 2 );
-                               if ( !isset( $memcParts[0] )
-                                       || ( !IP::isValid( $memcParts[0] )
-                                               && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
-                               ) {
-                                       $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
-                                       $retVal = false;
-                               } elseif ( !isset( $memcParts[1] ) ) {
-                                       $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
-                                       $retVal = false;
-                               } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
-                                       $this->parent->showError( 'config-memcache-badport', 1, 65535 );
-                                       $retVal = false;
-                               }
-                       }
-               }
-
-               return $retVal;
-       }
-
-}
-
-class WebInstallerInstall extends WebInstallerPage {
-
-       /**
-        * @return bool Always true.
-        */
-       public function isSlow() {
-               return true;
-       }
-
-       /**
-        * @return string|bool
-        */
-       public function execute() {
-               if ( $this->getVar( '_UpgradeDone' ) ) {
-                       return 'skip';
-               } elseif ( $this->getVar( '_InstallDone' ) ) {
-                       return 'continue';
-               } elseif ( $this->parent->request->wasPosted() ) {
-                       $this->startForm();
-                       $this->addHTML( "<ul>" );
-                       $results = $this->parent->performInstallation(
-                               array( $this, 'startStage' ),
-                               array( $this, 'endStage' )
-                       );
-                       $this->addHTML( "</ul>" );
-                       // PerformInstallation bails on a fatal, so make sure the last item
-                       // completed before giving 'next.' Likewise, only provide back on failure
-                       $lastStep = end( $results );
-                       $continue = $lastStep->isOK() ? 'continue' : false;
-                       $back = $lastStep->isOK() ? false : 'back';
-                       $this->endForm( $continue, $back );
-               } else {
-                       $this->startForm();
-                       $this->addHTML( $this->parent->getInfoBox( wfMessage( 'config-install-begin' )->plain() ) );
-                       $this->endForm();
-               }
-
-               return true;
-       }
-
-       /**
-        * @param string $step
-        */
-       public function startStage( $step ) {
-               // Messages: config-install-database, config-install-tables, config-install-interwiki,
-               // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage
-               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() .
-                       wfMessage( 'ellipsis' )->escaped() );
-
-               if ( $step == 'extension-tables' ) {
-                       $this->startLiveBox();
-               }
-       }
-
-       /**
-        * @param string $step
-        * @param Status $status
-        */
-       public function endStage( $step, $status ) {
-               if ( $step == 'extension-tables' ) {
-                       $this->endLiveBox();
-               }
-               $msg = $status->isOk() ? 'config-install-step-done' : 'config-install-step-failed';
-               $html = wfMessage( 'word-separator' )->escaped() . wfMessage( $msg )->escaped();
-               if ( !$status->isOk() ) {
-                       $html = "<span class=\"error\">$html</span>";
-               }
-               $this->addHTML( $html . "</li>\n" );
-               if ( !$status->isGood() ) {
-                       $this->parent->showStatusBox( $status );
-               }
-       }
-
-}
-
-class WebInstallerComplete extends WebInstallerPage {
-
-       public function execute() {
-               // Pop up a dialog box, to make it difficult for the user to forget
-               // to download the file
-               $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) );
-               if ( isset( $_SERVER['HTTP_USER_AGENT'] ) &&
-                       strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false
-               ) {
-                       // JS appears to be the only method that works consistently with IE7+
-                       $this->addHtml( "\n<script>jQuery( function () { location.href = " .
-                               Xml::encodeJsVar( $lsUrl ) . "; } );</script>\n" );
-               } else {
-                       $this->parent->request->response()->header( "Refresh: 0;url=$lsUrl" );
-               }
-
-               $this->startForm();
-               $this->parent->disableLinkPopups();
-               $this->addHTML(
-                       $this->parent->getInfoBox(
-                               wfMessage( 'config-install-done',
-                                       $lsUrl,
-                                       $this->getVar( 'wgServer' ) .
-                                       $this->getVar( 'wgScriptPath' ) . '/index.php',
-                                       '<downloadlink/>'
-                               )->plain(), 'tick-32.png'
-                       )
-               );
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-extension-link' )->text() ) );
-
-               $this->parent->restoreLinkPopups();
-               $this->endForm( false, false );
-       }
-
-}
-
-class WebInstallerRestart extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $really = $r->getVal( 'submit-restart' );
-                       if ( $really ) {
-                               $this->parent->reset();
-                       }
-
-                       return 'continue';
-               }
-
-               $this->startForm();
-               $s = $this->parent->getWarningBox( wfMessage( 'config-help-restart' )->plain() );
-               $this->addHTML( $s );
-               $this->endForm( 'restart' );
-
-               return null;
-       }
-
-}
-
-abstract class WebInstallerDocument extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       abstract protected function getFileName();
-
-       public function execute() {
-               $text = $this->getFileContents();
-               $text = InstallDocFormatter::format( $text );
-               $this->parent->output->addWikiText( $text );
-               $this->startForm();
-               $this->endForm( false );
-       }
-
-       /**
-        * @return string
-        */
-       public function getFileContents() {
-               $file = __DIR__ . '/../../' . $this->getFileName();
-               if ( !file_exists( $file ) ) {
-                       return wfMessage( 'config-nofile', $file )->plain();
-               }
-
-               return file_get_contents( $file );
-       }
-
-}
-
-class WebInstallerReadme extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'README';
-       }
-
-}
-
-class WebInstallerReleaseNotes extends WebInstallerDocument {
-
-       /**
-        * @throws MWException
-        * @return string
-        */
-       protected function getFileName() {
-               global $wgVersion;
-
-               if ( !preg_match( '/^(\d+)\.(\d+).*/i', $wgVersion, $result ) ) {
-                       throw new MWException( 'Variable $wgVersion has an invalid value.' );
-               }
-
-               return 'RELEASE-NOTES-' . $result[1] . '.' . $result[2];
-       }
-
-}
-
-class WebInstallerUpgradeDoc extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'UPGRADE';
-       }
-
-}
-
-class WebInstallerCopying extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'COPYING';
-       }
-
-}
diff --git a/includes/installer/WebInstallerReadme.php b/includes/installer/WebInstallerReadme.php
new file mode 100644 (file)
index 0000000..36bc306
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerReadme extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'README';
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerReleaseNotes.php b/includes/installer/WebInstallerReleaseNotes.php
new file mode 100644 (file)
index 0000000..e185876
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerReleaseNotes extends WebInstallerDocument {
+
+       /**
+        * @throws MWException
+        * @return string
+        */
+       protected function getFileName() {
+               global $wgVersion;
+
+               if ( !preg_match( '/^(\d+)\.(\d+).*/i', $wgVersion, $result ) ) {
+                       throw new MWException( 'Variable $wgVersion has an invalid value.' );
+               }
+
+               return 'RELEASE-NOTES-' . $result[1] . '.' . $result[2];
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerRestart.php b/includes/installer/WebInstallerRestart.php
new file mode 100644 (file)
index 0000000..8717cb8
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerRestart extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $really = $r->getVal( 'submit-restart' );
+                       if ( $really ) {
+                               $this->parent->reset();
+                       }
+
+                       return 'continue';
+               }
+
+               $this->startForm();
+               $s = $this->parent->getWarningBox( wfMessage( 'config-help-restart' )->plain() );
+               $this->addHTML( $s );
+               $this->endForm( 'restart' );
+
+               return null;
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerUpgrade.php b/includes/installer/WebInstallerUpgrade.php
new file mode 100644 (file)
index 0000000..7136120
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerUpgrade extends WebInstallerPage {
+
+       /**
+        * @return bool Always true.
+        */
+       public function isSlow() {
+               return true;
+       }
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               if ( $this->getVar( '_UpgradeDone' ) ) {
+                       // Allow regeneration of LocalSettings.php, unless we are working
+                       // from a pre-existing LocalSettings.php file and we want to avoid
+                       // leaking its contents
+                       if ( $this->parent->request->wasPosted() && !$this->getVar( '_ExistingDBSettings' ) ) {
+                               // Done message acknowledged
+                               return 'continue';
+                       } else {
+                               // Back button click
+                               // Show the done message again
+                               // Make them click back again if they want to do the upgrade again
+                               $this->showDoneMessage();
+
+                               return 'output';
+                       }
+               }
+
+               // wgDBtype is generally valid here because otherwise the previous page
+               // (connect) wouldn't have declared its happiness
+               $type = $this->getVar( 'wgDBtype' );
+               $installer = $this->parent->getDBInstaller( $type );
+
+               if ( !$installer->needsUpgrade() ) {
+                       return 'skip';
+               }
+
+               if ( $this->parent->request->wasPosted() ) {
+                       $installer->preUpgrade();
+
+                       $this->startLiveBox();
+                       $result = $installer->doUpgrade();
+                       $this->endLiveBox();
+
+                       if ( $result ) {
+                               // If they're going to possibly regenerate LocalSettings, we
+                               // need to create the upgrade/secret keys. Bug 26481
+                               if ( !$this->getVar( '_ExistingDBSettings' ) ) {
+                                       $this->parent->generateKeys();
+                               }
+                               $this->setVar( '_UpgradeDone', true );
+                               $this->showDoneMessage();
+
+                               return 'output';
+                       }
+               }
+
+               $this->startForm();
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-can-upgrade', $GLOBALS['wgVersion'] )->plain() ) );
+               $this->endForm();
+
+               return null;
+       }
+
+       public function showDoneMessage() {
+               $this->startForm();
+               $regenerate = !$this->getVar( '_ExistingDBSettings' );
+               if ( $regenerate ) {
+                       $msg = 'config-upgrade-done';
+               } else {
+                       $msg = 'config-upgrade-done-no-regenerate';
+               }
+               $this->parent->disableLinkPopups();
+               $this->addHTML(
+                       $this->parent->getInfoBox(
+                               wfMessage( $msg,
+                                       $this->getVar( 'wgServer' ) .
+                                       $this->getVar( 'wgScriptPath' ) . '/index.php'
+                               )->plain(), 'tick-32.png'
+                       )
+               );
+               $this->parent->restoreLinkPopups();
+               $this->endForm( $regenerate ? 'regenerate' : false, false );
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerUpgradeDoc.php b/includes/installer/WebInstallerUpgradeDoc.php
new file mode 100644 (file)
index 0000000..627ce65
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerUpgradeDoc extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'UPGRADE';
+       }
+
+}
+
diff --git a/includes/installer/WebInstallerWelcome.php b/includes/installer/WebInstallerWelcome.php
new file mode 100644 (file)
index 0000000..e011770
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Deployment
+ */
+
+class WebInstallerWelcome extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               if ( $this->parent->request->wasPosted() ) {
+                       if ( $this->getVar( '_Environment' ) ) {
+                               return 'continue';
+                       }
+               }
+               $this->parent->output->addWikiText( wfMessage( 'config-welcome' )->plain() );
+               $status = $this->parent->doEnvironmentChecks();
+               if ( $status->isGood() ) {
+                       $this->parent->output->addHTML( '<span class="success-message">' .
+                               wfMessage( 'config-env-good' )->escaped() . '</span>' );
+                       $this->parent->output->addWikiText( wfMessage( 'config-copyright',
+                               SpecialVersion::getCopyrightAndAuthorList() )->plain() );
+                       $this->startForm();
+                       $this->endForm();
+               } else {
+                       $this->parent->showStatusMessage( $status );
+               }
+
+               return '';
+       }
+
+}
+
+
index 9ddbfa4..e9ca48b 100644 (file)
@@ -77,6 +77,7 @@
        "config-apc": "Je nainstalováno [http://www.php.net/apc APC]",
        "config-wincache": "Je nainstalována [http://www.iis.net/download/WinCacheForPhp WinCache]",
        "config-no-cache": "'''Upozornění:''' Nebylo nalezeno [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache], ani [http://www.iis.net/download/WinCacheForPhp WinCache].\nKešování objektů bude vypnuto.",
+       "config-no-cache-apcu": "<strong>Upozornění:</strong> Nebylo nalezeno [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache], ani [http://www.iis.net/download/WinCacheForPhp WinCache].\nKešování objektů bude vypnuto.",
        "config-mod-security": "'''Upozornění''': váš webový server má zapnuto [http://modsecurity.org/ mod_security]. Při chybné konfiguraci může způsobovat potíže MediaWiki či dalším programům, které umožňují ukládat libovolný obsah.\nPokud narazíte na náhodné chyby, podívejte se do [http://modsecurity.org/documentation/ dokumentace mod_security] nebo kontaktujte technickou podporu vašeho poskytovatele.",
        "config-diff3-bad": "Nebyl nalezen GNU diff3.",
        "config-git": "Nalezen software pro správu verzí Git: <code>$1</code>.",
index c23a268..53a4d9a 100644 (file)
@@ -84,6 +84,7 @@
        "config-apc": "[http://www.php.net/apc APC] ist installiert",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] ist installiert",
        "config-no-cache": "'''Warnung:''' [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] oder [http://www.iis.net/download/WinCacheForPhp WinCache] wurden nicht gefunden.\nDas Objektcaching kann daher nicht aktiviert werden.",
+       "config-no-cache-apcu": "<strong>Warnung:</strong> [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] oder [http://www.iis.net/download/WinCacheForPhp WinCache] konnten nicht gefunden werden.\nDer Objektcache ist nicht aktiviert.",
        "config-mod-security": "'''Warnung:''' Auf dem Webserver wurde [http://modsecurity.org/ ModSecurity] aktiviert. Sofern falsch konfiguriert, kann dies zu Problemen mit MediaWiki sowie anderer Software auf dem Server führen und es Benutzern ermöglichen, beliebige Inhalte im Wiki einzustellen.\nFür weitere Informationen empfehlen wir die [http://modsecurity.org/documentation/ Dokumentation zu ModSecurity] oder den Kontakt zum Hoster, sofern Fehler auftreten.",
        "config-diff3-bad": "GNU diff3 wurde nicht gefunden.",
        "config-git": "Die Versionsverwaltungssoftware „Git“ wurde gefunden: <code>$1</code>.",
index ac3178f..f2ec7a7 100644 (file)
@@ -69,6 +69,7 @@
        "config-apc": "[http://www.php.net/apc APC] is installed",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] is installed",
        "config-no-cache": "<strong>Warning:</strong> Could not find [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] or [http://www.iis.net/download/WinCacheForPhp WinCache].\nObject caching is not enabled.",
+       "config-no-cache-apcu": "<strong>Warning:</strong> Could not find [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] or [http://www.iis.net/download/WinCacheForPhp WinCache].\nObject caching is not enabled.",
        "config-mod-security": "<strong>Warning:</strong> Your web server has [http://modsecurity.org/ mod_security]/mod_security2 enabled. Many common configurations of this will cause problems for MediaWiki and other software that allows users to post arbitrary content.\nIf possible, this should be disabled. Otherwise, refer to [http://modsecurity.org/documentation/ mod_security documentation] or contact your host's support if you encounter random errors.",
        "config-diff3-bad": "GNU diff3 not found.",
        "config-git": "Found the Git version control software: <code>$1</code>.",
index bb42d88..372b228 100644 (file)
@@ -87,6 +87,7 @@
        "config-apc": "Message indicates if this program is available",
        "config-wincache": "Message indicates if this program is available",
        "config-no-cache": "Status message in the MediaWiki installer environment checks.",
+       "config-no-cache-apcu": "Status message in the MediaWiki installer environment checks.",
        "config-mod-security": "Status message in the MediaWiki installer environment checks.",
        "config-diff3-bad": "Status message in the MediaWiki installer environment checks.",
        "config-git": "Message if Git version control software is available.\nParameter:\n* $1 is the <code>Git</code> executable file name.",
index c987980..1a1cc44 100644 (file)
@@ -18,7 +18,8 @@
                        "Meshkov.a",
                        "Eroha",
                        "Seb35",
-                       "Striking Blue"
+                       "Striking Blue",
+                       "Ильнар"
                ]
        },
        "config-desc": "Инсталлятор MediaWiki",
@@ -41,7 +42,7 @@
        "config-back": "← Назад",
        "config-continue": "Далее →",
        "config-page-language": "Язык",
-       "config-page-welcome": "Добро пожаловать в MediaWiki!",
+       "config-page-welcome": "MediaWiki проектына рәхим итегез!",
        "config-page-dbconnect": "Подключение к базе данных",
        "config-page-upgrade": "Обновление существующей установки",
        "config-page-dbsettings": "Настройки базы данных",
index 13cd520..cd3403d 100644 (file)
@@ -6,6 +6,9 @@
                        "Ильнар"
                ]
        },
+       "config-desc": "MediaWiki йөкләүче",
+       "config-title": "MediaWiki $1 куелышы",
+       "config-information": "Мәгълүмат",
        "config-back": "← Артка",
        "config-continue": "Киләсе →",
        "config-page-language": "Тел",
index 5e7ab06..d64e7f5 100644 (file)
@@ -92,6 +92,7 @@
        "config-apc": "[http://www.php.net/apc APC]已安装",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache]已安装",
        "config-no-cache": "'''警告:'''找不到[http://www.php.net/apc APC]、[http://xcache.lighttpd.net/ XCache]或[http://www.iis.net/download/WinCacheForPhp WinCache],无法启用对象缓存。\nObject caching is not enabled.",
+       "config-no-cache-apcu": "<strong>警告:</strong>找不到[http://www.php.net/apcu APCu]、[http://xcache.lighttpd.net/ XCache]或[http://www.iis.net/download/WinCacheForPhp WinCache]。\n对象缓存未启用。",
        "config-mod-security": "'''警告''':您的服务器已启动[http://modsecurity.org/ mod_security]。若其配置错误, 会导致MediaWiki和其他软件的错误并允许用户任意发布内容。如果您遇到任何错误,请查阅[http://modsecurity.org/documentation/ mod_security文档]或联系您的客服。",
        "config-diff3-bad": "找不到GNU diff3。",
        "config-git": "发现Git版本控制软件:<code>$1</code>",
index 796acb5..bb55ac6 100644 (file)
@@ -27,9 +27,11 @@ class ComposerJson {
         */
        public function getRequiredDependencies() {
                $deps = array();
-               foreach ( $this->contents['require'] as $package => $version ) {
-                       if ( $package !== "php" && strpos( $package, 'ext-' ) !== 0 ) {
-                               $deps[$package] = self::normalizeVersion( $version );
+               if ( isset( $this->contents['require'] ) ) {
+                       foreach ( $this->contents['require'] as $package => $version ) {
+                               if ( $package !== "php" && strpos( $package, 'ext-' ) !== 0 ) {
+                                       $deps[$package] = self::normalizeVersion( $version );
+                               }
                        }
                }
 
index aac0c79..6ac25e8 100644 (file)
@@ -65,6 +65,7 @@ class ExtensionProcessor implements Processor {
                'wgNamespaceContentModels' => 'array_plus',
                'wgNamespaceProtection' => 'array_plus',
                'wgCapitalLinkOverrides' => 'array_plus',
+               'wgRateLimits' => 'array_plus_2d',
        );
 
        /**
index 47d24dc..1b6e9d6 100644 (file)
@@ -107,11 +107,6 @@ class FakeConverter {
                return $key;
        }
 
-       /** @deprecated since 1.22 is no longer used */
-       function armourMath( $text ) {
-               return $text;
-       }
-
        function validateVariant( $variant = null ) {
                return $variant === $this->mLang->getCode() ? $variant : null;
        }
index 69f518b..cb2d24f 100644 (file)
@@ -4157,17 +4157,6 @@ class Language {
                return (bool)$this->mConverter->validateVariant( $variant );
        }
 
-       /**
-        * Put custom tags (e.g. -{ }-) around math to prevent conversion
-        *
-        * @param string $text
-        * @return string
-        * @deprecated since 1.22 is no longer used
-        */
-       public function armourMath( $text ) {
-               return $this->mConverter->armourMath( $text );
-       }
-
        /**
         * Perform output conversion on a string, and encode for safe HTML output.
         * @param string $text Text to be converted
index 78b3572..b00aa34 100644 (file)
@@ -1084,22 +1084,6 @@ class LanguageConverter {
                return true;
        }
 
-       /**
-        * Armour rendered math against conversion.
-        * Escape special chars in parsed math text. (in most cases are img elements)
-        *
-        * @param string $text Text to armour against conversion
-        * @return string Armoured text where { and } have been converted to
-        *   &#123; and &#125;
-        * @deprecated since 1.22 is no longer used
-        */
-       public function armourMath( $text ) {
-               // convert '-{' and '}-' to '-&#123;' and '&#125;-' to prevent
-               // any unwanted markup appearing in the math image tag.
-               $text = strtr( $text, array( '-{' => '-&#123;', '}-' => '&#125;-' ) );
-               return $text;
-       }
-
        /**
         * Get the cached separator pattern for ConverterRule::parseRules()
         * @return string
index 9b0254a..3520f64 100644 (file)
        "unblock": "Desbloquiar usuariu",
        "blockip": "Bloquiar {{GENDER:$1|al usuariu|a la usuaria}}",
        "blockip-legend": "Bloquiar usuariu",
-       "blockiptext": "Usa'l siguiente formulariu pa bloquiar el permisu d'escritura a una IP o a un usuariu concretu.\nEsto debería facese sólo pa prevenir vandalismu como indiquen les [[{{MediaWiki:Policy-url}}|polítiques]]. Da un motivu específicu (como por exemplu citar páxines que fueron vandalizaes).",
+       "blockiptext": "Usa'l siguiente formulariu pa bloquiar l'accesu d'escritura a una direición IP o a un usuariu concretu.\nEsto debería facese sólo pa prevenir vandalismu como indiquen les [[{{MediaWiki:Policy-url}}|polítiques]]. Da un motivu específicu (como por exemplu citar páxines que fueron vandalizaes).\nPues bloquiar rangos d'IPs usando la sintaxis [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; el mayor rangu permitíu ye /$1 pa IPv4 y /$2 pa IPv6.",
        "ipaddressorusername": "Direición IP o nome d'usuariu:",
        "ipbexpiry": "Caducidá:",
        "ipbreason": "Motivu:",
        "pageinfo-category-files": "Númberu de ficheros",
        "markaspatrolleddiff": "Marcar como supervisada",
        "markaspatrolledtext": "Marcar esta páxina como supervisada",
+       "markaspatrolledtext-file": "Marcar esta versión del ficheru como patrullada",
        "markedaspatrolled": "Marcar como supervisada",
        "markedaspatrolledtext": "La revisión seleicionada de [[:$1]] se marcó como supervisada.",
        "rcpatroldisabled": "Supervisión de cambios recientes desactivada",
        "newimages-legend": "Peñera",
        "newimages-label": "Nome d'archivu (o una parte d'él):",
        "newimages-showbots": "Ver les xubíes de los bots",
+       "newimages-hidepatrolled": "Despintar les entraes patrullaes",
        "noimages": "Nun hai nada que ver.",
        "ilsubmit": "Guetar",
        "bydate": "por fecha",
index a212a0e..988c912 100644 (file)
        "currentrevisionlink": "سون نوسخه",
        "cur": "ایندی",
        "next": "سونراکی",
-       "last": "اؤنجه",
+       "last": "قاباقکی",
        "page_first": "ایلک",
        "page_last": "سون",
-       "histlegend": "فرقلری سئچمه: موقاییسه ائتمک ایسته‌دیگینیز دییشیک‌لیکلرین یانینداکی گیرده دویمه‌لره علامت قویون و سونرا Enter-ی ووروب یوخسا آشاغیداکی اویمه‌نی وورون.<br />\nآچیقلاما:'''({{int:cur}})''' =سون نوسخه ایله فرقلر ، '''({{int:last}})''' = اؤنجه‌کی نوسخه ایله فرقلر، '''{{int:minoreditletter}}''' = کیچیک دییشیک‌لیک.",
+       "histlegend": "فرقلری سئچمه: موقاییسه ائتمک ایسته‌دیگینیز دییشیک‌لیکلرین یانینداکی گیرده دۆیمه‌لره علامت قویون و سوْنرا Enter-ی وۇروب یوْخسا آشاغیداکی دۆیمه‌نی وورون.<br />\nآچیقلاما:'''({{int:cur}})''' =سون نوسخه ایله فرقلر ، '''({{int:last}})''' = قاباقکی نوسخه ایله فرقلر، '''{{int:minoreditletter}}''' = کیچیک دییشیک‌لیک.",
        "history-fieldset-title": "گئچمیشی آختار",
        "history-show-deleted": "یالنیز سیلینَنلر",
        "histfirst": "ان اسکی",
        "shown-title": "هر صفحه‌ده {{PLURAL:$1|بیر|$1}} نتیجه گؤرست",
        "viewprevnext": "گؤستر ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''بو ویکی‌ده «[[:$1]]» آدلی صحیفه واردیر.'''",
-       "searchmenu-new": "<strong>بو ویکی‌ده «[[:$1]]» صحیفه‌‌سینی يارات!</strong> {{PLURAL:$2|0=|هابئله تاپیلمیش صفحه نی آختاریشینیزلا گورون.|هابئله تاپیلمیش آختاریشین نتیجه سین گورون.}}",
+       "searchmenu-new": "<strong>بۇ ویکی‌ده «[[:$1]]» صفحه‌‌سینی يارات!</strong> {{PLURAL:$2|0=|هابئله تاپیلمیش صفحه‌نی آختاریشینیزلا گؤرون.|هابئله تاپیلمیش آختاریشین نتیجه‌سین گؤرون.}}",
        "searchprofile-articles": "مقاله‌لر",
        "searchprofile-images": "مولتی‌مئدیا",
        "searchprofile-everything": "هرشئی",
        "contributions-userdoesnotexist": "«$1» ایشلدن حسابی ثبت اولونماییب‌دیر.",
        "nocontribs": "بو موشخصاتا اویغون دییشدیر تاپیلمادی",
        "uctop": "(ایندیکی)",
-       "month": "بو آی‌دان (و اؤنجه‌سی):",
-       "year": "بو ایل‌دن (و اؤنجه‌سی):",
+       "month": "بۇ آی‌دان (و قاباقجا):",
+       "year": "بۇ ایل‌دن (و قاباقجا):",
        "sp-contributions-newbies": "تکجه یئنی ایشلدنلرین چالیشمالارینی گؤستر",
        "sp-contributions-newbies-sub": "یئنی ایستیفاده‌چی‌لر اوچون",
        "sp-contributions-newbies-title": "یئنی حساب‌لار اوچون ایستیفاده‌چی فالیت‌لری",
        "sp-contributions-search": "چالیشمالاری آختار",
        "sp-contributions-username": "آی‌پی آدرسی یوْخسا ایشلدن آدی:",
        "sp-contributions-toponly": "تکجه سون نوسخه اولان دییشیکلری گؤستر",
-       "sp-contributions-newonly": "یالنیز صفحه یاراتماق دَییشیکلیکلرینی گؤستر",
+       "sp-contributions-newonly": "تکجه صفحه یاراتماق دَییشیکلیکلرینی گؤستر",
        "sp-contributions-submit": "آختار",
        "whatlinkshere": "بو صفحه‌یه باغلانتیلار",
        "whatlinkshere-title": "«$1»-ه باغلانان صحیفه‌لر",
        "isredirect": "یوللاندیرما صفحه‌سی",
        "istemplate": "داخیل اولموش",
        "isimage": "فایلا باغلانتی",
-       "whatlinkshere-prev": "{{PLURAL:$1|اؤنجه‌کی|اؤنجه‌کی $1}}",
+       "whatlinkshere-prev": "{{PLURAL:$1|قاباقکی|قاباقکی $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|سونراکی|سونراکی $1}}",
        "whatlinkshere-links": "← باغلانتیلار",
        "whatlinkshere-hideredirs": "یول‌لاندیرمالاری $1",
        "htmlform-user-not-valid": "<strong>$1</strong> بیر دوزگون ایشلدن آدی دئییل.",
        "sqlite-has-fts": "$1 بوتون یازی آختارما دستگی‌له",
        "sqlite-no-fts": "$1 بوتون یازی آختارماماق‌لا",
-       "logentry-delete-delete": "$1، $3 صحیفه‌سینی {{GENDER:$2|سیلدی}}",
-       "logentry-delete-restore": "$1، $3 صحیفه‌سینی {{GENDER:$2|قایتاردی}}",
+       "logentry-delete-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|سیلدی}}",
+       "logentry-delete-restore": "$1، $3 صفحه‌سینی {{GENDER:$2|قایتاردی}}",
        "logentry-delete-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-delete-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} {{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-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|یاتیردی}}",
        "logentry-suppress-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-suppress-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-suppress-event-legacy": "$1، $3-ده سیاهی اولایلارینین گؤرونوشلرینی گیزلینجه {{GENDER:$2|دَییشدیردی}}",
        "revdelete-unrestricted": "ایداره‌چیلرین محدودیتلرینی گؤتوردو",
        "logentry-block-block": "$1 {{GENDER:$4|$3}}-نی {{GENDER:$2|بلوکلادی}}. قورتارماق تاریخی: $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$4|$3}}-نین {{GENDER:$2|بلوکلاماغینی قالدیردی}}",
-       "logentry-move-move": "$1، $3 صحیفه‌سینی $4-ه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move-noredirect": "$1، $3 صحیفه‌سینی، یول‌لاندیرما قویماماق‌لا، $4-ه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move_redir": "$1، $3 صحیفه‌سینی، $4-ده یول‌لاندیرما اوستونه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move_redir-noredirect": "$1، $3 صحیفه‌سینی، یول‌لاندیرما قویماماق‌لا، یول‌لاندیرما اولان $4 اوستونه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move": "$1، $3 صفحه‌سینی $4-ه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move-noredirect": "$1، $3 صفحه‌سینی، یوْل‌لاندیرما قوْیماماق‌لا، $4-ه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move_redir": "$1، $3 صفحه‌سینی، $4-ده یوْل‌لاندیرما اۆستونه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move_redir-noredirect": "$1، $3 صفحه‌سینی، یوْل‌لاندیرما قوْیماماق‌لا، یوْل‌لاندیرما اوْلان $4 اۆستونه {{GENDER:$2|آپاردی}}",
        "logentry-patrol-patrol": "$1، $3 صحیفه‌سینین $4 نوسخه‌سینی، نظارتلنمیش {{GENDER:$2|نیشانلادی}}",
        "logentry-patrol-patrol-auto": "$1، $3 صحیفه‌سینین $4 نوسخه‌سینی، اوتوماتیک اولاراق نظارتلنمیش {{GENDER:$2|نیشانلادی}}",
        "logentry-newusers-newusers": " بیر ایستیفاده‌چی حسابی $1 {{GENDER:$2|یاراتدی}}",
index edf10c8..fd6b239 100644 (file)
        "unblock": "Разблякаваць удзельніка",
        "blockip": "Заблякаваць {{GENDER:$1|удзельніка|удзельніцу}}",
        "blockip-legend": "Заблякаваць удзельніка",
-       "blockiptext": "Наступная форма дазваляе заблякаваць магчымасьць рэдагаваньня з пэўнага IP-адрасу альбо імя ўдзельніка. Гэта трэба рабіць толькі дзеля прадухіленьня вандалізму і згодна з [[{{MediaWiki:Policy-url}}|правіламі]]. Пазначце ніжэй дакладную прычыну (напрыклад, пералічыце асобныя старонкі, на якіх былі парушэньні).",
+       "blockiptext": "Наступная форма дазваляе заблякаваць магчымасьць рэдагаваньня з пэўнага IP-адрасу альбо імя ўдзельніка. Гэта трэба рабіць толькі дзеля прадухіленьня вандалізму і згодна з [[{{MediaWiki:Policy-url}}|правіламі]]. Пазначце ніжэй дакладную прычыну (напрыклад, пералічыце асобныя старонкі, на якіх былі парушэньні).\nВы можаце блякаваць IP-дыяпазоны з дапамогай [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-сынтаксысу; найбольшы дазволены дыяпазоны — гэта /$1 для IPv4 і /$2 для IPv6.",
        "ipaddressorusername": "IP-адрас альбо імя ўдзельніка/ўдзельніцы:",
        "ipbexpiry": "Тэрмін:",
        "ipbreason": "Прычына:",
        "pageinfo-category-files": "Колькасьць файлаў",
        "markaspatrolleddiff": "Пазначыць як «патруляваную»",
        "markaspatrolledtext": "Пазначыць гэтую старонку як «патруляваную»",
+       "markaspatrolledtext-file": "Пазначыць гэтую вэрсію файлу як патруляваную",
        "markedaspatrolled": "Пазначаная як «патруляваная»",
        "markedaspatrolledtext": "Выбраная вэрсія [[:$1]] пазначаная як «патруляваная».",
        "rcpatroldisabled": "Патруляваньне апошніх зьменаў адключанае",
index 50b3ffb..0f3eefe 100644 (file)
@@ -32,7 +32,8 @@
                        "Лорд Бъмбъри",
                        "Matma Rex",
                        "Xð",
-                       "Miroslav35232"
+                       "Miroslav35232",
+                       "Ket"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
        "laggedslavemode": "Внимание: Страницата може да не съдържа последните обновявания.",
        "readonly": "Базата от данни е затворена за промени",
        "enterlockreason": "Посочете причина за затварянето, като дадете и приблизителна оценка кога базата от данни ще бъде отново отворена",
-       "readonlytext": "Ð\91азаÑ\82а Ð¾Ñ\82 Ð´Ð°Ð½Ð½Ð¸ Ðµ Ð²Ñ\80еменно Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ñ\80омени â\80\94 Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð° Ñ\80Ñ\83Ñ\82инна Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ñ\81лед ÐºÐ¾Ñ\8fÑ\82о Ñ\89е Ð±Ñ\8aде Ð¾Ñ\82ново Ð½Ð° Ñ\80азположение.\nАдминистраторът, който я е затворил, дава следното обяснение:\n$1",
+       "readonlytext": "Ð\91азаÑ\82а Ð´Ð°Ð½Ð½Ð¸ Ðµ Ð²Ñ\80еменно Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ñ\80омени â\80\94 Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð° Ñ\80Ñ\83Ñ\82инна Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ñ\81лед ÐºÐ¾Ñ\8fÑ\82о Ñ\89е Ð±Ñ\8aде Ð¾Ñ\82ново Ð´Ð¾Ñ\81Ñ\82Ñ\8aпна.\nАдминистраторът, който я е затворил, дава следното обяснение:\n$1",
        "missing-article": "В базата от данни не беше открит текста на страницата „$1“ $2.\n\nТова обикновено се случва при последване на остаряла разликова връзка или връзка към историята на междувременно изтрита страница.\n\nАко все пак случаят не е такъв, причината вероятно е софтуерен бъг.\nМоля, докладвайте на [[Special:ListUsers/sysop|администратор]] за проблема, като предоставите уеб адреса за връзка.",
        "missingarticle-rev": "(версия#: $1)",
        "missingarticle-diff": "(Разлика: $1, $2)",
        "viewsource": "Преглед на кода",
        "viewsource-title": "Преглеждане на кода на $1",
        "actionthrottled": "Ограничение в скоростта",
-       "actionthrottledtext": "Като част от защитата против спам, многократното повтаряне на това действие за кратък период от време е ограничено и вие вече сте надвишили лимита си. Опитайте отново след няколко минути.",
+       "actionthrottledtext": "Като част от защитата против спам, многократното повтаряне на това действие за кратък период от време е ограничено и вие вече сте надвишили лимита. Моля опитайте отново след няколко минути.",
        "protectedpagetext": "Тази страница е защитена, за да се предотвратят редактиране или други действия.",
        "viewsourcetext": "Можете да разгледате и да копирате кодa на страницата.",
        "viewyourtext": "Можете да прегледате и копирате изходния код на <strong>вашите редакции</strong> на тази страница.",
        "copyrightwarning": "Обърнете внимание, че всички приноси към {{SITENAME}} се публикуват при условията на $2 (за подробности вижте $1).\nАко не сте съгласни вашата писмена работа да бъде променяна и разпространявана без ограничения, не я публикувайте.<br />\n\nСъщо потвърждавате, че '''вие''' сте написали материала или сте използвали '''свободни ресурси''' — <em>обществено достояние</em> или друг свободен източник.\nАко сте ползвали чужди материали, за които имате разрешение, непременно посочете източника.\n\n<div style=\"font-variant:small-caps\">'''Не публикувайте произведения с авторски права без разрешение!'''</div>",
        "copyrightwarning2": "Обърнете внимание, че всички приноси към {{SITENAME}} могат да бъдат редактирани, променяни или премахвани от останалите сътрудници.\nАко не сте съгласни вашата писмена работа да бъде променяна без ограничения, не я публикувайте.<br />\nСъщо потвърждавате, че '''вие''' сте написали материала или сте използвали '''свободни ресурси''' — <em>обществено достояние</em> или друг свободен източник (за подробности вижте $1).\nАко сте ползвали чужди материали, за които имате разрешение, непременно посочете източника.\n\n<div style=\"font-variant:small-caps\">'''Не публикувайте произведения с авторски права без разрешение!'''</div>",
        "longpageerror": "'''ГРЕШКА: Изпратеният текст е с големина {{PLURAL:$1|един килобайт|$1 килобайта}}, което надвишава позволения максимум от {{PLURAL:$2|един килобайт|$2 килобайта}}.'''\nПоради тази причина той не може да бъде съхранен.",
-       "readonlywarning": "<strong>Ð\92Ð\9dÐ\98Ð\9cÐ\90Ð\9dÐ\98Ð\95: Ð\91азаÑ\82а Ð¾Ñ\82 Ð´Ð°Ð½Ð½Ð¸ Ð±ÐµÑ\88е Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ð·Ð°Ñ\82ова Ð² Ð¼Ð¾Ð¼ÐµÐ½Ñ\82а Ð¿Ñ\80омениÑ\82е Ð½Ñ\8fма Ð´Ð° Ð¼Ð¾Ð³Ð°Ñ\82 Ð´Ð° Ð±Ñ\8aдаÑ\82 Ñ\81Ñ\8aÑ\85Ñ\80анени.</strong>\n\nÐ\90ко Ð¶ÐµÐ»Ð°ÐµÑ\82е, Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° Ñ\81Ñ\8aÑ\85Ñ\80аниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а ÐºÐ°Ñ\82о Ñ\82екÑ\81Ñ\82ов Ñ\84айл Ð¸ Ð´Ð° Ñ\81е Ð¾Ð¿Ð¸Ñ\82аÑ\82е Ð´Ð° Ñ\8f Ð¿Ñ\83бликÑ\83ваÑ\82е Ð¿Ð¾-кÑ\8aÑ\81но.\n\nСиÑ\81Ñ\82емниÑ\8fÑ\82 Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80, ÐºÐ¾Ð¹Ñ\82о Ðµ Ð·Ð°Ñ\82воÑ\80ил Ð±Ð°Ð·Ð°Ñ\82а Ð¾Ñ\82 данни, е посочил следната причина: $1",
+       "readonlywarning": "<strong>Ð\92Ð\9dÐ\98Ð\9cÐ\90Ð\9dÐ\98Ð\95: Ð\91азаÑ\82а Ð´Ð°Ð½Ð½Ð¸ Ð±ÐµÑ\88е Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ð·Ð°Ñ\82ова Ð² Ð¼Ð¾Ð¼ÐµÐ½Ñ\82а Ð¿Ñ\80омениÑ\82е Ð½Ñ\8fма Ð´Ð° Ð¼Ð¾Ð³Ð°Ñ\82 Ð´Ð° Ð±Ñ\8aдаÑ\82 Ñ\81Ñ\8aÑ\85Ñ\80анени.</strong>\n\nÐ\90ко Ð¶ÐµÐ»Ð°ÐµÑ\82е, Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° Ñ\81Ñ\8aÑ\85Ñ\80аниÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а ÐºÐ°Ñ\82о Ñ\82екÑ\81Ñ\82ов Ñ\84айл Ð¸ Ð´Ð° Ñ\81е Ð¾Ð¿Ð¸Ñ\82аÑ\82е Ð´Ð° Ñ\8f Ð¿Ñ\83бликÑ\83ваÑ\82е Ð¿Ð¾-кÑ\8aÑ\81но.\n\nСиÑ\81Ñ\82емниÑ\8fÑ\82 Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80, ÐºÐ¾Ð¹Ñ\82о Ðµ Ð·Ð°Ñ\82воÑ\80ил Ð±Ð°Ð·Ð°Ñ\82а данни, е посочил следната причина: $1",
        "protectedpagewarning": "'''Внимание: Страницата е защитена и само потребители със статут на администратори могат да я редактират.'''\nЗа справка по-долу е показан последният запис от дневниците.",
        "semiprotectedpagewarning": "'''Забележка:''' Тази страница е защитена и само регистрирани потребители могат да я редактират.\nЗа справка по-долу е показан последният запис от дневниците.",
        "cascadeprotectedwarning": "<strong>Внимание:</strong> Страницата е защитена, като само потребители с администраторски права могат да я редактират. Тя е включена в {{PLURAL:$1|следната страница|следните страници}} с каскадна защита:",
        "revdelete-show-file-submit": "Да",
        "logdelete-selected": "{{PLURAL:$1|Избрано събитие|Избрани събития}}:",
        "revdelete-confirm": "Необходимо е да потвърдите, че желаете да извършите действието, разбирате последствията и го правите според [[{{MediaWiki:Policy-url}}|политиката]].",
-       "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n*Неподходяща или неприемлива лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
+       "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n* Потенциално уязвима в правно отношение информация\n* Неподходяща лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
        "revdelete-legend": "Задаване на ограничения:",
        "revdelete-hide-text": "Текст на версията",
        "revdelete-hide-image": "Скриване на файловото съдържание",
        "prefs-watchlist-token": "Уникален идентификатор на списъка за наблюдение:",
        "prefs-misc": "Други",
        "prefs-resetpass": "Промяна на паролата",
-       "prefs-changeemail": "Промяна на е-поща",
+       "prefs-changeemail": "Ð\9fÑ\80омÑ\8fна Ð¸Ð»Ð¸ Ð¿Ñ\80емаÑ\85ване Ð½Ð° Ðµ-поÑ\89а",
        "prefs-setemail": "Настройка на адрес за е-поща",
        "prefs-email": "Настройки за електронната поща",
        "prefs-rendering": "Облик",
        "gender-female": "Тя редактира уики страниците",
        "prefs-help-gender": "По желание: използва се за коректно обръщение по род в системните съобщения на софтуера. Тази информация е публично достъпна.",
        "email": "Е-поща",
-       "prefs-help-realname": "* <strong>Истинско име</strong> <em>(незадължително)</em>: Ако го посочите, на него ще бъдат приписани вашите приноси.",
+       "prefs-help-realname": "* Истинското име не е задължително. Ако го посочите, вашите приноси ще бъдат приписани на него.",
        "prefs-help-email": "Електронната поща е незадължителна, но позволява възстановяване на забравена или загубена парола.",
        "prefs-help-email-others": "Можете да изберете да позволите на другите да се свързват с вас по електронна поща, като щракват на препратка от вашата лична потребителска страница или беседа. \nАдресът на електронната ви поща не се разкрива на потребителите, които се свързват с вас по този начин.",
        "prefs-help-email-required": "Изисква се адрес за електронна поща.",
        "boteditletter": "б",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдаващ потребител|наблюдаващи потребители}}]",
        "rc_categories": "Само от категории (разделител „|“)",
-       "rc_categories_any": "Която и да е",
+       "rc_categories_any": "Която и да е от избраните",
        "rc-change-size-new": "$1 {{PLURAL:$1|байт|байта}} след редакцията",
        "newsectionsummary": "Нова тема /* $1 */",
        "rc-enhanced-expand": "Показване на детайли",
        "mywatchlist": "Списък за наблюдение",
        "watchlistfor2": "За $1 $2",
        "nowatchlist": "Списъкът ви за наблюдение е празен.",
-       "watchlistanontext": "За преглеждане и редактиране на списъка за наблюдение се изисква $1 в системата.",
+       "watchlistanontext": "За преглеждане и редактиране на списъка за наблюдение се изисква влизане в системата.",
        "watchnologin": "Не сте влезли",
        "addwatch": "Добавяне към списъка за наблюдение",
-       "addedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e'''[[:$1]]'''â\80\9c Ð±ÐµÑ\88е Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð° ÐºÑ\8aм [[Special:Watchlist|Ñ\81пиÑ\81Ñ\8aка Ð²Ð¸ Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение]].\nÐ\9dейниÑ\82е Ð±Ñ\8aдеÑ\89и Ð¿Ñ\80омени, ÐºÐ°ÐºÑ\82о Ð¸ Ð½Ð° Ñ\81Ñ\8aоÑ\82веÑ\82наÑ\82а Ð¹ Ð´Ð¸Ñ\81кÑ\83Ñ\81ионна Ñ\81Ñ\82Ñ\80аниÑ\86а, Ñ\89е Ñ\81е Ð¾Ð¿Ð¸Ñ\81ваÑ\82 Ñ\82ам.",
+       "addedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e'''[[:$1]]'''â\80\9c Ð¸ Ð±ÐµÑ\81едаÑ\82а Ð¹ Ð±Ñ\8fÑ\85а Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð¸ ÐºÑ\8aм [[Special:Watchlist|Ñ\81пиÑ\81Ñ\8aка Ð²Ð¸ Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение]].",
        "addedwatchtext-short": "Страницата „$1“ беше добавена към списъка Ви за наблюдение.",
        "removewatch": "Премахване от списъка за наблюдение",
-       "removedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð±ÐµÑ\88е Ð¿Ñ\80емаÑ\85наÑ\82а от [[Special:Watchlist|списъка ви за наблюдение]].",
+       "removedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð¸ Ð±ÐµÑ\81едаÑ\82а Ð¹ Ð±Ñ\8fÑ\85а Ð¿Ñ\80емаÑ\85наÑ\82и от [[Special:Watchlist|списъка ви за наблюдение]].",
        "removedwatchtext-short": "Страницата „$1“ беше премахната от списъка Ви за наблюдение.",
        "watch": "Наблюдение",
        "watchthispage": "Наблюдаване на страницата",
        "wlshowhidebots": "ботове",
        "wlshowhideliu": "регистрирани потребители",
        "wlshowhideanons": "анонимни потребители",
-       "wlshowhidepatr": "проверени редакции",
+       "wlshowhidepatr": "патрулирани редакции",
        "wlshowhidemine": "моите редакции",
        "watchlist-options": "Опции на списъка за наблюдение",
        "watching": "Наблюдение…",
        "exbeforeblank": "премахнато преди това съдържание: „$1“",
        "delete-confirm": "Изтриване на „$1“",
        "delete-legend": "Изтриване",
-       "historywarning": "\"Внимание:</strong>  Страницата, която възнамерявате да изтриете, има история с приблизително $1 {{PLURAL:$1|редакция|редакции}}:",
+       "historywarning": "<strong>Внимание:</strong> Страницата, която възнамерявате да изтриете, има история с приблизително $1 {{PLURAL:$1|редакция|редакции}}:",
        "historyaction-submit": "Показване",
        "confirmdeletetext": "На път сте безвъзвратно да изтриете страница или файл, заедно с цялата прилежаща редакционна история, от базата от данни.\nПотвърдете, че искате това, разбирате последствията и правите това в съответствие с [[{{MediaWiki:Policy-url}}|линията на поведение]].",
        "actioncomplete": "Действието беше изпълнено",
index d4e13d0..3c1156c 100644 (file)
        "userrights-groupsmember": "সদস্য:",
        "userrights-groupsmember-auto": "শর্তহীন সদস্য",
        "userrights-groups-help": "আপনি এই ব্যবহারকারীর বর্তমান দল পরিবর্তন করতে পারবেন:\n* টিক চিহ্ন দেওয়া ঘরের অর্থ ব্যবহারকারী এখন ঐ দলের অন্তর্ভুক্ত।\n* টিক চিহ্ন বিহীন ঘরের অর্থ ব্যবহারকারী ঐ দলের অন্তর্ভুক্ত নন।\n* একটি তারকা চিহ্ন (*) দ্বারা বোঝানো হচ্ছে এই দলের অন্তর্ভুক্তির পর আপনি আর তা বাতিল করতে পারবেন না।",
-       "userrights-reason": "কারণ:",
+       "userrights-reason": "কারণ (বাংলায় লিখুন):",
        "userrights-no-interwiki": "আপনার অন্য উইকিতে ব্যবহারকারী অধিকার সম্পাদনা করার অনুমতি নেই।",
        "userrights-nodatabase": "$1 ডাটাবেজটির হয় কোন অস্তিত্ব নেই অথবা এটি স্থানীয় ডাটাবেজ নয়।",
        "userrights-nologin": "ব্যবহারকারী অধিকার প্রযুক্ত করতে হলে আপনাকে কোন প্রশাসকের অ্যাকাউন্টে [[Special:UserLogin|প্রবেশ]] করতে হবে।",
        "protect-cascadeon": "এই পাতাটি বর্তমানে সুরক্ষিত আছে, কারণ পাতাটি নিচের {{PLURAL:$1|পাতায়|পাতাগুলিতে}} অন্তর্ভুক্ত, {{PLURAL:$1|যাতে|যেগুলিতে}} প্রপাতাকার সুরক্ষা চালু আছে। আপনি এই পাতাটির সুরক্ষা স্তর পরিবর্তন করতে পারেন, তবে এটি প্রপাতাকার সুরক্ষাটিতে কোন পরিবর্তন সাধন করবে না।",
        "protect-default": "সমস্ত ব্যবহারকারীর জন্য",
        "protect-fallback": "\"$1\" অধিকার রয়েছে এমন ব্যবহারকারীদের জন্য অনুমতি",
-       "protect-level-autoconfirmed": "à¦\95à§\87বলমাতà§\8dর à¦¸à¦¯à¦¼à¦\82à¦\95à§\8dরিয় পরীক্ষিত ব্যবহারকারীদের জন্য",
+       "protect-level-autoconfirmed": "শà§\81ধà§\81মাতà§\8dর à¦¸à§\8dবয়à¦\82 পরীক্ষিত ব্যবহারকারীদের জন্য",
        "protect-level-sysop": "কেবল প্রশাসকদের জন্য অনুমতি",
        "protect-summary-cascade": "প্রপাতাকার",
        "protect-expiring": "$1 (ইউটিসি) সময়ে মেয়াদোত্তীর্ণ",
index 7ca6533..2461ed3 100644 (file)
        "no-null-revision": "«$1» агӀона нисдар дан цаделира",
        "badtitle": "Цамегаш йолу цӀе",
        "badtitletext": "Дехарца йолу агӀонан цӀе нийса яц, йаьсса ю, хила мега нийса ца хӀоттийна меттаюкъар йа юкъарвики цӀе. Хила мега, цӀарца цамагош йолу символаш.",
-       "perfcached": "Лахара хаам схьаэцна кэша чура цундела тӀаьххьарлера хийцамаш гойтуш бац. Кэша чохь латтайо оцул $1  кӀезиг {{PLURAL:$1|дӀаяздар}}.",
-       "perfcachedts": "Лахара хаам схьаэцна кэша чура иза тӀаьххьара карлаяьлла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|дӀаяздар}}.",
+       "perfcached": "Лахара хаам схьаэцна кэша чура цундела тӀаьххьарлера хийцамаш гойтуш бац. Кэша чохь латтайо $1 кӀезиг {{PLURAL:$1|дӀаяздар}}.",
+       "perfcachedts": "Лахара хаам схьаэцна кэша чура иза тӀаьххьара карлаяьлла $1. Кэша чохь латта до $4 кӀезиг {{PLURAL:$4|дӀаяздар}}.",
        "querypage-no-updates": "ХӀинца хӀара агӀо карлаякхар дӀадайина ду.\nКхузахь гайтина болу хаамаш карла боккхур бац.",
        "viewsource": "Хьажар",
        "viewsource-title": "Агӏона $1 дуьххьарлера йозане хьажар",
index a4d7e26..8e1bd3e 100644 (file)
@@ -32,8 +32,8 @@
        },
        "tog-underline": "Podtrhávat odkazy:",
        "tog-hideminor": "Skrýt malé editace v posledních změnách",
-       "tog-hidepatrolled": "Skrýt patrolované editace v posledních změnách",
-       "tog-newpageshidepatrolled": "Skrýt patrolované stránky v seznamu nových stránek",
+       "tog-hidepatrolled": "Skrýt prověřené editace v posledních změnách",
+       "tog-newpageshidepatrolled": "Skrýt prověřené stránky v seznamu nových stránek",
        "tog-hidecategorization": "Skrýt kategorizaci stránek",
        "tog-extendwatchlist": "Na seznamu sledovaných stránek zobrazovat všechny změny, ne jen tu poslední",
        "tog-usenewrc": "V posledních změnách a sledovaných stránkách seskupovat změny podle stránek",
@@ -64,7 +64,7 @@
        "tog-watchlisthideliu": "Na seznamu sledovaných stránek skrýt editace přihlášených uživatelů",
        "tog-watchlistreloadautomatically": "Při změně nastavení automaticky aktualizovat seznam sledovaných stránek (vyžaduje JavaScript)",
        "tog-watchlisthideanons": "Na seznamu sledovaných stránek skrýt editace nepřihlášených uživatelů",
-       "tog-watchlisthidepatrolled": "Skrýt patrolované editace ve sledovaných stránkách",
+       "tog-watchlisthidepatrolled": "Skrýt prověřené editace ve sledovaných stránkách",
        "tog-watchlisthidecategorization": "Skrýt kategorizaci stránek",
        "tog-ccmeonemails": "Zasílat mi kopie e-mailů, které pošlu jiným uživatelům",
        "tog-diffonly": "Nezobrazovat obsah stránky pod rozdílem verzí",
        "october-date": "$1. října",
        "november-date": "$1. listopadu",
        "december-date": "$1. prosince",
+       "period-am": "dop.",
+       "period-pm": "odp.",
        "pagecategories": "{{PLURAL:$1|Kategorie}}",
        "category_header": "Stránky v kategorii „$1“",
        "subcategories": "Podkategorie",
        "right-noratelimit": "Imunita vůči rychlostním limitům",
        "right-import": "Import stránek z jiných wiki",
        "right-importupload": "Import stránek nahráním souboru",
-       "right-patrol": "Označování úprav jako prověřené",
-       "right-autopatrol": "Automatické označování editací jako prověřených",
-       "right-patrolmarks": "Zobrazování patrolovacích značek v Posledních změnách",
+       "right-patrol": "Označování cizích editací jako prověřených",
+       "right-autopatrol": "Automatické označování vlastních editací jako prověřených",
+       "right-patrolmarks": "Zobrazování záznamů o prověření v Posledních změnách",
        "right-unwatchedpages": "Zobrazování seznamu nesledovaných stránek",
        "right-mergehistory": "Slučování historií stránek",
        "right-userrights": "Nastavování práv ostatním uživatelům",
        "action-rollback": "rychle revertovat úpravy posledního uživatele editujícího danou stránku",
        "action-import": "importovat stránky z jiné wiki",
        "action-importupload": "importovat stránky z načteného souboru",
-       "action-patrol": "označit úpravy ostatních jako zhlédnuté",
+       "action-patrol": "označovat cizí editace jako prověřené",
        "action-autopatrol": "označit vlastní úpravy jako zhlédnuté",
        "action-unwatchedpages": "zobrazit seznam nesledovaných stránek",
        "action-mergehistory": "sloučit historii této stránky",
        "unblock": "Odblokovat uživatele",
        "blockip": "Zablokovat {{GENDER:$1|uživatele|uživatelku}}",
        "blockip-legend": "Zablokovat uživatele",
-       "blockiptext": "Tento formulář slouží k zablokování editací z konkrétní IP adresy nebo uživatelského jména.\nToto by mělo být používáno jen v souladu s [[{{MediaWiki:Policy-url}}|pravidly]].\nUdejte přesný důvod níže (například ocitujte, které stránky byly poškozeny).",
+       "blockiptext": "Tento formulář slouží k zablokování editací z konkrétní IP adresy nebo uživatelského jména.\nToto by mělo být používáno jen v souladu s [[{{MediaWiki:Policy-url}}|pravidly]].\nUdejte přesný důvod níže (například ocitujte, které stránky byly poškozeny).\nIP rozsahy můžete blokovat pomocí syntaxe [https://cs.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; největší dovolený rozsah je /$1 pro IPv4 a /$2 pro IPv6.",
        "ipaddressorusername": "IP adresa nebo uživatelské jméno:",
        "ipbexpiry": "Čas vypršení:",
        "ipbreason": "Důvod:",
        "export-download": "Nabídnout uložení jako soubor",
        "export-templates": "Zahrnout šablony",
        "export-pagelinks": "Zahrnout odkazované stránky až do hloubky:",
+       "export-manual": "Přidat stránky ručně:",
        "allmessages": "Všechna systémová hlášení",
        "allmessagesname": "Označení hlášení",
        "allmessagesdefault": "Původní text",
        "pageinfo-category-files": "Počet souborů",
        "markaspatrolleddiff": "Označit jako prověřené",
        "markaspatrolledtext": "Označit tuto stránku jako prověřenou",
+       "markaspatrolledtext-file": "Označit tuto verzi souboru jako prověřenou",
        "markedaspatrolled": "Označeno jako prověřené",
        "markedaspatrolledtext": "Vybraná verze stránky [[:$1]] byla označena jako prověřená.",
        "rcpatroldisabled": "Hlídka posledních změn vypnuta",
-       "rcpatroldisabledtext": "Hlídka posledních změn je momentálně vypnuta.",
+       "rcpatroldisabledtext": "Patrola posledních změn je momentálně vypnuta.",
        "markedaspatrollederror": "Nelze označit za prověřené",
        "markedaspatrollederrortext": "Musíte zvolit revizi, která má být označena jako prověřená.",
        "markedaspatrollederror-noautopatrol": "Nemáte dovoleno označovat vlastní editace jako prověřené.",
        "newimages-legend": "Filtr",
        "newimages-label": "Název souboru (nebo jeho část):",
        "newimages-showbots": "Zobrazit soubory načtené boty",
+       "newimages-hidepatrolled": "Skrýt prověřená načtení souborů",
        "noimages": "Není co zobrazit.",
        "ilsubmit": "Hledat",
        "bydate": "podle data",
        "hebrew-calendar-m11-gen": "avu",
        "hebrew-calendar-m12-gen": "elulu",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskuse]])",
+       "timezone-local": "místní čas",
        "duplicate-defaultsort": "Upozornění: Implicitní klíč řazení (DEFAULTSORTKEY) „$2“ přepisuje dříve nastavenou hodnotu „$1“.",
        "duplicate-displaytitle": "<strong>Upozornění:</strong> Předchozí zobrazovaný název „$1“ je nahrazen zobrazovaným názvem „$2“.",
        "invalid-indicator-name": "<strong>Chyba:</strong> Atribut <code>name</code> indikátoru stavu stránky nesmí být prázdný.",
        "expand_templates_preview": "Náhled",
        "expand_templates_preview_fail_html": "<em>Protože {{SITENAME}} má povolené syrové HTML a došlo ke ztrátě dat sezení, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, zkuste to znovu.</strong>\nPokud to stále nebude fungovat, zkuste se [[Special:UserLogout|odhlásit]] a znovu přihlásit.",
        "expand_templates_preview_fail_html_anon": "<em>Protože {{SITENAME}} má povolené syrové HTML a vy nejste přihlášeni, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, [[Special:UserLogin|přihlaste se]] a zkuste to znovu.</strong>",
+       "expand_templates_input_missing": "Musíte zadat alespoň nějaký vstupní text.",
        "pagelanguage": "Volba jazyka stránky",
        "pagelang-name": "Stránka",
        "pagelang-language": "Jazyk",
        "mediastatistics-summary": "Statistika o typech načtených souborů. Zahrnuje vždy jen nejnovější verzi souboru. Staré nebo smazané verze se nezapočítávají.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2; $3 %)",
-       "mediastatistics-bytespertype": "Celková velikost souborů v této sekci: $1 bajtů.",
-       "mediastatistics-allbytes": "Celková velikost všech souborů: $1 bajtů.",
+       "mediastatistics-bytespertype": "Celková velikost souborů v této sekci: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Celková velikost všech souborů: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2).",
        "mediastatistics-table-mimetype": "MIME typ",
        "mediastatistics-table-extensions": "Možné přípony",
        "mediastatistics-table-count": "Počet souborů",
index 2ead049..bd7ba5a 100644 (file)
        "october-date": "$1. Oktober",
        "november-date": "$1. November",
        "december-date": "$1. Dezember",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategorie|Kategorien}}",
        "category_header": "Seiten in der Kategorie „$1“",
        "subcategories": "Unterkategorien",
        "pageinfo-category-files": "Anzahl der Dateien",
        "markaspatrolleddiff": "Als kontrolliert markieren",
        "markaspatrolledtext": "Diese Seite als kontrolliert markieren",
+       "markaspatrolledtext-file": "Diese Dateiversion als kontrolliert markieren",
        "markedaspatrolled": "Als kontrolliert markiert",
        "markedaspatrolledtext": "Die ausgewählte Version von [[:$1]] wurde als kontrolliert markiert.",
        "rcpatroldisabled": "Kontrolle der letzten Änderungen gesperrt",
        "newimages-legend": "Filter",
        "newimages-label": "Dateiname (oder ein Teil davon):",
        "newimages-showbots": "Uploads von Bots anzeigen",
+       "newimages-hidepatrolled": "Kontrollierte Uploads ausblenden",
        "noimages": "Keine Dateien gefunden.",
        "ilsubmit": "Suchen",
        "bydate": "nach Datum",
        "hijri-calendar-m11": "Dhu l-qaʿda",
        "hijri-calendar-m12": "Dhu l-hiddscha",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskussion]])",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "Achtung: Der Sortierungsschlüssel „$2“ überschreibt den vorher verwendeten Schlüssel „$1“.",
        "duplicate-displaytitle": "<strong>Warnung:</strong> Der Anzeigetitel „$2“ überschreibt den früheren Anzeigetitel „$1“.",
        "invalid-indicator-name": "<strong>Fehler:</strong> Das Attribut <code>name</code> des Seitenstatusindikators darf nicht leer sein.",
index d51f849..b436f90 100644 (file)
        "october-date": "October $1",
        "november-date": "November $1",
        "december-date": "December $1",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Category|Categories}}",
        "pagecategorieslink": "Special:Categories",
        "category_header": "Pages in category \"$1\"",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|talk]])",
        "signature-anon": "[[{{#special:Contributions}}/$1|$2]]",
        "timezone-utc": "UTC",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Warning:</strong> Default sort key \"$2\" overrides earlier default sort key \"$1\".",
        "duplicate-displaytitle": "<strong>Warning:</strong> Display title \"$2\" overrides earlier display title \"$1\".",
        "invalid-indicator-name": "<strong>Error:</strong> Page status indicators' <code>name</code> attribute must not be empty.",
index 6ee50d8..97a8bd6 100644 (file)
                        "Matiia",
                        "SinNovedades",
                        "Rodm23",
-                       "Yllelder"
+                       "Yllelder",
+                       "Syum90"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "export-download": "Guardar como archivo",
        "export-templates": "Incluir plantillas",
        "export-pagelinks": "Incluir páginas enlazadas a una profundidad de:",
+       "export-manual": "Añadir páginas manualmente:",
        "allmessages": "Todos los mensajes de MediaWiki",
        "allmessagesname": "Nombre",
        "allmessagesdefault": "Texto predeterminado",
        "watchlisttools-edit": "Ver y editar tu lista de seguimiento",
        "watchlisttools-raw": "Editar lista de seguimiento en crudo",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discusión]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Advertencia:</strong> la clave de ordenamiento predeterminada «$2» anula la clave de ordenamiento anterior «$1».",
        "duplicate-displaytitle": "<strong>Advertencia:</strong> El título visualizado \"$2\" sobreescribe al anterior \"$1\".",
        "invalid-indicator-name": "<strong>Error:</strong> el atributo <code>name</code> de los indicadores de estado de página no debe estar vacío.",
        "logentry-suppress-block": "$1 {{GENDER:$2|bloqueó}} {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|cambió}} la configuración del bloqueo de {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|importó}} $3 subiendo un archivo",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|importó}} $3 subiendo un archivo ($4 {{PLURAL:$4|revisión|revisiones}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|importó}} $3 desde otro wiki",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|importó}} $3 desde $5 ($4 {{PLURAL:$4|revisión|revisiones}})",
        "logentry-merge-merge": "$1 {{GENDER:$2|combinó}} $3 en $4 (revisiones hasta el $5)",
        "logentry-move-move": "$1 {{GENDER:$2|trasladó}} la página $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|trasladó}} la página $3 a $4 sin dejar una redirección",
index be375b0..4c31544 100644 (file)
        "undelete_short": "احیای {{PLURAL:$1|یک ویرایش|$1 ویرایش}}",
        "viewdeleted_short": "نمایش {{PLURAL:$1|یک ویرایش حذف‌شده|$1 ویرایش حذف‌شده}}",
        "protect": "محافظت",
-       "protect_change": "گؤەڕانن/تغییر",
+       "protect_change": "تغییر",
        "protectthispage": "محافظت از این صفحه",
-       "unprotect": "گؤەڕانن/تغییر محافظت",
+       "unprotect": "تغییر محافظت",
        "unprotectthispage": "تغییر محافظت این صفحه",
        "newpage": "صفحهٔ تازه",
        "talkpage": "بحث دربارهٔ این صفحه",
        "php-mail-error-unknown": "خطای ناشناخته در تابع  mail()‎ پی‌اچ‌پی",
        "user-mail-no-addy": "تلاش برای ارسال ایمیل بدون آدرس ایمیل.",
        "user-mail-no-body": "سعی کردید نامه‌ای الکترونیکی با محتوایی بسیار کوتاه و یا خالی بفرستید.",
-       "changepassword": "گؤەڕانن/تغییر رمز",
+       "changepassword": "تغییر گذرواژه",
        "resetpass_announce": "شما باید برای پایان ورود به سامانه، گذرواژهٔ جدیدی را تنظیم کنید.",
        "resetpass_text": "<!-- اینجا متن اضافه کنید -->",
        "resetpass_header": "تغییر گذرواژهٔ حساب کاربری",
        "revdelete-hide-comment": "خلاصهٔ ویرایش",
        "revdelete-hide-user": "نام کاربری/نشانی آی‌پی",
        "revdelete-hide-restricted": "فرونشانی اطلاعات برای مدیران به همراه دیگران",
-       "revdelete-radio-same": "(بدون گؤەڕانن/تغییر)",
+       "revdelete-radio-same": "(بدون تغییر)",
        "revdelete-radio-set": "پنهان",
        "revdelete-radio-unset": "نمایان",
        "revdelete-suppress": "از دسترسی مدیران به داده نیز مانند سایر کاربران جلوگیری به عمل آید.",
        "revdelete-failure": "'''پیدایی نسخه‌ها قابل به روز کردن نیست:'''\n$1",
        "logdelete-success": "تغییر پیدایی مورد با موفقیت انجام شد.",
        "logdelete-failure": "'''پیدایی سیاهه‌ها قابل تنظیم نیست:'''\n$1",
-       "revdel-restore": "گؤەڕانن/تغییر پیدایی",
+       "revdel-restore": "تغییر پیدایی",
        "pagehist": "تاریخچهٔ صفحه",
        "deletedhist": "تاریخچهٔ حذف‌شده",
        "revdelete-hide-current": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر است و قابل پنهان کردن نیست.",
        "unblock": "بازکردن کاربر",
        "blockip": "بستن {{GENDER:$1|کاربر}}",
        "blockip-legend": "بستن کاربر",
-       "blockiptext": "از فرم زیر برای بستن دسترسی ویرایش یک نشانی آی‌پی یا نام کاربری مشخص استفاده کنید.\nاین کار فقط فقط باید برای جلوگیری از خرابکاری و بر اساس [[{{MediaWiki:Policy-url}}|سیاست قطع دسترسی]] انجام شود.\nدلیل مشخص این کار را در زیر ذکر کنید (مثلاً با ذکر صفحه‌های به‌خصوصی که مورد خرابکاری واقع شده‌اند).",
+       "blockiptext": "از فرم زیر برای بستن دسترسی ویرایش یک نشانی آی‌پی یا نام کاربری مشخص استفاده کنید.\nاین کار فقط فقط باید برای جلوگیری از خرابکاری و بر اساس [[{{MediaWiki:Policy-url}}|سیاست قطع دسترسی]] انجام شود.\nدلیل مشخص این کار را در زیر ذکر کنید (مثلاً با ذکر صفحه‌های به‌خصوصی که مورد خرابکاری واقع شده‌اند).\nشما می‌توانید بازرهٔ آی‌پی که از ساختار [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] استفاده می‌کنید را ببندید. بزرگترین بازه /$1 برای IPv4 و /$2 برای IPv6 است.",
        "ipaddressorusername": "نشانی آی‌پی یا نام کاربری:",
        "ipbexpiry": "زمان سرآمدن:",
        "ipbreason": "دلیل:",
        "allmessagesnotsupportedDB": "این صفحه نمی‌تواند استفاده شود به این دلیل که <bdi>'''$wgUseDatabaseMessages'''</bdi> غیرفعال شده‌است.",
        "allmessages-filter-legend": "پالایه",
        "allmessages-filter": "پالودن بر اساس وضعیت شخصی‌سازی:",
-       "allmessages-filter-unmodified": "نەگؤەڕیائە/تغییرنیافتە",
+       "allmessages-filter-unmodified": "تغییر نیافته",
        "allmessages-filter-all": "همه",
        "allmessages-filter-modified": "تغییر یافته",
        "allmessages-prefix": "پالودن بر اساس پسوند:",
        "pageinfo-category-files": "تعداد پرونده‌ها",
        "markaspatrolleddiff": "برچسب گشت بزن",
        "markaspatrolledtext": "به این صفحه برچسب گشت بزن",
+       "markaspatrolledtext-file": "انتخاب این نسخهٔ پرونده به عنوان گشت خورده",
        "markedaspatrolled": "برچسب گشت زده شد",
        "markedaspatrolledtext": "به نسخهٔ انتخاب شده از [[:$1]] برچسب گشت زده شد.",
        "rcpatroldisabled": "گشت‌زنی تغییرات اخیر غیرفعال است",
        "newimages-legend": "پالودن",
        "newimages-label": "نام پرونده (یا قسمتی از آن):",
        "newimages-showbots": "نمایش بارگذاری‌ها توسط ربات‌ها",
+       "newimages-hidepatrolled": "مخفی کردن بارگذاری گشت‌زن‌ها",
        "noimages": "چیزی برای دیدن نیست.",
        "ilsubmit": "جستجو",
        "bydate": "از روی تاریخ",
index bec4b88..a316142 100644 (file)
        "october-date": "$1 octobre",
        "november-date": "$1 novembre",
        "december-date": "$1 décembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Catégorie|Catégories}}",
        "category_header": "Pages dans la catégorie « $1 »",
        "subcategories": "Sous-catégories",
        "revision-info": "Révision de $1 par {{GENDER:$6|$2}}$7",
        "previousrevision": "← Version précédente",
        "nextrevision": "Version suivante →",
-       "currentrevisionlink": "Voir la version courante",
+       "currentrevisionlink": "Voir la version actuelle",
        "cur": "actu",
        "next": "suivant",
        "last": "diff",
        "unblock": "Débloquer l’utilisateur",
        "blockip": "Bloquer l’{{GENDER:$1|utilisateur|utilisatrice}}",
        "blockip-legend": "Bloquer l’utilisateur",
-       "blockiptext": "Utilisez le formulaire ci-dessous pour bloquer les tentatives de modification faites à partir d’une adresse IP spécifique ou d’un nom d’utilisateur.\nUne telle mesure ne devrait être prise que pour prévenir le vandalisme et en accord avec les [[{{MediaWiki:Policy-url}}|règles internes]].\nDonnez ci-dessous un motif précis (par exemple en citant les pages qui ont été vandalisées).",
+       "blockiptext": "Utilisez le formulaire ci-dessous pour bloquer les tentatives de modification faites à partir d’une adresse IP spécifique ou d’un nom d’utilisateur.\nUne telle mesure ne devrait être prise que pour prévenir le vandalisme et en accord avec les [[{{MediaWiki:Policy-url}}|règles internes]].\nDonnez ci-dessous un motif précis (par exemple en citant les pages qui ont été vandalisées).\nVous pouvez bloquer des plages d’adresses IP en utilisant la syntaxe [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] ; la plus grande plage autorisée est /$1 pour IP v4 et /$2 pour IP v6.",
        "ipaddressorusername": "Adresse IP ou nom d'utilisateur :",
        "ipbexpiry": "Durée avant expiration :",
        "ipbreason": "Motif :",
        "pageinfo-category-files": "Nombre de fichiers",
        "markaspatrolleddiff": "Marquer comme relue",
        "markaspatrolledtext": "Marquer cette page comme relue",
+       "markaspatrolledtext-file": "Marquer cette version de fichier comme patrouillée",
        "markedaspatrolled": "Marquée comme relue",
        "markedaspatrolledtext": "La version sélectionnée de [[:$1]] a été marquée comme relue.",
        "rcpatroldisabled": "La fonction de relecture des modifications récentes n'est pas activée.",
        "newimages-legend": "Nom du fichier",
        "newimages-label": "Nom du fichier (ou une partie de celui-ci) :",
        "newimages-showbots": "Afficher les imports par des robots",
+       "newimages-hidepatrolled": "Masquer les téléchargements patrouillés",
        "noimages": "Aucune image à afficher.",
        "ilsubmit": "Rechercher",
        "bydate": "par date",
        "hebrew-calendar-m11-gen": "av",
        "hebrew-calendar-m12-gen": "eloul",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussion]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "Attention : la clé de tri par défaut « $2 » écrase la précédente clé « $1 ».",
        "duplicate-displaytitle": "<strong>Attention :</strong> Le titre d'affichage « $2 » remplace l'ancien titre d'affichage « $1 ».",
        "invalid-indicator-name": "<strong>Erreur :</strong> L’attribut <code>name</code> des indicateurs d’état de la page ne doit pas être vide.",
index 5ab7fb4..d67eb56 100644 (file)
        "restriction-level-sysop": "સંપૂર્ણ સંરક્ષિત",
        "restriction-level-autoconfirmed": "અર્ધ સંરક્ષિત",
        "restriction-level-all": "કોઈ પણ સ્તર",
-       "undelete": "ભà«\82àª\82સાડà«\87લા àªªàª¾àª¨àª¾ àª¬àª¤àª¾àªµà«\8b",
+       "undelete": "ભૂંસેલા પાના બતાવો",
        "undeletepage": "હટાવેલ પાના જુઓ અને પુનર્જીવિત કરો",
        "undeletepagetitle": "'''નીચે [[:$1|$1]] ના ભૂંસાડેલ સંપાદનો છે.'''.",
        "viewdeletedpage": "ભૂંસેલા પાના બતાવો",
index e07228d..e13fb30 100644 (file)
        "october-date": "$1 באוקטובר",
        "november-date": "$1 בנובמבר",
        "december-date": "$1 בדצמבר",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|קטגוריה|קטגוריות}}",
        "category_header": "דפים בקטגוריה \"$1\"",
        "subcategories": "קטגוריות משנה",
        "unblock": "שחרור משתמש",
        "blockip": "חסימת {{GENDER:$1|משתמש|משתמשת}}",
        "blockip-legend": "חסימת משתמש",
-       "blockiptext": "×\94שת×\9eש×\95 ×\91×\98×\95פס ×©×\9c×\94×\9c×\9f ×\9b×\93×\99 ×\9c×\97ס×\95×\9d ×\90ת ×\94רש×\90×\95ת ×\94×\9bת×\99×\91×\94 ×\9e×\9bת×\95×\91ת IP ×\90×\95 ×\9eשת×\9eש ×\9eס×\95×\99×\9e×\99×\9d.\n×\97ס×\99×\9e×\95ת ×\9b×\90×\9c×\94 ×¦×¨×\99×\9b×\95ת ×\9c×\94ת×\91צע ×¨×§ ×\9b×\93×\99 ×\9c×\9e× ×\95×¢ ×\94ש×\97ת×\94, ×\95×\91×\94ת×\90×\9d ×\9c[[{{MediaWiki:Policy-url}}|× ×\94×\9c×\99×\9d]].\n×\90× ×\90 ×\9e×\9c×\90×\95 ×\90ת ×\94ס×\99×\91×\94 ×\94פר×\98× ×\99ת ×\9c×\97ס×\99×\9e×\94 ×\9c×\94×\9c×\9f (×\9c×\93×\95×\92×\9e×\94, ×\91×\90×\9eצע×\95ת ×¦×\99×\95×\9f ×\93פ×\99×\9d ×\9eס×\95×\99×\9e×\99×\9d ×©×\94ש×\97×\99ת ×\94×\9eשת×\9eש).\n×\91×\90פשר×\95ת×\9a ×\9c×\97ס×\95×\9d ×\98×\95×\95×\97×\99 IP ×\91×\90×\9eצע×\95ת ×ª×\97×\91×\99ר [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; ×\94×\98×\95×\95×\97 ×\94×\92×\93×\95×\9c ×\91×\99×\95תר ×©×\9e×\95רש×\94 הוא <span dir=\"ltr\">/$1</span> עבור IPv4 ו־<span dir=\"ltr\">/$2</span> עבור IPv6.",
+       "blockiptext": "×\94שת×\9eש×\95 ×\91×\98×\95פס ×©×\9c×\94×\9c×\9f ×\9b×\93×\99 ×\9c×\97ס×\95×\9d ×\90ת ×\94רש×\90×\95ת ×\94×\9bת×\99×\91×\94 ×\9e×\9bת×\95×\91ת IP ×\90×\95 ×\9eשת×\9eש ×\9eס×\95×\99×\9e×\99×\9d.\n×\97ס×\99×\9e×\95ת ×\9b×\90×\9c×\94 ×¦×¨×\99×\9b×\95ת ×\9c×\94ת×\91צע ×¨×§ ×\9b×\93×\99 ×\9c×\9e× ×\95×¢ ×\94ש×\97ת×\94, ×\95×\91×\94ת×\90×\9d ×\9c[[{{MediaWiki:Policy-url}}|× ×\94×\9c×\99×\9d]].\n×\90× ×\90 ×\9e×\9c×\90×\95 ×\90ת ×\94ס×\99×\91×\94 ×\94פר×\98× ×\99ת ×\9c×\97ס×\99×\9e×\94 ×\9c×\94×\9c×\9f (×\9c×\93×\95×\92×\9e×\94, ×\91×\90×\9eצע×\95ת ×¦×\99×\95×\9f ×\93פ×\99×\9d ×\9eס×\95×\99×\9e×\99×\9d ×©×\94ש×\97×\99ת ×\94×\9eשת×\9eש).\n×\91×\90פשר×\95ת×\9b×\9d ×\9c×\97ס×\95×\9d ×\98×\95×\95×\97×\99 ×\9bת×\95×\91×\95ת IP ×\91×\90×\9eצע×\95ת ×ª×\97×\91×\99ר [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; ×\94×\98×\95×\95×\97 ×\94×\92×\93×\95×\9c ×\91×\99×\95תר ×©× ×\99ת×\9f ×\9c×\97ס×\95×\9d הוא <span dir=\"ltr\">/$1</span> עבור IPv4 ו־<span dir=\"ltr\">/$2</span> עבור IPv6.",
        "ipaddressorusername": "כתובת IP או שם משתמש:",
        "ipbexpiry": "פקיעה:",
        "ipbreason": "סיבה:",
        "pageinfo-category-files": "מספר הקבצים",
        "markaspatrolleddiff": "סימון השינוי כבדוק",
        "markaspatrolledtext": "סימון דף זה כבדוק",
+       "markaspatrolledtext-file": "סימון גרסת קובץ זו כבדוקה",
        "markedaspatrolled": "השינוי סומן כבדוק",
        "markedaspatrolledtext": "השינוי שבחרת בדף [[:$1]] סומן כבדוק.",
        "rcpatroldisabled": "אפשרות סימון השינויים כבדוקים מבוטלת",
        "newimages-legend": "מסנן",
        "newimages-label": "שם הקובץ (או חלק ממנו):",
        "newimages-showbots": "הצגת העלאות שבוצעו על־ידי בוטים",
+       "newimages-hidepatrolled": "הסתרת העלאות בדוקות",
        "noimages": "אין קבצים.",
        "ilsubmit": "חיפוש",
        "bydate": "לפי תאריך",
        "hebrew-calendar-m11-gen": "באב",
        "hebrew-calendar-m12-gen": "באלול",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|שיחה]])",
+       "timezone-local": "מקומי",
        "duplicate-defaultsort": "'''אזהרה:''' המיון הרגיל \"$2\" דורס את המיון הרגיל המוקדם ממנו \"$1\".",
        "duplicate-displaytitle": "<strong>אזהרה:</strong> כותרת התצוגה \"$2\" דורסת את כותרת התצוגה הקודמת \"$1\".",
        "invalid-indicator-name": "<strong>שגיאה:</strong> התכונה <code>name</code> של מצייני מצב הדף אינה יכולה להיות ריקה.",
index d837fba..2e26bb7 100644 (file)
        "october-date": "{{PLURAL:$1|1°|$1}} ottobre",
        "november-date": "{{PLURAL:$1|1°|$1}} novembre",
        "december-date": "{{PLURAL:$1|1°|$1}} dicembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoria|Categorie}}",
        "category_header": "Pagine nella categoria \"$1\"",
        "subcategories": "Sottocategorie",
        "unblock": "Sblocca utente",
        "blockip": "Blocca {{GENDER:$1|utente}}",
        "blockip-legend": "Blocca l'utente",
-       "blockiptext": "Usare il modulo sottostante per bloccare l'accesso in scrittura a uno specifico indirizzo IP o a un utente registrato.\nIl blocco dev'essere operato per prevenire atti di vandalismo e in stretta osservanza delle [[{{MediaWiki:Policy-url}}|regole di {{SITENAME}}]].\nIndicare il motivo specifico per il quale si procede al blocco (per esempio, citando i titoli di eventuali pagine oggetto di vandalismo).",
+       "blockiptext": "Usa il modulo sottostante per bloccare l'accesso in scrittura a uno specifico indirizzo IP o a un utente registrato.\nIl blocco dev'essere operato per prevenire atti di vandalismo e in stretta osservanza delle [[{{MediaWiki:Policy-url}}|regole di {{SITENAME}}]].\nIndica il motivo specifico per il quale si procede al blocco (per esempio, citando i titoli di eventuali pagine oggetto di vandalismo).\nPuoi bloccare intervalli di IP utilizzando la sintassi [https://it.wikipedia.org/wiki/CIDR CIDR]; l'intervallo più ampio consentito è /$1 per IPv4 e /$2 per IPv6.",
        "ipaddressorusername": "Indirizzo IP o nome utente:",
        "ipbexpiry": "Scadenza del blocco:",
        "ipbreason": "Motivo:",
        "pageinfo-category-files": "Numero di file",
        "markaspatrolleddiff": "Segna come verificata",
        "markaspatrolledtext": "Segna questa pagina come verificata",
+       "markaspatrolledtext-file": "Segna questo file come verificato",
        "markedaspatrolled": "Modifica verificata",
        "markedaspatrolledtext": "La modifica di [[:$1]] selezionata è stata segnata come verificata.",
        "rcpatroldisabled": "La verifica delle ultime modifiche è disattivata",
        "newimages-legend": "Filtra",
        "newimages-label": "Nome file (o una parte di esso):",
        "newimages-showbots": "Mostra caricamenti di bot",
+       "newimages-hidepatrolled": "Nascondi caricamenti verificati",
        "noimages": "Non c'è nulla da vedere.",
        "ilsubmit": "Ricerca",
        "bydate": "per data",
        "hebrew-calendar-m10": "Tammuz",
        "hebrew-calendar-m10-gen": "Tammuz",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussioni]])",
+       "timezone-local": "Locale",
        "duplicate-defaultsort": "Attenzione: la chiave di ordinamento predefinita \"$2\" sostituisce la precedente \"$1\".",
        "duplicate-displaytitle": "<strong>Attenzione:</strong> il titolo visualizzato \"$2\" sostituisce il precedente titolo \"$1\".",
        "invalid-indicator-name": "<strong>Errore:</strong> attributo <code>name</code> degli indicatori dello stato della pagina non può essere vuoto.",
        "htmlform-float-invalid": "Il valore specificato non è un numero.",
        "htmlform-int-toolow": "Il valore specificato è inferiore al minimo di $1",
        "htmlform-int-toohigh": "Il valore specificato è superiore al massimo di $1",
-       "htmlform-required": "Questo valore è necessario",
+       "htmlform-required": "Questo valore è obligatorio.",
        "htmlform-submit": "Invia",
        "htmlform-reset": "Annulla modifiche",
        "htmlform-selectorother-other": "Altro",
        "htmlform-chosen-placeholder": "Seleziona un'opzione",
        "htmlform-cloner-create": "Aggiungi altro",
        "htmlform-cloner-delete": "Rimuovi",
-       "htmlform-cloner-required": "È necessario almeno un valore.",
+       "htmlform-cloner-required": "È obbligatorio almeno un valore.",
        "htmlform-title-badnamespace": "[[:$1]] non si trova nel namespace \"{{ns:$2}}\".",
        "htmlform-title-not-creatable": "\"$1\" è il titolo di una pagina non creabile",
        "htmlform-title-not-exists": "$1 non esiste.",
index cfa3451..73efe8f 100644 (file)
        "fileduplicatesearch-result-n": "«$1» файлына тең $2 телнұсқасы бар.",
        "fileduplicatesearch-noresults": "\"$1\" атауымен файл табылмады.",
        "specialpages": "Арнайы беттер",
-       "specialpages-note-top": "Ð\90Ò£Ñ\8bз",
-       "specialpages-note": "* Қалыпты арнайы беттер. \n* <span class==\"mw-specialpagerestricted\">Шектелген арнайы беттер.</span>",
+       "specialpages-note-top": "ШаÑ\80Ñ\82Ñ\82Ñ\8b Ð±ÐµÐ»Ð³Ñ\96леÑ\80",
+       "specialpages-note": "* Қалыпты арнайы беттер. \n* <span class=\"mw-specialpagerestricted\">Шектелген арнайы беттер.</span>",
        "specialpages-group-maintenance": "Техникалық талқылау есептері",
        "specialpages-group-other": "Тағы басқа арнайы беттер",
        "specialpages-group-login": "Кіру / тіркелу",
        "logentry-newusers-autocreate": "$1 қатысушы аккаунтын автоматты түрде {{GENDER:$2|тіркеді}}",
        "logentry-protect-move_prot": "$1 protection settings from $4 дегеннен $3 дегенге қорғалу баптауларын {{GENDER:$2|жылжытты}}",
        "logentry-protect-protect": "$1 $3 бетін {{GENDER:$2|қорғады}}  $4",
+       "logentry-protect-modify-cascade": "$1 $3 бетінің қорғалу деңгейін $4 мерзіміне {{GENDER:$2|өзгертті}} [баулы]",
        "logentry-rights-rights": "$1 $3 үшін топ мүшелігін $4 дегеннен $5 дегенге {{GENDER:$2|өзгертті}}",
        "logentry-rights-rights-legacy": "$1 $3 үшін топ мүшелігін {{GENDER:$2|өзгертті}}",
        "logentry-rights-autopromote": "$1 $4 дегенен $5 дегенге автоматты түрде {{GENDER:$2|деңгейі көтерілген}}",
index 86a2f3e..6b8c9f8 100644 (file)
        "email": "Litterae electronicae",
        "prefs-help-realname": "Nomen verum non necesse est.\nSi vis id dare, opera tua tibi ascribentur.",
        "prefs-help-email": "Non necesse est inscriptionem electronicam dare. Qua tamen data tessera tibi tribui poterit nova, si prioris oblitus eris.",
-       "prefs-help-email-others": "Praeterea, si libeat, aliis concedas tibi nuntia per nexum in pagina vel disputatione tua positum mittere electronicas.\nInscriptio tua electronica usoribus tibi scribentis non erit visibilis.",
+       "prefs-help-email-others": "Praeterea, si libeat, aliis concedas tibi nuntia per nexum in pagina vel disputatione tua positum mittere electronicas.\nInscriptio tua electronica usoribus, qui tibi scribeant, non erit visibilis.",
        "prefs-help-email-required": "Inscriptio electronica necesse est.",
        "prefs-info": "Generalia",
        "prefs-i18n": "Sermonis delectus",
index c1be4f0..b0c6727 100644 (file)
        "unblock": "D'Spär vum Benotzer ophiewen",
        "blockip": "{{GENDER:$1|Benotzer}} spären",
        "blockip-legend": "Benotzer spären",
-       "blockiptext": "Benotzt dëse Formulaire fir eng spezifesch IP-Adress oder e Benotzernumm ze spären. Dëst soll nëmmen am Fall vu Vandalismus gemaach ginn, en accordance mat den [[{{MediaWiki:Policy-url}}|interne Richlinen]]. Gitt e spezifesche Grond un (zum Beispill Säite wou Vandalismus virgefall ass).",
+       "blockiptext": "Benotzt dëse Formulaire fir eng spezifesch IP-Adress oder e Benotzernumm ze spären.\nDëst soll nëmmen am Fall vu Vandalismus gemaach ginn, en accordance mat den [[{{MediaWiki:Policy-url}}|interne Richlinen]].\nGitt e spezifesche Grond un (zum Beispill Säite wou Vandalismus virgefall ass).\nDir kënnt IP-Beräicher spären an deem Dir d' [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] Syntax benotzt; de gréissten erlaabte Beräich as  /$1 fir IPv4 an /$2 fir IPv6",
        "ipaddressorusername": "IP-Adress oder Benotzernumm:",
        "ipbexpiry": "Gültegkeet:",
        "ipbreason": "Grond:",
        "pageinfo-category-files": "Zuel vun de Fichieren",
        "markaspatrolleddiff": "Als nogekuckt markéieren",
        "markaspatrolledtext": "Dës Säit als nogekuckt markéieren",
+       "markaspatrolledtext-file": "Dës Versioun vum Fichier als nogekuckt markéieren",
        "markedaspatrolled": "ass als nogekuckt markéiert",
        "markedaspatrolledtext": "Déi gewielt Versioun vu(n) [[:$1]] gouf als nogekuckt markéiert.",
        "rcpatroldisabled": "Rezent Ännerungskontroll ausgeschalt.",
        "expand_templates_generate_xml": "Weis d'Struktur vum XML",
        "expand_templates_generate_rawhtml": "HTML-Format weisen",
        "expand_templates_preview": "Kucken ouni ofzespäicheren",
+       "expand_templates_input_missing": "Dir musst mindestens een Text aginn.",
        "pagelanguage": "Eraussiche vun der Sprooch vun der Säit",
        "pagelang-name": "Säit",
        "pagelang-language": "Sprooch",
index bc25dfc..2e134e4 100644 (file)
        "qbfind": "پیاکردن-ئادین",
        "qbbrowse": "مِنِی -گۀشتن",
        "qbedit": "دسکاری",
-       "qbpageoptions": "ئئ وةڵگة",
+       "qbpageoptions": "ئێ وەڵگە",
        "qbmyoptions": "وۀلگۀل مِ",
        "faq": "پرسش‌های متداول",
        "faqpage": "Project:پرسش‌های متداول",
        "create": "دؤِرس کردن/سازین",
        "create-local": "افزودن توضیحات محلی",
        "editthispage": "ئئ وةڵگۀ دةسکاری کةن",
-       "create-this-page": "دؤرِس کردن ئئ وةڵگة",
+       "create-this-page": " ئێ وەڵگە دؤرِس کە",
        "delete": "حۀذف کردن/پاک کردن",
        "deletethispage": "حذف این صفحه",
        "undeletethispage": "بازگردانی ئئ وةڵگة",
        "protect_change": "گؤەڕانن/تغییر",
        "protectthispage": "محافظت إژ ئئ وةڵگة",
        "unprotect": "گؤەڕانن/تغییر محافظت",
-       "unprotectthispage": "تغییر محافظت ئئ وةڵگة",
+       "unprotectthispage": "گؤەڕانن/تغییر محافظت ئێ وەڵگە",
        "newpage": "وةڵگة  تازۀ",
-       "talkpage": "گةپ دةربارة ئئ وةڵگة",
+       "talkpage": "دەربارە ئێ وەڵگە گەپ بووشن",
        "talkpagelinktext": "قسۀ-گۀپ",
        "specialpage": "وةڵگة/پةرة  ویژة",
        "personaltools": "ابزارۀل ووژی/شخصی",
        "nosuchuser": "کاربةری وۀ نام «$1» ئة ائرة نیة.\nنام کاربةری وة کةڵنگی و گؤجةری حروف حساسة .\nاملای نام را بررسی کنید، یا [[Special:UserLogin/signup|یک حساب کاربری تازه بسازید]].",
        "nosuchusershort": "هؤیچ کاربةری وة نام ''$1'' ئة ائرة نیة.\nاملایتان را وارسی کنید.",
        "nouserspecified": ".باید یإ گِلة  نام کاربةری دیاری کئین",
-       "login-userblocked": ".ئئ کاربةرة بةسیائة. إنؤم هةتِن سیستم مجاز نیة",
+       "login-userblocked": ".ئی کاربرە بەسیائە. إنؤم هەتِن سیستم ڕاووآ(مجاز)نیە",
        "wrongpassword": "گذرواژه‌ای که وارد کردید نادرستة.\nخواهشا دووباره تلاش کةن.",
        "wrongpasswordempty": "گذرواژه‌ای که وارد کرده‌اید، خالی است.\nلطفاً دوباره تلاش کنید.",
        "passwordtooshort": "گذرواژه باید دست‌کم {{PLURAL:$1|۱ حرف|$1 حرف}} داشته باشد.",
        "action-reupload-shared": "باطل کردن این پرونده روی یک مخزن مشترک",
        "action-upload_by_url": "بارگذاری این پرونده از یک نشانی اینترنتی",
        "action-writeapi": "استفاده از API نوشتن",
-       "action-delete": "حةذف ئئ وةڵگة",
+       "action-delete": "پاک کردن ئێ وەڵگە",
        "action-deleterevision": "حذف این نسخه",
        "action-deletedhistory": "مشاهدهٔ تاریخچهٔ حذف شدهٔ این صفحه",
        "action-browsearchive": "جستجوی صفحه‌های حذف‌شده",
-       "action-undelete": "بازگردانی ئئ وةڵگة",
+       "action-undelete": "واگردانی(گلآدائن)ئێ وەڵگە",
        "action-suppressrevision": "مشاهده و احیای ویرایش‌های حذف شده",
        "action-suppressionlog": "مشاهدهٔ این سیاههٔ خصوصی",
        "action-block": "قطع دسترسی این کاربر از ویرایش‌کردن",
        "sp-contributions-toponly": "فقط ویرایش‌هایی که آخرین نسخه‌اند نمایش داده شود",
        "sp-contributions-newonly": "فقط نمایش ویرایش‌هایی که ایجاد صفحه هستند",
        "sp-contributions-submit": "گئردین/مهِ نی",
-       "whatlinkshere": "Ù¾Û\8cÙ\88Ù\86دÛ\80Ù\84 Ù\88Û\80 Ø¦Ø¦ Ù\88ةڵگة",
+       "whatlinkshere": "Ù¾Û\8cÙ\88Ù\86دÛ\95Ù\84 Ù\88Û\95 Ø¦Û\8e Ù\88Û\95ÚµÚ¯Û\95",
        "whatlinkshere-title": "وۀلگۀلئ گإ  وۀ «$1» پیوۀند دِرِن",
        "whatlinkshere-page": ":پەڕە/وةڵگة",
        "linkshere": "The following pages link to <strong>[[:$1]]</strong>:",
        "tooltip-pt-logout": "دەرچێن|خروج",
        "tooltip-pt-createaccount": "مکئس تانۀ مۀکیم حساووئ بسازن و بونإ سامانۀ؛ هرچۀند حساوو کاربری سازین دل .بخوایۀ",
        "tooltip-ca-talk": "گۀپ/قسۀ دۀربارۀ بنچۀک/محتوا وۀلگۀ",
-       "tooltip-ca-edit": "ئئ وۀلگۀ دۀسکاری کۀن",
+       "tooltip-ca-edit": "ئێ وەڵگە دەسکاری کە",
        "tooltip-ca-addsection": "بۀخش تازه بسازِن",
        "tooltip-ca-viewsource": ".ئئ وۀلگۀ محافظۀت بیۀ\nمۀتؤنین  متن مبدأ/بنچۀک بؤینین",
        "tooltip-ca-history": "ورژن دؤمائن/پئش ئئ وۀلگۀ",
-       "tooltip-ca-protect": "محافظت إژ ئئ وةڵگة",
-       "tooltip-ca-unprotect": "گؤەڕانن/تغییر محافظت ئئ وةڵگة",
+       "tooltip-ca-protect": "محافظت إژ ئێ وەڵگە",
+       "tooltip-ca-unprotect": "گؤەڕانن/تغییر محافظت ئێ وةڵگە",
        "tooltip-ca-delete": "حةذف ئئ وةڵگة",
        "tooltip-ca-undelete": "بازگرداندن نسخه‌های صفحهٔ حذف‌شده",
-       "tooltip-ca-move": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø¦Ø¦ Ù\88Û\80Ù\84Ú¯Û\80",
+       "tooltip-ca-move": "ئÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¬Ø§Ù\88Ù\88آز Ú©Û\95",
        "tooltip-ca-watch": "اضافۀ کردن ئئ وۀلگۀ وۀ لیست پیگیریۀل تان",
        "tooltip-ca-unwatch": "حذف کردن ئئ وۀلگۀ وۀ لیست پیگیریۀل تان",
        "tooltip-search": " {{SITENAME}}مِنِی کرد أ نوم",
        "tooltip-t-recentchangeslinked": "تغییرۀل ایسۀ وۀلگۀلئ گإ ئئ وۀلگۀ  پیوند درئۀ  اؤِنۀ",
        "tooltip-feed-rss": "خبرنامه آراس‌اس برای این صفحه",
        "tooltip-feed-atom": "حاووال اتم أرا ئئ وۀلگۀ",
-       "tooltip-t-contributions": "Ù\84Û\8cست Ù\87ؤÙ\85کارÛ\8cÛ\80Ù\84 Ø¦Ø¦ Ú©Ø§Ø±Ø¨Ø±Û\80",
+       "tooltip-t-contributions": "Ù\84Û\8cست Ù\87اÙ\85 Ú©Ø§Ø±Û\8cÛ\95Ù\84\87Û\95Ù\85آرتÛ\8cÛ\95Ù\84)ئÛ\8e Ú©Ø§Ø±Ø¨Ø±Û\95",
        "tooltip-t-emailuser": "ارسال ایمیل به این کاربر",
        "tooltip-t-info": "اطلاعات بیشتر دربارهٔ این صفحه",
        "tooltip-t-upload": "بارنیائن فایلۀل",
index d78cf6d..ca4f720 100644 (file)
        "nstab-template": "आकृति",
        "nstab-help": "सहायता पृष्ठ",
        "nstab-category": "संवर्ग",
+       "mainpage-nstab": "सम्मुख पन्ना",
        "nosuchaction": "एहेन कोनो क्रिया नै अछि",
        "nosuchactiontext": "ऐ सार्वत्रिक विभव संकेत द्वारा निर्दिष्ट क्रिया अमान्य अछि।\nअहाँ सार्वत्रिक विभव संकेतक गलत टंकण केने हएब, वा कोनो गलत लिंकक पाछाँ गेल हएब।\nई {{अन्तर्जाल}} प्रयोक्ता द्वारा प्रयुक्त तंत्रांशमे स्थित कोनो दोषक संकेत सेहो कऽ सकैए।",
        "nosuchspecialpage": "एहेन कोनो विशेष पृष्ठ नै अछि",
        "rcshowhidemine": "$1 हमर सम्पादन सभ",
        "rcshowhidemine-show": "देखाउ",
        "rcshowhidemine-hide": "नुकाऊ",
+       "rcshowhidecategorization-show": "देखाऊ",
+       "rcshowhidecategorization-hide": "नुकाऊ",
        "rclinks": "देखाऊ अंतिम $1 परिवर्त्तन अंतिम $2 दिनमे<br />$3",
        "diff": "अंतर",
        "hist": "इति.",
        "upload-too-many-redirects": "ई सार्वत्रिक विभव संकेत बड्ड बेसी घुमौआ लागिक संग अछि।",
        "upload-http-error": "परिसंविद भ्रम आएल:$1",
        "upload-copy-upload-invalid-domain": "कपि अपलोड इ डोमेन स उपलब्ध नै अछि।",
+       "upload-dialog-title": "फाइल अपलोड करी",
+       "upload-dialog-button-cancel": "रद्द करी",
        "backend-fail-stream": "\"$1\" केँ नै स्ट्रिम क सकल।",
        "backend-fail-backup": "\"$1\" केँ नै ब्याकअप क सकल।",
        "backend-fail-notexists": "फाइल $1 नै अछि।",
index f20007b..98a177f 100644 (file)
        "october-date": "$1 октомври",
        "november-date": "$1 ноември",
        "december-date": "$1 декември",
+       "period-am": "претпладне",
+       "period-pm": "попладне",
        "pagecategories": "{{PLURAL:$1|Категорија|Категории}}",
        "category_header": "Страници во категоријата „$1“",
        "subcategories": "Поткатегории",
        "unblock": "Одблокирај корисник",
        "blockip": "Блокирај {{GENDER:$1|корисник}}",
        "blockip-legend": "Блокирај корисник",
-       "blockiptext": "Користете го долниот образец за да го забраните пристапот за пишување од одредена IP-адреса или корисничко име.\nОва единствено треба да се прави за да се спречи вандализам, во согласност со [[{{MediaWiki:Policy-url}}|правилата на Википедија]].\nИзберете конкретна причина подолу (на пр. наведувајќи ги страниците што биле вандализирани).",
+       "blockiptext": "Користете го долниот образец за да го забраните пристапот за пишување од одредена IP-адреса или корисничко име.\nОва единствено треба да се прави за да се спречи вандализам, во согласност со [[{{MediaWiki:Policy-url}}|правилата на Википедија]].\nИзберете конкретна причина подолу (на пр. наведувајќи ги страниците што биле вандализирани).\nМожете да блокирате IP-опсези со помош на [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing синтаксата на CIDR]; најголемиот допуштен опсег е /$1 за IPv4 и /$2 за IPv6.",
        "ipaddressorusername": "IP-адреса или корисничко име:",
        "ipbexpiry": "Истек на рокот:",
        "ipbreason": "Причина:",
        "pageinfo-category-files": "Број на податотеки",
        "markaspatrolleddiff": "Означи како проверена верзија",
        "markaspatrolledtext": "Означи ја верзијата како проверена",
+       "markaspatrolledtext-file": "Означи ја верзијава како испатролирана",
        "markedaspatrolled": "Означено како проверено",
        "markedaspatrolledtext": "Избраната преработка на [[:$1]] е означена како испатролирана.",
        "rcpatroldisabled": "Оневозможено проверка на скорешни промени",
        "newimages-legend": "Филтрирај",
        "newimages-label": "Име на податотека (или дел од името):",
        "newimages-showbots": "Прикажувај подигања од ботови",
+       "newimages-hidepatrolled": "Сокриј испатролриани подигања",
        "noimages": "Нема ништо.",
        "ilsubmit": "Барај",
        "bydate": "по датум",
        "hebrew-calendar-m11-gen": "ав",
        "hebrew-calendar-m12-gen": "елул",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|разговор]])",
+       "timezone-local": "Месно",
        "duplicate-defaultsort": "Предупредување: Основниот клуч за подредување „$2“ го поништува претходниот основен клуч за подредување „$1“.",
        "duplicate-displaytitle": "<strong>Предупредување:</strong> Приказниот наслов „$2“ го заменува претходнито приказен наслов „$1“.",
        "invalid-indicator-name": "<strong>Грешка:</strong> Атрибутот <code>name</code> што го покажува статусот на страницата не може да биде празен.",
index a68a15e..81b3308 100644 (file)
        "laggedslavemode": "'''सुचना:''' पानावर अद्ययावत बदल नसतील.",
        "readonly": "विदागारास (डाटाबेस) ताळे आहे.",
        "enterlockreason": "विदागारास ताळे ठोकण्याचे कारण, ताळे उघडले जाण्याच्या अदमासे कालावधीसहीत द्या.",
-       "readonlytext": "बहुधा विदागार परिरक्षणामुळे (मेंटेनन्स) नवीन भर घालण्यापासून आणि इतर बदल करण्यापासून बंद ठेवण्यात आला आहे, परिरक्षणानंतर तो सामान्य होईल.\n\nताळे ठोकणाऱ्या प्रबंधकांनी खालील स्पष्टीकरण नमूद केले आहे: $1",
+       "readonlytext": "बहà¥\81धा à¤µà¤¿à¤¦à¤¾à¤\97ार à¤ªà¤°à¤¿à¤°à¤\95à¥\8dषणामà¥\81ळà¥\87 (मà¥\87à¤\82à¤\9fà¥\87ननà¥\8dस) à¤¨à¤µà¥\80न à¤­à¤° à¤\98ालणà¥\8dयापासà¥\82न à¤\86णि à¤\87तर à¤¬à¤¦à¤² à¤\95रणà¥\8dयापासà¥\82न à¤¬à¤\82द à¤ à¥\87वणà¥\8dयात à¤\86ला à¤\86हà¥\87, à¤ªà¤°à¤¿à¤°à¤\95à¥\8dषणानà¤\82तर à¤¤à¥\8b à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤¹à¥\8bà¤\88ल.\n\nताळà¥\87 à¤ à¥\8bà¤\95णाऱà¥\8dया à¤ªà¥\8dरणालà¥\80 à¤ªà¥\8dरबà¤\82धà¤\95ाà¤\82नà¥\80 à¤\96ालà¥\80ल à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¨à¤®à¥\82द à¤\95à¥\87लà¥\87 à¤\86हà¥\87: $1",
        "missing-article": "डाटाबेसला \"$1\" $2 नावाचे पान मिळालेले नाही, जे मिळायला हवे होते.\n\nअसे बहुदा संपुष्टात आलेल्या फरकामुळे किंवा वगळलेल्या पानाच्या इतिहास दुव्यामुळे घडते.\n\nजर असे घडलेले नसेल, तर तुम्हाला प्रणाली मधील त्रुटी आढळलेली असू शकते.\nकृपया याबद्दल एखाद्या [[Special:ListUsers/sysop|प्रचालकाशी]] चर्चा करा व या URLची नोंद करा.",
        "missingarticle-rev": "(आवृत्ती#: $1)",
        "missingarticle-diff": "(फरक: $1, $2)",
        "mypreferencesprotected": "आपणास आपला पसंतीक्रम बदलण्याची परवानगी नाही.",
        "ns-specialprotected": "विशेष पाने संपादित करता येत नाहीत.",
        "titleprotected": "या शीर्षकाचे पान सदस्य [[User:$1|$1]]ने निर्मितीपासून सुरक्षित केलेले आहे.त्याने याचे \"\"$2\"\" हे कारण नमूद केलेले आहे.",
-       "filereadonlyerror": "\"$1\" à¤²à¤¾ à¤¸à¥\81धार à¤\85शà¤\95à¥\8dय à¤\86हà¥\87 à¤\95ारण à¤¸à¤\82à¤\9aिà¤\95ाभाà¤\82डार  \"$2\" à¤¹à¥\87 'फà¤\95à¥\8dत à¤µà¤¾à¤\9aा'(रà¥\80ड à¤\93नà¥\8dलà¥\80) à¤¯à¤¾ à¤¶à¥\8dरà¥\87णà¥\80तà¤\9a à¤\86हà¥\87.\n\nà¤\9cà¥\8dया à¤ªà¥\8dरशासà¤\95ानà¥\87 à¤¹à¥\87 à¤\95à¥\81लà¥\81पबà¤\82द à¤\95à¥\87लà¥\87 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¦à¤¿à¤²à¥\87लà¥\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤\86हà¥\87: \"$3\"",
+       "filereadonlyerror": "\"$1\" à¤¸à¤\82à¤\9aिà¤\95à¥\87à¤\9aा à¤¸à¥\81धार à¤\85शà¤\95à¥\8dय à¤\86हà¥\87 à¤\95ारण à¤¸à¤\82à¤\9aिà¤\95ाभाà¤\82डार  \"$2\" à¤¹à¥\87 'फà¤\95à¥\8dत à¤µà¤¾à¤\9aा'(रà¥\80ड à¤\93नà¥\8dलà¥\80) à¤¯à¤¾ à¤¸à¥\8dथितà¥\80तà¤\9a à¤\86हà¥\87.\n\nà¤\9cà¥\8dया à¤ªà¥\8dरशासà¤\95ानà¥\87 à¤¹à¥\87 à¤\95à¥\81लà¥\81पबà¤\82द à¤\95à¥\87लà¥\87 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¦à¤¿à¤²à¥\87लà¥\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤\86हà¥\87: \"$3\".",
        "invalidtitle-knownnamespace": "\"$2\" नामविश्वात \"$3\" मजकूराचे अयोग्य शीर्षक",
        "invalidtitle-unknownnamespace": "अनोळखी नामविश्वाच्या आकड्यासह अवैध मथळा $1 व मजकूर \"$2\"",
        "exception-nologin": "सनोंद-प्रवेशित नाही",
        "copyrightwarning2": "{{SITENAME}} येथे केलेले कोणतेही लेखन हे इतर संपादकांकरवी बदलले अथवा काढले जाऊ शकते. जर आपणास आपल्या लेखनाचे मुक्त संपादन होणे पसंत नसेल तर येथे संपादन करू नये.<br />\nतुम्ही येथे लेखन करताना हे सुद्धा गृहीत धरलेले असते की येथे केलेले लेखन तुमचे स्वतःचे आणि केवळ स्वतःच्या प्रताधिकार (कॉपीराईट) मालकीचे आहे किंवा प्रताधिकाराने गठित न होणाऱ्या सार्वजनिक ज्ञानक्षेत्रातून घेतले आहे किंवा तत्सम मुक्त स्रोतातून घेतले आहे. तुम्ही संपादन करताना तसे वचन देत आहात (अधिक माहितीसाठी $1 पहा). '''प्रताधिकारयुक्त लेखन सुयोग्य परवानगीशिवाय मुळीच चढवू/भरू नये!'''",
        "editpage-cannot-use-custom-model": "या पानाचा आशय-आराखडा(कंटेन्ट मॉडेल) बदलता येणार नाही.",
        "longpageerror": "<strong>त्रुटी:आपण दिलेला मजकूर जास्तीत जास्त शक्य {{PLURAL:$2|१ किलोबाईट पेक्षा|$2 किलोबाईट पेक्षा}}  अधिक लांबीचा {{PLURAL:$1|१ किलोबाईट|$1 किलोबाईट}} आहे.</strong>\nतो जतन केला जाऊ शकत नाही.",
-       "readonlywarning": "'''सावधान:विदागारास अनुरक्षणासाठी(मेंटेनन्स) ताळे ठोकले आहे,त्यामुळे सध्याच तुम्ही तुमचे संपादन जतन करू शकत नाही.'''\nजर तुम्हाला हवे असेल तर नंतर उपयोग करण्याच्या दृष्टीने, तुम्ही मजकूर नक्कल करुन, पुढील संपादनासाठी ’मजकुर संचिकेत’(टेक्स्ट फाईल)चिटकवू शकता.\nविदागारास ताळे ठोकलेल्या प्रचालकांनी खालील स्पष्टीकरण दिले आहे:$1",
+       "readonlywarning": "<strong>सावधान:विदागारास अनुरक्षणासाठी(मेंटेनन्स) ताळे ठोकले आहे,त्यामुळे सध्याच तुम्ही तुमचे संपादन जतन करू शकत नाही.</strong>\nजर तुम्हाला हवे असेल तर नंतर उपयोग करण्याच्या दृष्टीने, तुम्ही मजकूर नक्कल करुन, पुढील संपादनासाठी ’मजकुर संचिकेत’(टेक्स्ट फाईल)चिटकवू शकता.\nविदागारास ताळे ठोकलेल्या प्रणाली प्रशासकांनी खालील स्पष्टीकरण दिले आहे:$1",
        "protectedpagewarning": "'''सूचना: हे सुरक्षित पान आहे. फक्त प्रचालक याच्यात बदल करू शकतात.'''",
        "semiprotectedpagewarning": "'''सूचना:''' हे पान सुरक्षित आहे. फक्त नोंदणीकृत सदस्य याच्यात बदल करू शकतात.",
        "cascadeprotectedwarning": "<strong>ताकिद:</strong>हे पान निम्न-लिखीत निपतन-प्रतिबंधीत {{PLURAL:$1|पानात|पानांत}} आंतरभूत असल्यामुळे,केवळ प्रचालक-सुविधाप्राप्त सदस्यांनाच संपादन करता यावे असे ताळे त्यास ठोकलेले आहे :",
        "permissionserrors": "परवानगीस नकार",
        "permissionserrorstext": "खालील{{PLURAL:$1|कारणामुळे|कारणांमुळे}} तुम्हाला तसे करण्याची परवानगी नाही:",
        "permissionserrorstext-withaction": "तुम्हाला $2 क्रियेची परवानगी नाही, खालील {{PLURAL:$1|कारणासाठी|कारणांसाठी}}:",
-       "contentmodelediterror": "ही आवृत्ती आपण संपादू शकत नाही कारण त्याचा आशय-आराखडा (कंटेन्ट मॉडेल)<code>$1</code> आहे व सध्याच्या पानाचा आशय आराखडा <code>$2</code> आहे.",
+       "contentmodelediterror": "ही आवृत्ती आपण संपादू शकत नाही कारण त्याचा आशय-आराखडा (कंटेन्ट मॉडेल)<code>$1</code> आहे व सध्याच्या <code>$2</code> पानाचा आशय आराखडा वेगळा आहे.",
        "recreate-moveddeleted-warn": "'''सूचना: पूर्वी वगळलेला लेख तुम्ही पुन्हा बनवित आहात.'''\n\nआपण याचा विचार करा कि या पानाचे संपादन यापुढे करणे योग्य आहे काय.या पानाच्या वगळण्याच्या व स्थानांतराच्या नोंदी आपल्या (कामाच्या) सुलभतेसाठी दिलेल्या आहेत:",
        "moveddeleted-notice": "हे पान वगळण्यात आलेले आहे.\nसंदर्भासाठी, वगळण्याची व स्थानांतराची नोंद खाली दिलेली आहे.",
        "moveddeleted-notice-recent": "माफ करा,हे पान अलीकडेच (मागील २४ तासात) वगळल्या गेले आहे.हा पानाच्या वगळण्याचा व हलविण्याचा लॉग संदर्भासाठी खाली दिला आहे.",
        "usereditcount": "$1 {{PLURAL:$1|संपादन|संपादने}}",
        "usercreated": "दि. $1 ला, $2 वाजता, सदस्य खाते{{GENDER:$3|द्वारे बनविल्या गेले}}",
        "newpages": "नवीन पाने",
+       "newpages-submit": "दाखवा",
        "newpages-username": "सदस्य नाव:",
        "ancientpages": "जुनी पाने",
        "move": "स्थानांतरण",
        "specialloguserlabel": "कार्यकर्ता:",
        "speciallogtitlelabel": "लक्ष (शिर्षक किंवा {{ns:user}}:सदस्याचे सदस्यनाव):",
        "log": "नोंदी",
+       "logeventslist-submit": "दाखवा",
        "all-logs-page": "सर्व सार्वजनिक नोंदी",
        "alllogstext": "{{SITENAME}}च्या सर्व नोंदीचे एकत्र दर्शन.नोंद प्रकार, सदस्यनाव किंवा बाधित पान निवडून तुम्ही तुमचे दृश्यपान मर्यादित करू शकता.",
        "logempty": "नोंदीत अशी बाब नाही.",
        "cachedspecial-viewing-cached-ts": "तुम्ही या पानाची कॅशेतील आवृत्ती पहात आहात जी, पुर्णतः मूळ आवृत्ती नसू शकते.",
        "cachedspecial-refresh-now": "नुकतेच केलेले दाखवा.",
        "categories": "वर्ग",
+       "categories-submit": "दाखवा",
        "categoriespagetext": "विकिवर खालील वर्ग {{PLURAL:$1|आहे|आहेत}}.\n[[Special:UnusedCategories|न वापरलेले वर्ग]] येथे दाखवलेले नाहीत.\nहेही पहा: [[Special:WantedCategories|पाहिजे असलेले वर्ग]].",
        "categoriesfrom": "या शब्दापासून सुरू होणारे वर्ग दाखवा:",
        "special-categories-sort-count": "मोजणीनुसार निवडा",
        "delete-confirm": "\"$1\" वगळा",
        "delete-legend": "वगळा",
        "historywarning": "<strong>इशारा:</strong> आपण वगळत असलेल्या पानाला $1 {{PLURAL:$1|आवर्तनाचा|आवर्तनांचा}} इतिहास आहे:",
+       "historyaction-submit": "दाखवा",
        "confirmdeletetext": "आपण एक लेखपान त्याच्या सर्व इतिहासासोबत वगळण्याच्या तयारीत आहात.\nकृपया, याची खात्री कि, करीत असलेल्या कृतीचे परिणाम, आपण कृती करण्यापूर्वी जाणून घेतले आहेत व आपण हे   [[{{MediaWiki:Policy-url}}|मीडियाविकीच्या नीतीनुसारच]] करीत आहात.",
        "actioncomplete": "काम पूर्ण",
        "actionfailed": "कृती अयशस्वी झाली",
        "unblock": "सदस्यप्रतिबंध काढा",
        "blockip": "{{GENDER:$1|सदस्यास}} प्रतिबंधित करा",
        "blockip-legend": "सदस्यास प्रतिबंध करा",
-       "blockiptext": "एखाद्या विशिष्ट अंकपत्त्याची किंवा सदस्याची लिहिण्याची क्षमता प्रतिबंधित  करण्याकरिता खालील सारणी वापरा.\nहे केवळ उच्छेद टाळण्याच्याच दृष्टीने आणि [[{{MediaWiki:Policy-url}}|निती]]स अनुसरून केले पाहिजे.\nखाली विशिष्ट कारण भरा(उदाहरणार्थ,ज्या पानांवर उच्छेद माजवला गेला त्यांची उद्धरणे देऊन).",
+       "blockiptext": "एखाद्या विशिष्ट अंकपत्त्याची किंवा सदस्याची लिहिण्याची क्षमता प्रतिबंधित  करण्याकरिता खालील सारणी वापरा.\nहे केवळ उच्छेद टाळण्याच्याच दृष्टीने आणि [[{{MediaWiki:Policy-url}}|निती]]स अनुसरून केले पाहिजे.\nखाली विशिष्ट कारण भरा(उदाहरणार्थ,ज्या पानांवर उच्छेद माजवला गेला त्यांची उद्धरणे देऊन).\nआपण [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] ही वाक्यरचना वापरुन अंकपत्त्याचा आवाका प्रतिबंधित करु शकता. जास्तीत जास्त अनुमानित आवाका आहे  /$1  IPv4 साठी व /$2 IPv6 साठी.",
        "ipaddressorusername": "अंकपत्ता किंवा सदस्यनाम:",
        "ipbexpiry": "समाप्ति:",
        "ipbreason": "कारण:",
        "pageinfo-category-files": "संचिकांची संख्या",
        "markaspatrolleddiff": "टेहळणी केल्याची खूण करा",
        "markaspatrolledtext": "या पानावर गस्त झाल्याची खूण करा",
+       "markaspatrolledtext-file": "या संचिकेच्या आवृत्तीस गस्त घातली म्हणून् खूण करा",
        "markedaspatrolled": "गस्त केल्याची खूण केली",
        "markedaspatrolledtext": "निवडलेल्या [[:$1]]च्या आवर्तनास गस्त घातल्याची खूण केली.",
        "rcpatroldisabled": "अलीकडील बदलची गस्ती अनुपलब्ध",
        "newimages-legend": "गाळक",
        "newimages-label": "संचिकानाम (किंवा त्याचा भाग):",
        "newimages-showbots": "सांगकाम्याद्वारे केलेली अपभारणे दाखवा",
+       "newimages-hidepatrolled": "गस्त घातलेली अपभारणे लपवा",
        "noimages": "बघण्यासारखे येथे काही नाही.",
        "ilsubmit": "शोधा",
        "bydate": "तारखेनुसार",
index 4e6d608..cb92008 100644 (file)
                        "Zawthet",
                        "ကိုရာဝီ",
                        "아라",
-                       "9.sinistra"
+                       "9.sinistra",
+                       "Ninjastrikers"
                ]
        },
        "tog-underline": "လင့်ကို မျဉ်းသားသည့် ပုံစံ -",
        "tog-hideminor": "လတ်တလော အပြောင်းအလဲများတွင် အရေးမကြီးသည်များကို ဝှက်ရန်",
-       "tog-hidepatrolled": "လတ်တလော အပြောင်းအလဲများတွင် အရေးမကြီးသည်များကို ဝှက်ရန်",
-       "tog-newpageshidepatrolled": "လက်တလော အပြောင်းလဲများတွင် စာမျက်နှာသစ်များကို ဝှက်ရန်",
+       "tog-hidepatrolled": "လတ်တလော အပြောင်းအလဲများတွင် ကင်းလှည့်တည်းဖြတ်မှုများကို ဝှက်ရန်",
+       "tog-newpageshidepatrolled": "စာမျက်နှာသစ်စာရင်းတွင် ကင်းလှည့်စာမျက်နှာများကို ဝှက်ရန်",
+       "tog-hidecategorization": "စာမျက်နှာများ၏ ကဏ္ဍကို ဝှက်ရန်",
        "tog-extendwatchlist": "စောင့်ကြည့်စာရင်းတွင် ပြောင်းလဲမှုအားလုံးအား  ပြရန်။",
-       "tog-usenewrc": "á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸á\80\96á\80¼á\80\84á\80·á\80º á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\9cá\80­á\80¯á\80\80á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸ (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
-       "tog-numberheadings": "ခေါင်းစဉ်များ အား စေ့ဆော်ချက်အတိုင်း လုပ်ဆောင်ရန်",
-       "tog-showtoolbar": "á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\9bá\80\94á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸ (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
-       "tog-editondblclick": "á\80\80á\80\9cá\80\85á\80ºá\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\94á\80¾á\80­á\80\95á\80ºá\80\9cá\80»á\80¾á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\95á\80« (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
+       "tog-usenewrc": "á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\9bá\80¾á\80­ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\9cá\80­á\80¯á\80\80á\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\81á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸",
+       "tog-numberheadings": "ခေါင်းစဉ်များအား အလိုအလျောက် နံပါတ်စဉ်ရန်",
+       "tog-showtoolbar": "á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\98á\80¬á\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80\9bá\80\94á\80º",
+       "tog-editondblclick": "á\80\80á\80\9cá\80\85á\80ºá\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\94á\80¾á\80­á\80\95á\80ºá\80\95á\80¼á\80®á\80¸ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80¡á\80¬á\80¸ á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\9bá\80\94á\80º",
        "tog-editsectiononrightclick": "အပိုင်းလိုက်ခေါင်းစဉ်များကို ညာကလစ်နှိပ်ခြင်းဖြင့် အပိုင်းလိုက် တည်းဖြတ်ခြင်းကို အသုံးပြုရန်",
        "tog-watchcreations": "ကျွန်ုပ်စတင်ရေးသားခဲ့သည့်စာမျက်နှာများနှင့် အပ်လုပ်တင်ခဲ့သည့် ဖိုင်များကို စောင့်​ကြည့်​စာ​ရင်း​ထဲ ပေါင်းထည့်ရန်",
        "tog-watchdefault": "ကျွန်ုပ် တည်းဖြတ်ခဲ့သည့် စာမျက်နှာများနှင့် ဖိုင်များကို စောင့်ကြည့်စာရင်းသို့  ပေါင်းထည့်ပါ။",
        "tog-minordefault": "တည်းဖြတ်မှုအားလုံးသည် အရေးမကြီးသော တည်းဖြတ်မှုဟု ပုံသေသတ်မှတ်ရန်",
        "tog-previewontop": "တည်းဖြတ်သည့်အကွက်မတိုင်မီ နမူနာကို ပြရန်",
        "tog-previewonfirst": "ပထမတည်းဖြတ်မှုတွင် နမူနာကို ပြရန်",
-       "tog-enotifwatchlistpages": "ကျွန်ုပ်၏စောင့်ကြည့်စာရင်းမှ စာမျက်နှာတစ်ခု သို့မဟုတ် ဖိုင်တစ်ခုကို ပြောင်းလဲလိုက်ပါက ကျွနုပ်ဆီ အီးမေးပို့ရန်",
+       "tog-enotifwatchlistpages": "á\80\80á\80»á\80½á\80\94á\80ºá\80¯á\80\95á\80ºá\81\8fá\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\9eá\80­á\80¯á\80·á\80\99á\80\9fá\80¯á\80\90á\80º á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\9cá\80­á\80¯á\80\80á\80ºá\80\95á\80«á\80\80 á\80\80á\80»á\80½á\80\94á\80ºá\80¯á\80\95á\80ºá\80\86á\80® á\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\95á\80­á\80¯á\80·á\80\9bá\80\94á\80º",
        "tog-enotifusertalkpages": "ကျွန်ုပ်၏ဆွေးနွေးချက်စာမျက်နှာ ပြောင်းလဲမှုရှိပါက ကျွန်ုပ်ထံ အီးမေးပို့ရန်",
        "tog-enotifminoredits": "စာမျက်နှာများနှင့် ဖိုင်များ၏ အရေးမကြီးသော တည်းဖြတ်မှုများကိုလည်း အီးမေးပို့ရန်",
        "tog-enotifrevealaddr": " အသိပေးချက်အီးမေးများတွင် ကျွန်ုပ်၏ အီးမေးလိပ်စာကို ဖော်ပြရန်",
        "tog-shownumberswatching": "စောင့်ကြည့်နေသော အသုံးပြုသူအရေအတွက်ကို ပြရန်",
-       "tog-oldsig": "ရှိနှင့်ပြီးသား လက်မှတ်၏ နမူနာ -",
+       "tog-oldsig": "ရှိနှင့်ပြီးသား လက်မှတ် -",
        "tog-fancysig": "လက်မှတ်ကို ဝီကီလင့်အဖြစ် သတ်မှတ်ရန် (အလိုအလျောက်လင့်မပါဘဲနှင့်)",
        "tog-forceeditsummary": "တည်းဖြတ်အတိုချုပ် ဗလာဖြစ်နေလျှင် သတိပေးရန်",
        "tog-watchlisthideown": "ကျွန်ုပ်၏ တည်းဖြတ်မှုများကို စောင့်ကြည့်စာရင်းမှ ဝှက်ထားရန်",
        "tog-watchlisthideliu": "စောင့်ကြည့်စာရင်းမှ loggin ဝင်ထားသော အသုံးပြုသူတို့၏ တည်းဖြတ်မှုများကို ဝှက်ရန်",
        "tog-watchlisthideanons": "စောင့်ကြည့်စာရင်းမှ အမည်မသိ အသုံးပြုသူများ၏ တည်းဖြတ်မှုများကို ဝှက်ရန်",
        "tog-watchlisthidepatrolled": "patrolled တည်းဖြတ်မှုများကို စောင့်ကြည့်စာရင်းမှ ဝှက်ထားရန်",
+       "tog-watchlisthidecategorization": "စာမျက်နှာများ၏ ကဏ္ဍကို ဝှက်ရန်",
        "tog-ccmeonemails": "ကျွန်ုပ် အခြားအသုံးပြုသူများထံပို့သော အီးမေးမိတ္တူကို ကျွန်ုပ်ထံ ပြန်ပို့ရန်",
        "tog-diffonly": "ကွဲပြားမှုများအောက်ရှိ စာမျက်နှာတွင်ပါဝင်သည်များကို မပြပါနှင့်",
        "tog-showhiddencats": "ဝှက်ထားသော ကဏ္ဍများကို ပြရန်",
        "tog-useeditwarning": "မသိမ်းရသေးသော ပြောင်းလဲမှုများ နှင့် တည်းဖြတ်ဆဲစာမျက်နှာမှ ထွက်သွားလျှင် သတိပေးပါ",
+       "tog-prefershttps": "log in ဝင်တိုင်း လုံခြုံသော ဆက်သွယ်မှုကို အသုံးပြုရန်",
        "underline-always": "အမြဲ",
        "underline-never": "ဘယ်သောအခါမျှ",
        "underline-default": "ဘရောက်ဆာ သို့ Skin default အတိုင်း",
@@ -70,7 +74,7 @@
        "monday": "တနင်္လာ",
        "tuesday": "အင်္ဂါ",
        "wednesday": "ဗုဒ္ဓဟူး",
-       "thursday": "ကြာ​သ​ပ​တေး​",
+       "thursday": "ကြာသပတေး",
        "friday": "သောကြာ",
        "saturday": "စနေ",
        "sun": "နွေ",
        "thu": "တေး",
        "fri": "ကြာ",
        "sat": "နေ",
-       "january": "ဇန်​န​ဝါ​ရီ​",
+       "january": "ဇန်နဝါရီ",
        "february": "ဖေဖော်ဝါရီ",
-       "march": "မတ်",
-       "april": "ဧ​ပြီ​",
-       "may_long": "မေ",
-       "june": "ဇွန်",
-       "july": "ဇူ​လိုင်​",
-       "august": "ဩ​ဂုတ်​",
-       "september": "စက်​တင်​ဘာ​",
-       "october": "အောက်​တို​ဘာ​",
-       "november": "နို​ဝင်​ဘာ​",
-       "december": "ဒီ​ဇင်​ဘာ​",
-       "january-gen": "ဇန်​န​ဝါ​ရီ​",
-       "february-gen": "ဖေ​ဖော်​ဝါ​ရီ​",
-       "march-gen": "မတ်",
-       "april-gen": "ဧ​ပြီ​",
-       "may-gen": "မေ",
-       "june-gen": "ဇွန်",
-       "july-gen": "ဇူ​လိုင်​",
-       "august-gen": "ဩ​ဂုတ်​",
-       "september-gen": "စက်​တင်​ဘာ​",
-       "october-gen": "အောက်​တို​ဘာ​",
-       "november-gen": "နို​ဝင်​ဘာ​",
-       "december-gen": "ဒီ​ဇင်​ဘာ​",
+       "march": "မတ်",
+       "april": "ဧပြီ",
+       "may_long": "မေ",
+       "june": "ဇွန်",
+       "july": "ဇူလိုင်",
+       "august": "ဩဂုတ်",
+       "september": "စက်တင်ဘာ",
+       "october": "အောက်တိုဘာ",
+       "november": "နိုဝင်ဘာ",
+       "december": "ဒီဇင်ဘာ",
+       "january-gen": "ဇန်နဝါရီ",
+       "february-gen": "ဖေဖော်ဝါရီ",
+       "march-gen": "မတ်",
+       "april-gen": "ဧပြီ",
+       "may-gen": "မေ",
+       "june-gen": "ဇွန်",
+       "july-gen": "ဇူလိုင်",
+       "august-gen": "ဩဂုတ်",
+       "september-gen": "စက်တင်ဘာ",
+       "october-gen": "အောက်တိုဘာ",
+       "november-gen": "နိုဝင်ဘာ",
+       "december-gen": "ဒီဇင်ဘာ",
        "jan": "ဇန်",
        "feb": "ဖေ",
        "mar": "မတ်",
        "apr": "ဧ",
-       "may": "မေ",
+       "may": "မေ",
        "jun": "ဇွန်",
        "jul": "ဇူ",
        "aug": "ဩ",
        "march-date": "မတ် $1",
        "april-date": "ဧပြီ $1",
        "may-date": "မေ $1",
-       "june-date": "á\80\82á\80»ွန် $1",
-       "july-date": "á\80\82á\80»ူလိုင် $1",
+       "june-date": "á\80\87ွန် $1",
+       "july-date": "á\80\87ူလိုင် $1",
        "august-date": "ဩဂုတ် $1",
        "september-date": "စက်တင်ဘာ $1",
        "october-date": "အောက်တိုဘာ $1",
        "november-date": "နိုဝင်ဘာ $1",
        "december-date": "ဒီဇင်ဘာ $1",
-       "pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍ}}",
+       "pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍများ}}",
        "category_header": "ကဏ္ဍ \"$1\" မှ စာမျက်နှာများ",
-       "subcategories": "á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯ခွဲ",
+       "subcategories": "á\80\80á\80\8fá\80¹á\80\8dခွဲ",
        "category-media-header": "ကဏ္ဍ \"$1\" မှ မီဒီယာ",
-       "category-empty": "ဗလာအုပ်စု",
+       "category-empty": "<em>ဤကဏ္ဍသည် လက်ရှိတွင် စာမျက်နှာများ သို့မဟုတ် မီဒီယာများ မရှိပါ။</em>",
        "hidden-categories": "{{PLURAL:$1|ဝှက်ထားသော ကဏ္ဍ|ဝှက်ထားသော ကဏ္ဍများ}}",
        "hidden-category-category": "ဝှက်ထားသော ကဏ္ဍများ",
        "category-subcat-count": "{{PLURAL:$2|ဤကဏ္ဍတွင် အောက်ပါ ကဏ္ဍခွဲသာ ရှိသည်။ |ဤကဏ္ဍတွင် စုစုပေါင်း $2 ခု အနက်မှ အောက်ပါ {{PLURAL:$1|ကဏ္ဍခွဲ|ကဏ္ဍခွဲ $1 ခု}} ရှိသည်။}}",
        "newwindow": "(ဝင်းဒိုးအသစ်တခုကိုဖွင့်ရန်)",
        "cancel": "မ​လုပ်​တော့​",
        "moredotdotdot": "နောက်ထပ်...",
-       "morenotlisted": "á\80\94á\80±á\80¬á\80\80á\80ºá\80\91á\80\95á\80º á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«...",
+       "morenotlisted": "á\80¤á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\99á\80¾á\80¬ á\80\99á\80\95á\80¼á\80\8aá\80·á\80ºá\80\85á\80¯á\80¶á\80\95á\80«á\81\8b",
        "mypage": "စာမျက်နှာ",
        "mytalk": "ဆွေးနွေးချက်",
-       "anontalk": "á\80¤ IP address á\80¡á\80\90á\80½á\80\80á\80º á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\9bá\80\94á\80º",
+       "anontalk": "ဆွေးနွေးရန်",
        "navigation": "အ​ညွှန်း​",
        "and": "&#32;နှင့်",
        "qbfind": "ရှာပါ",
        "actions": "ဆောင်ရွက်ချက်များ",
        "namespaces": "အမည်ညွှန်းများ",
        "variants": "အမျိုးမျိုးအပြားပြား",
+       "navigation-heading": "လမ်းညွှန်မီနူး",
        "errorpagetitle": "အမှား",
        "returnto": "$1 သို့ ပြန်သွားရန်။",
        "tagline": "{{SITENAME}} မှ",
        "searcharticle": "သွား​ပါ​",
        "history": "စာမျက်နှာ ရာဇဝင်",
        "history_short": "ရာဇဝင်",
-       "updatedmarker": "á\80\94á\80±á\80¬á\80\80á\80ºá\80\86á\80¯á\80¶á\80¸á\80\91á\80¬á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸á\80\9eá\80\8aá\80·á\80ºá\80\94á\80±á\80¬á\80\80á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80ºá\81\8b",
+       "updatedmarker": "နောက်ဆုံးကြည့်ပြီးသည့်နောက်ပိုင်း တည်းဖြတ်ထားသည်။",
        "printableversion": "ပရင့်ထုတ်ရန်",
        "permalink": "ပုံ​သေ​လိပ်​စာ​",
        "print": "ပရင့်",
        "view": "ကြည့်ရန်",
+       "view-foreign": "$1 တွင် ကြည့်ရန်",
        "edit": "ပြင်​ဆင်​ရန်​",
        "create": "စတင်ရေးသားရန်",
        "editthispage": "ဤစာမျက်နှာကို ပြင်ရန်",
        "create-this-page": "ဤစာမျက်နှာကို စတင်ရေးသားရန်",
        "delete": "ဖျက်​ပါ​",
        "deletethispage": "ဤစာမျက်နှာဖျက်ပါ",
+       "undeletethispage": "ဤစာမျက်နှာကို မဖျက်တော့ရန်",
        "undelete_short": "{{PLURAL:$1|တည်းဖြတ်မှုတစ်ခု|တည်းဖြတ်မှု $1 ခုတို့}}ကို မဖျက်တော့ရန်",
        "viewdeleted_short": "{{PLURAL:$1|ဖျက်လိုက်သည့်တည်းဖြတ်မှုတစ်ခု|ဖျက်လိုက်သည့် တည်းဖြတ်မှု $1 ခု}}ကို ကြည့်ရန်",
-       "protect": "á\80\91á\80­á\80\99á\80ºá\80¸â\80\8bá\80\9eá\80­á\80\99á\80ºá\80¸â\80\8bá\80\95á\80«â\80\8b",
+       "protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\95á\80«",
        "protect_change": "ပြောင်းလဲရန်",
        "protectthispage": "ဤစာမျက်နှာကို ကာကွယ်ရန်",
-       "unprotect": "á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
-       "unprotectthispage": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
+       "unprotect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
+       "unprotectthispage": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "newpage": "စာမျက်နှာအသစ်",
        "talkpage": "ဆွေးနွေးရန်",
-       "talkpagelinktext": "ဆွေးနွေးရန်",
+       "talkpagelinktext": "ဆွေးနွေး",
        "specialpage": "အထူး စာမျက်နှာ",
        "personaltools": "ကိုယ်ပိုင် ကိရိယာများ",
        "articlepage": "မာတိကာ ကြည့်ရန်",
        "talk": "ဆွေးနွေးချက်များ",
        "views": "ပုံပန်းသွင်ပြင်",
-       "toolbox": "á\80\9cá\80\80á\80ºá\80\85á\80½á\80² á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸",
+       "toolbox": "ကိရိယာများ",
        "userpage": "အသုံးပြုသူ၏ စာမျက်နှာကို ကြည့်ရန်",
        "projectpage": "ပရောဂျက်စာမျက်နှာကို ကြည့်ရန်",
        "imagepage": "ဖိုင်စာမျက်နှာကိုကြည့်ရန်",
-       "mediawikipage": "á\80\99á\80®á\80\92á\80®á\80\9aá\80¬á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬",
+       "mediawikipage": "á\80\85á\80¬á\80\90á\80­á\80¯á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\9bá\80\94á\80º",
        "templatepage": "တမ်းပလိတ်စာမျက်နှာကို ကြည့်ရန်",
-       "viewhelppage": "á\80\80á\80°á\80\8aá\80®á\80\99á\80\8aá\80·á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ကြည့်ရန်",
-       "categorypage": "á\80\80á\80\8fá\80¹á\80\8dá\80\99á\80»á\80¬á\80¸ကို ကြည့်ရန်",
+       "viewhelppage": "á\80¡á\80\80á\80°á\80¡á\80\8aá\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ ကြည့်ရန်",
+       "categorypage": "á\80\80á\80\8fá\80¹á\80\8dá\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ကို ကြည့်ရန်",
        "viewtalkpage": "ဆွေးနွေးမှုကို ကြည့်ရန်",
        "otherlanguages": "တခြား ဘာသာဖြင့်",
        "redirectedfrom": "($1 မှ ပြန်ညွှန်းထားသည်)",
        "redirectpagesub": "ပြန်ညွှန်းသော စာမျက်နှာ",
+       "redirectto": "ပြန်ညွှန်းရန် -",
        "lastmodifiedat": "ဤစာမျက်နှာကို $1 ရက် $2 အချိန်တွင် နောက်ဆုံး ပြင်ဆင်ခဲ့သည်။",
        "viewcount": "ဤစာမျက်နှာကို {{PLURAL:$1|တစ်ကြိမ်|$1 ကြိမ်}} ဝင်ထားသည်။",
        "protectedpage": "ကာကွယ်ထားသည့် စာမျက်နှာ",
        "jumptonavigation": "အ​ညွှန်း​",
        "jumptosearch": "ရှာ​ဖွေ​ရန်​",
        "view-pool-error": "ဆာဗာသည် ယခုအချိန်တွင် မမျှသောဝန်ကို ထမ်းနေရသည်။\nအသုံးပြုသူ အမြောက်အများက ဤစာမျက်နှာကို ကြည့်ရှုရန် ကြိုးပမ်းနေကြသည်။\nဤစာမျက်နှာကို နောက်တစ်ကြိမ် ပြန်မကြည့်မီ ခဏတာမျှ စောင့်ပါ။\n\n$1",
+       "generic-pool-error": "ဝမ်းနည်းပါသည်၊ ဆာဗာများသည် ယခုအချိန်တွင် မမျှသောဝန်ကို ထမ်းနေရသည်။\nအသုံးပြုသူ အမြောက်အများက ဤစာမျက်နှာကို ကြည့်ရှုရန် ကြိုးပမ်းနေကြသည်။\nဤစာမျက်နှာကို နောက်တစ်ကြိမ် ပြန်မကြည့်မီ ခဏတာမျှ စောင့်ပါ။",
        "pool-errorunknown": "အမည်မသိအမှား",
+       "poolcounter-usage-error": "အသုံးပြုမှု အမှား: $1",
        "aboutsite": "{{SITENAME}} အကြောင်း",
        "aboutpage": "Project: အကြောင်းအရာ",
-       "copyright": "$1 အောက်တွင် ဤအကြောင်းအရာကို ရရှိနိုင်သည်။",
+       "copyright": "$1 á\80¡á\80±á\80¬á\80\80á\80ºá\80\90á\80½á\80\84á\80º á\80¤á\80¡á\80\80á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9bá\80¬á\80\80á\80­á\80¯ á\80\99á\80¾á\80\90á\80ºá\80\9eá\80¬á\80¸á\80\99á\80\91á\80¬á\80¸á\80\95á\80«á\80\80 á\80\9bá\80\9bá\80¾á\80­á\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b",
        "copyrightpage": "{{ns:project}}: မူပိုင်ခွင့်",
-       "currentevents": "လက်​ရှိ​လုပ်​ငန်း​များ​",
-       "currentevents-url": "Project:လက်​ရှိ​လုပ်​ငန်း​များ​",
+       "currentevents": "လက်ရှိဖြစ်ရပ်များ",
+       "currentevents-url": "Project:လက်ရှိဖြစ်ရပ်များ",
        "disclaimers": "သတိပြုစရာများ",
        "disclaimerpage": "Project: အထွေထွေ သတိပြုဖွယ်",
        "edithelp": "ပြင်​ဆင်​ရန် အ​ကူ​အ​ညီ​",
+       "helppage-top-gethelp": "အကူအညီ",
        "mainpage": "ဗဟိုစာမျက်နှာ",
        "mainpage-description": "ဗ​ဟို​စာ​မျက်​နှာ​",
        "policy-url": "Project:မူ​ဝါ​ဒ",
        "privacy": "ကိုယ်ပိုင်ရေးရာ မူဝါဒ",
        "privacypage": "Project: ကိုယ်ပိုင်ရေးရာ မူဝါဒ",
        "badaccess": "ခွင့်ပြုချက်မှ အမှား",
-       "versionrequired": "မီဒီယာဝီကီဗာရှင်း $1 လိုအပ်သည်",
+       "badaccess-groups": "သင်တောင်းဆိုလိုက်သော လုပ်ဆောင်ချက်ကို {{PLURAL:$2|အုပ်စု|အုပ်စုဝင်}}: $1 ၏ အသုံးပြုသူများကိုသာ ကန့်သတ်ထားသည်။",
+       "versionrequired": "မီဒီယာဝီကီဗားရှင်း $1 လိုအပ်သည်",
        "versionrequiredtext": "ဤစာမျက်နှာကို ကြည့်ရန် မီဒီယာဝီကီဗာရှင်း $1 လိုအပ်သည်။\n[[Special:Version|ဗားရှင်းစာမျက်နှာ]]ကို ကြည့်ပါ။",
        "ok": "အိုကေ",
        "retrievedfrom": "\"$1\" မှ ရယူရန်",
        "youhavenewmessages": "သင့်တွင် $1 ($2) ရှိသည်။",
-       "youhavenewmessagesmulti": "$1 မှာ မက်ဆေ့အသစ်များ ရှိသည်",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|သင့်ထံတွင်}} {{PLURAL:$3|အခြားအသုံးပြုသူ|အသုံးပြုသူများ $3 ဦး}} ထံမှ $1 ရှိသည် ($2)။",
+       "youhavenewmessagesmanyusers": "သင့်ထံတွင် အသုံးပြုသူများထံမှ $1 ရှိသည် ($2)။",
+       "newmessageslinkplural": "{{PLURAL:$1|စာလွှာအသစ် တစ်စောင်|999=စာလွှာ အသစ်များ}}",
+       "newmessagesdifflinkplural": "နောက်ဆုံး {{PLURAL:$1|ပြောင်းလဲမှု|999=ပြောင်းလဲမှုများ}}",
+       "youhavenewmessagesmulti": "$1 မှာ စာတိုအသစ်များ ရှိသည်",
        "editsection": "ပြင်​ဆင်​ရန်​",
        "editold": "ပြင်​ဆင်​ရန်​",
        "viewsourceold": "ရင်းမြစ်ကို ကြည့်ရန်",
        "hidetoc": "ဝှက်",
        "collapsible-collapse": "ချုံ့ရန်",
        "collapsible-expand": "ချဲ့ရန်",
+       "confirmable-confirm": "{{GENDER:$1|သင်}} သေချာပြီလား?",
+       "confirmable-yes": "လုပ်မည်",
+       "confirmable-no": "မလုပ်ပါ",
        "thisisdeleted": "$1 ကို ကြည့်မည်လော (သို့) restore ပြန်သိမ်းမည်လော။",
        "viewdeleted": "$1 ကို ကြည့်မည်လော။",
        "restorelink": "{{PLURAL:$1|ဖျက်လိုက်သည့်တည်းဖြတ်မှုတစ်ခု|ဖျက်လိုက်သည့် တည်းဖြတ်မှု $1 ခု}}",
        "nstab-template": "တမ်းပလိတ်",
        "nstab-help": "အကူအညီ စာမျက်နှာ",
        "nstab-category": "ကဏ္ဍ",
+       "mainpage-nstab": "ဗဟိုစာမျက်နှာ",
        "nosuchaction": "ဤကဲ့သို့ ဆောင်ရွက်ချက်မျိုး မရှိပါ။",
        "nosuchspecialpage": "ဤကဲ့သို့သော အထူးစာမျက်နှာ မရှိပါ",
        "error": "အမှား",
        "databaseerror": "ဒေတာဘေ့စ် အမှား",
+       "databaseerror-function": "လုပ်ဆောင်ချက် - $1",
+       "databaseerror-error": "အမှား - $1",
        "readonly": "ဒေတာဘေ့စ် ပိတ်ထားသည်။",
        "missing-article": "စာမျက်နှာ \"$1\" မှ $2 ကို ရှာတွေ့သင့်သည်ဖြစ်သော်လည်း ဒေတာဘေ့(စ်) သည် ရှာမတွေ့ပါ။\n\nယင်းသည် ဖျက်ထားပြီးသား diff သို့မဟုတ် မှတ်တမ်းလင့် တစ်ခုကြောင့် ဖြစ်လေ့ရှိသည်။\n\nယင်းသို့မဟုတ်ပါက သင်သည် ဤဆော့ဝဲအတွင်းမှ အမှားတစ်ခုကို တွေ့နေခြင်းဖြစ်ကောင်းဖြစ်မည်။ ဤသည်ကို [[Special:ListUsers/sysop|administrator]] သို့ ကျေးဇူးပြု၍ သတင်းပို့ပေးပါ။ URL လင့်ကိုပါ ထည့်သွင်းဖော်ပြပေးပါရန်။",
        "missingarticle-rev": "(တည်းဖြတ်မူ#: $1)",
        "directorycreateerror": "လမ်းညွှန် \"$1\" ကို ဖန်တီးမရနိုင်ပါ။",
        "filenotfound": "ဖိုင် \"$1\" ကို ရှာမတွေ့ပါ။",
        "formerror": "အမှား - ဖောင်သွင်းနိုင်ခြင်းမရှိပါ",
+       "cannotdelete-title": "\"$1\" စာမျက်နှာကို ဖျက်၍ မရပါ",
        "badtitle": "ညံ့ဖျင်းသော ခေါင်းစဉ်",
        "badtitletext": "တောင်းဆိုထားသော စာမျက်နှာ ခေါင်းစဉ်သည် တရားမဝင်ပါ (သို့) ဗလာဖြစ်နေသည် (သို့) အခြားဘာသာများ(inter-language or inter-wiki title)သို့ မှားယွင်းစွာ လင့်ချိတ်ထားသည်။",
        "viewsource": "ရင်းမြစ်ကို ကြည့်ရန်",
-       "protectedpagetext": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\99á\80\9bá\80\94á\80­á\80¯á\80\84á\80ºá\80\9bá\80\94á\80º á\80\91á\80­á\80\94á\80ºá\80¸á\80\9eá\80­á\80\99á\80ºá\80¸ထားသည်။",
+       "protectedpagetext": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80¡á\80\81á\80¼á\80¬á\80¸á\80\9cá\80¯á\80\95á\80ºá\80\86á\80±á\80¬á\80\84á\80ºá\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸ á\80\99á\80\9cá\80¯á\80\95á\80ºá\80\86á\80±á\80¬á\80\84á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80¡á\80±á\80¬á\80\84á\80º á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºထားသည်။",
        "namespaceprotected": "'''$1''' စာညွှန်းဖြင့် စာမျက်နှာကို တည်းဖြတ်ရန် ခွင့်ပြုချက် မရှိပါ။",
        "ns-specialprotected": "အထူးစာမျက်နှာများကို တည်းဖြတ်မရနိုင်ပါ။",
        "virus-unknownscanner": "အမည်မသိအန်တီဗိုင်းရပ်စ် -",
-       "logouttext": "သင်သည် လော့ဂ်အောက် လုပ်လိုက်ပြီဖြစ်သည်။\nသင့်အနေနှင့် ဤ {{SITENAME}} ဝက်ဘ်ဆိုက်ဒ်ကို အမည်မသိ အသုံးပြုသူ အနေနှင့် ဆက်လက် အသုံးပြုနိုင်သည်။ သို့မဟုတ် ယခင် အသုံးပြုသူ အမည် သို့ အသုံးပြုသူ အခြားအမည်တစ်ခုဖြင့် <span class='plainlinks'>[$1 နောက်တစ်ကြိမ် လော့ဂ်အင်ပြန်ဝင်]</span> နိုင်သည်။\nသင်၏ ဘရောက်ဆာမှ cache ကို ရှင်းလင်းသည့် အချိန် အထိ အချို့သော စာမျက်နှာ များသည် သင် လော့ဂ်အင် ဝင်ထားစဉ်က အတိုင်းပင် ဆက်လက် ပြသနေမည်ဖြစ်သည်။",
+       "logouttext": "<strong>သင်သည် လော့ဂ်အောက် လုပ်လိုက်ပြီဖြစ်သည်။</strong>",
+       "welcomeuser": "ကြိုဆိုပါတယ် $1!",
        "yourname": "အသုံးပြုသူအမည် -",
+       "userlogin-yourname": "အသုံးပြုသူအမည်",
+       "userlogin-yourname-ph": "သင်၏ အသုံးပြုသူအမည် ရိုက်ထည့်ပါ",
+       "createacct-another-username-ph": "အသုံးပြုသူအမည် ရိုက်ထည့်ပါ",
        "yourpassword": "စကားဝှက် -",
+       "userlogin-yourpassword": "စကားဝှက်",
+       "userlogin-yourpassword-ph": "သင်၏ စကားဝှက်ကို ရိုက်ထည့်ပါ",
+       "createacct-yourpassword-ph": "စကားဝှက် ရိုက်ထည့်ပါ",
        "yourpasswordagain": "စကားဝှက် ပြန်​ရိုက်​ပါ -",
-       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွနု်ပ်ကို​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
+       "createacct-yourpasswordagain": "စကားဝှက်ကို အတည်ပြုပါ",
+       "createacct-yourpasswordagain-ph": "စကားဝှက်ကို ထပ်မံ ရိုက်ထည့်ပါ",
+       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွန်ုပ်ကို ​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
+       "userlogin-remembermypassword": "Log in ဝင်ထားမည်",
+       "userlogin-signwithsecure": "လုံခြုံသော ဆက်သွယ်မှုကို သုံးမည်",
        "yourdomainname": "သင့်ဒိုမိန်း -",
+       "password-change-forbidden": "ဤဝီကီတွင် စကားဝှက်များကို ပြောင်းလဲ၍ မရပါ။",
        "login": "Log in ဝင်ရန်",
-       "nav-login-createaccount": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\9cá\80¯á\80\95á\80ºရန်",
-       "userlogin": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\9cá\80¯á\80\95á\80ºရန်",
+       "nav-login-createaccount": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
+       "userlogin": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
        "userloginnocreate": "Log in ဝင်ရန်",
        "logout": "ထွက်ရန်",
        "userlogout": "ထွက်ရန်",
-       "notloggedin": "logged in ဝင်မထားပါ",
+       "notloggedin": "log in ဝင်မထားပါ",
+       "userlogin-noaccount": "အကောင့် မရှိဘူးလား?",
+       "userlogin-joinproject": "{{SITENAME}} ကို ချိတ်ဆက်ရန်",
        "nologin": "အကောင့်မရှိဘဲ ဖြစ်နေပါသလား။ $1။",
-       "nologinlink": "á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\9cá\80¯á\80\95á\80ºရန်",
-       "createaccount": "အကောင့်လုပ်ရန်",
+       "nologinlink": "á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯ á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
+       "createaccount": "အကောင့် ဖန်တီးရန်",
        "gotaccount": "အကောင့်ရှိပြီးသားလား။ $1။",
        "gotaccountlink": "Log in ဝင်ရန်",
        "userlogin-resetlink": "Login ဝင်သည့် အသေးစိတ်တို့ကို မေ့သွားပါသလား?",
-       "createaccountmail": "အီးမေးဖြင့်",
+       "userlogin-resetpassword-link": "စကားဝှက် မေ့နေသလား?",
+       "userlogin-helplink2": "log in အကူအညီ",
+       "userlogin-loggedin": "သင်သည် {{GENDER:$1|$1}} အနေဖြင့် လော့အင်ဝင်ထားပြီး ဖြစ်သည်။ အခြားအသုံးပြုသူ အနေဖြင့် ဝင်ရောက်ရန် အောက်ပါပုံစံကို အသုံးပြုပါ။",
+       "userlogin-createanother": "အခြားအကောင့် ဖန်တီးရန်",
+       "createacct-emailrequired": "အီးမေး လိပ်စာ",
+       "createacct-emailoptional": "အီးမေး လိပ်စာ (ဖြည့်လိုက)",
+       "createacct-email-ph": "သင့်အီးမေး လိပ်စာ ရိုက်ထည့်ပါ",
+       "createacct-another-email-ph": "အီးမေး လိပ်စာ ရိုက်ထည့်ပါ",
+       "createaccountmail": "ယာယီ ကျပန်းစကားဝှက်ကို သီးသန့် အီးမေးလ်လိပ်စာသို့ ပေးပို့အသုံးပြုရန်",
+       "createacct-realname": "နာမည်ရင်း (ဖြည့်လိုက)",
        "createaccountreason": "အ​ကြောင်း​ပြ​ချက် -",
+       "createacct-reason": "အကြောင်းပြချက်",
+       "createacct-reason-ph": "သင်ဘာကြောင့် အခြားအကောင့် ဖန်တီးချင်တာလဲ",
+       "createacct-submit": "သင့်အကောင့်ကို ဖန်တီးရန်",
+       "createacct-another-submit": "အကောင့် ဖန်တီးရန်",
+       "createacct-benefit-heading": "{{SITENAME}} အား သင်ကဲ့သို့သော လူများဖြင့် ပြုလုပ်ထားသည်။",
+       "createacct-benefit-body1": "{{PLURAL:$1|တည်းဖြတ်မှု|တည်းဖြတ်မှုများ}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|စာမျက်နှာ|စာမျက်နှာများ}}",
+       "createacct-benefit-body3": "မကြာမီက {{PLURAL:$1|ဆောင်ရွက်သူ|ဆောင်ရွက်သူများ}}",
        "badretype": "သင်ထည့်သွင်းလိုက်သော စကားဝှက်များ ကိုက်ညီမှု မရှိပါ။",
        "userexists": "သင်ရွေးသော အသုံးပြုသူအမည်မှာ ရှိပြီးဖြစ်သည်။\nအခြား အမည် ရွေးပါ။",
        "loginerror": "Login ဝင်ခြင်း အမှား",
        "passwordtooshort": "စကားဝှက်တွင် စကားလုံး အနည်းဆုံး {{PLURAL:$1|တစ်လုံး|$1 လုံး}} ရှိရမည်။",
        "password-name-match": "သင့်စကားဝှက်သည် အသုံးပြုသူအမည်နှင့် အတူတူမဖြစ်စေရဘဲ ကွဲပြားရမည်။",
        "password-login-forbidden": "ဤအသုံးပြုသူအမည်နှင့် စကားဝှက်အား အသုံးပြုခြင်းကို တားမြစ်ထားသည်။",
-       "mailmypassword": "á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80¡á\80\9eá\80\85á\80ºá\80\80á\80­á\80¯ á\80¡á\80®á\80¸á\80\99á\80±á\80¸ á\80\95á\80­á\80¯á\80·ရန်",
+       "mailmypassword": "á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80\80á\80­á\80¯ á\80\95á\80¼á\80\94á\80ºá\80\81á\80»á\80­á\80\94á\80ºရန်",
        "passwordremindertitle": "{{SITENAME}} အတွက် ယာယီစကားဝှက်အသစ်",
        "noemail": "အသုံးပြုသူ \"$1\" အတွက် မည်သည့်အီးမေးလိပ်စာမှ မှတ်သားထားခြင်း မရှိပါ။",
        "noemailcreate": "တရာဝင်အီးမေးလိပ်စာ ပေးရန် လိုအပ်သည်",
        "mailerror": "မေးပို့ခြင်း အမှား - $1",
-       "emailauthenticated": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80­á\80\95á\80ºá\80\85á\80¬á\80\90á\80\8aá\80ºá\80\9bá\80¾á\80­á\80\80á\80¼á\80±á\80¬á\80\84á\80ºá\80¸ $2 á\80\94á\80±á\80· $3 á\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\80 á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\9cá\80­á\80¯á\80\80်သည်။",
+       "emailauthenticated": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80­á\80\95á\80ºá\80\85á\80¬á\80\80á\80­á\80¯ $2 á\80\94á\80±á\80· $3 á\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\90á\80½á\80\84á\80º á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\95á\80¼á\80®á\80¸ á\80\96á\80¼á\80\85်သည်။",
        "emailconfirmlink": "အီးမေးကိုအတည်ပြုပါ",
        "accountcreated": "အကောင့်ဖန်တီးပြီးပါပြီ",
-       "accountcreatedtext": "$1 အတွက် အသုံးပြုသူအကောင့်တစ်ခု ဖန်တီးပြီးဖြစ်သည်။",
+       "accountcreatedtext": "[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|ဆွေးနွေး]]) အတွက် အကောင့်ကို ဖန်တီးပြီး ဖြစ်သည်။",
        "createaccount-title": "{{SITENAME}} အတွက် အကောင့်ပြုလုပ်ခြင်း",
-       "login-throttled": "သင်သည် login ဝင်ရန် အကြိမ်မြောက်မြားစွာ အားထုတ်ခဲ့ပြီးဖြစ်သည်။\nကျေးဇူးပြု၍ ထပ်မဝင်ခင် စောင့်ပေးပါ။",
+       "login-throttled": "သင်သည် login ဝင်ရန် အကြိမ်မြောက်မြားစွာ အားထုတ်ခဲ့ပြီးဖြစ်သည်။\nကျေးဇူးပြု၍ ထပ်မဝင်ခင် $1 စောင့်ပေးပါ။",
        "login-abort-generic": "Login ဝင်ခြင်း မအောင်မြင်ပါ - ထွက်သွားပြီ",
        "loginlanguagelabel": "ဘာသာ: $1",
        "pt-login": "အကောင့်ဝင်ရန်",
+       "pt-login-button": "အကောင့်ဝင်ရန်",
        "pt-createaccount": "အကောင့် ဖန်တီးရန်",
+       "pt-userlogout": "ထွက်ရန်",
        "changepassword": "စကားဝှက် ပြောင်းရန်",
-       "resetpass_announce": "á\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80\9aá\80¬á\80\9aá\80® á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80\96á\80¼á\80\84á\80·á\80º á\80\9dá\80\84á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80º á\80¡á\80\9eá\80\85á\80ºá\80¡á\80¬á\80¸ á\80¤á\80\94á\80±á\80\9bá\80¬á\80\90á\80½á\80\84á\80ºá\80\9bá\80­á\80¯á\80\80á\80ºá\80\95á\80« :",
+       "resetpass_announce": "á\80\9cá\80±á\80¬á\80·á\80\82á\80ºá\80¡á\80\84á\80ºá\80\9dá\80\84á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸ á\80\95á\80¼á\80®á\80¸á\80\99á\80¼á\80±á\80¬á\80\80á\80ºá\80\9bá\80\94á\80º á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80¡á\80\9eá\80\85á\80º á\80\95á\80±á\80¸á\80\9bá\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b",
        "resetpass_header": "အကောင့်စကားဝှက်ပြောင်းရန်",
        "oldpassword": "စကားဝှက် အဟောင်း -",
        "newpassword": "စကားဝှက် အသစ် -",
        "retypenew": "စကားဝှက် အသစ်ကို ထပ်ရိုက်ပါ -",
        "resetpass_submit": "စကားဝှက်ကို သတ်မှတ်ပြီးနောက် Log in ဝင်ရန်",
-       "changepassword-success": "သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။ အခု Log in ဝင်နေပါပြီ...",
+       "changepassword-success": "သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။",
        "resetpass_forbidden": "စကားဝှက် ပြောင်းမရနိုင်ပါ",
        "resetpass-no-info": "ဤစာမျက်နှာကို တိုက်ရိုက်အသုံးပြုနိုင်ရန်အတွက် Log in ဝင်ထားရပါမည်။",
        "resetpass-submit-loggedin": "စကားဝှက်ပြောင်းရန်",
        "resetpass-submit-cancel": "မလုပ်တော့ပါ",
        "resetpass-temp-password": "ယာယီစကားဝှက် -",
+       "passwordreset": "စကားဝှက်ကို ပြန်ချိန်ရန်",
        "passwordreset-username": "အသုံးပြုသူအမည် :",
        "passwordreset-email": "အီးမေး လိပ်စာ :",
+       "changeemail": "အီးမေးလိပ်စာ ပြင်ဆင် သို့ ဖယ်ရှားရန်",
        "bold_sample": "စာလုံးမည်း",
        "bold_tip": "စာလုံးမည်း",
        "italic_sample": "စာလုံး အစောင်း",
        "sig_tip": "အချိန်ပါပြသော သင့်လက်မှတ်",
        "hr_tip": "မျဉ်းလဲ (စိစစ်သုံးရန်)",
        "summary": "အ​ကျဉ်း​ချုပ်​ -",
-       "subject": "အကြောင်းအရာ/ခေါင်းကြီးပိုင်း -",
+       "subject": "အကြောင်းအရာ -",
        "minoredit": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "watchthis": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "savearticle": "ဤစာမျက်နှာကို သိမ်းရန်",
        "preview": "နမူနာ",
        "showpreview": "န​မူ​နာ​ပြ​ရန်",
        "showdiff": "ပြင်​ဆင်​ထား​သည်​များ​ကို​ ပြရန်",
-       "anoneditwarning": "'''သတိပေးချက် - ''' သင်သည် logged in ဝင်မထားပါ။\nဤစာမျက်နှာ၏ တည်းဖြတ်မှတ်တမ်းတွင် သင့် IP address ကို မှတ်သားထားမည် ဖြစ်သည်။",
+       "anoneditwarning": "<strong>သတိပေးချက် - </strong> သင်သည် လော့ဂ်အင် ဝင်မထားပါ။ သင်တည်းဖြတ်မှု ပြုလုပ်ပါက သင့်အိုင်ပီလိပ်စာကို မည်သူမဆို တွေ့မြင်နိုင်မည်။ အကယ်၍ သင် <strong>[$1 လော့ဂ်အင်ဝင်]</strong> သို့မဟုတ် <strong>[$2 အကောင့်တစ်ခု ဖန်တီး]</strong>ပါက၊ သင့်တည်းဖြတ်မှုများသည် သင့်အမည်နှင့် တွဲဖက်မှတ်သားမည် ဖြစ်သည်။",
        "anonpreviewwarning": "သင်သည် logged in ဝင်မထားပါ။ သိမ်းဆည်းမည် ဆိုပါက သင်၏IP အား ဤစာမျက်နှာ မှတ်တမ်းတွင် မှတ်သားထားမည်ဖြစ်ပါသည်။",
        "missingcommenttext": "ကျေးဇူးပြု၍ အောက်တွင် မှတ်ချက်တစ်ခုရေးပါ။",
        "summary-preview": "အ​ကျဉ်း​ချုပ်​န​မူ​နာ:",
-       "subject-preview": "အကြောင်းအရာ/ခေါင်းကြီးပိုင်း နမူနာ -",
+       "subject-preview": "အကြောင်းအရာ နမူနာ -",
        "blockedtitle": "အသုံးပြုသူကို ပိတ်ပင်ထားသည်",
        "blockednoreason": "အကြောင်းပြချက် မပေးထားပါ",
        "whitelistedittext": "စာမျက်နှာများကို တည်းဖြတ်ရန် $1ရမည်။",
        "newarticle": "(အသစ်)",
        "newarticletext": "သင်သည် မရှိသေးသော စာမျက်နှာလင့် ကို ရောက်လာခြင်းဖြစ်သည်။\nစာမျက်နှာအသစ်စတင်ရန် အောက်မှ သေတ္တာထဲတွင် စတင်ရိုက်ထည့်ပါ (နောက်ထပ် သတင်းအချက်အလက်များအတွက်[$1 အကူအညီ စာမျက်နှာ]ကို ကြည့်ပါ)။\nမတော်တဆရောက်လာခြင်း ဖြစ်ပါက ဘရောက်ဆာ၏ နောက်ပြန်ပြန်သွားသော'''back''' ခလုတ်ကို နှိပ်ပါ။",
        "noarticletext": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]၊ သို့မဟုတ် [{{fullurl:{{FULLPAGENAME}}|action=edit}} ဤစာမျက်နှာကို တည်းဖြတ်နိုင်သည်]</span>။",
-       "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။",
+       "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။ သို့သော် ဤစာမျက်နှာကို ဖန်တီးရန် သင့်တွင် အခွင့်အရေး မရှိပါ။",
        "note": "'''မှတ်ချက် -'''",
-       "previewnote": "'''ဤသည်မှာ နမူနာ ကြည့်နေခြင်းသာဖြစ်ကြောင်း မမေ့ပါနှင့်။'''\nသင်ပြောင်းလဲထားသည်များကို မသိမ်းရသေးပါ။",
+       "previewnote": "<strong>ဤသည်မှာ နမူနာ ကြည့်နေခြင်းသာဖြစ်ကြောင်း မမေ့ပါနှင့်။</strong>\nသင်ပြောင်းလဲထားသည်များကို မသိမ်းရသေးပါ။",
        "editing": "$1 ကို တည်းဖြတ်နေသည်",
        "editingsection": "$1 (အပိုင်း) ကို ပြင်ဆင်နေသည်။",
        "editingcomment": "$1 (အပိုင်းသစ်) ကို ပြင်ဆင်နေသည်။",
        "yourtext": "သင့်စာသား",
        "storedversion": "သိမ်းဆည်းထားသောမူ",
        "yourdiff": "ကွဲပြားချက်များ",
-       "copyrightwarning": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးကို $2 အောက်တွင် ဖြန့်ဝေရန် ဆုံးဖြတ်ပြီး ဖြစ်သည်ကို ကျေးဇူးပြု၍ သတိပြုပါ။။ (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ။)\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည်။\n'''မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။'''",
-       "copyrightwarning2": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးသည် အခြားပုံပိုးသူများ၏ တည်းဖြတ်၊ ပြောင်းလဲ၊ ဖယ်ရှားခံရနိုင်သည်ကို သတိပြုပါ။\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည် (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ)။\n'''မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။'''",
+       "copyrightwarning": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးကို $2 အောက်တွင် ဖြန့်ဝေရန် ဆုံးဖြတ်ပြီး ဖြစ်သည်ကို ကျေးဇူးပြု၍ သတိပြုပါ။။ (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ။)\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည်။\n<strong>မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။</strong>",
+       "copyrightwarning2": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးသည် အခြားပုံပိုးသူများ၏ တည်းဖြတ်၊ ပြောင်းလဲ၊ ဖယ်ရှားခံရနိုင်သည်ကို သတိပြုပါ။\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည် (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ)။\n<strong>မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။</strong>",
        "templatesused": "{{PLURAL:$1|တမ်းပလိတ်|တမ်းပလိတ်}} ခုကို ဤစာမျက်နှာကို သုံးထားသည် -",
        "templatesusedpreview": "{{PLURAL:$1|တမ်းပလိတ်|တမ်းပလိတ်}} ခုကို ဤနမူနာတွင် သုံးထားသည် -",
        "template-protected": "(ကာကွယ်ထားသည်)",
        "hiddencategories": "ဤစာမျက်နှာသည် {{PLURAL:$1|ဝှက်ထားသော ကဏ္ဍတစ်ခု|ဝှက်ထားသော ကဏ္ဍ $1 ခု}} ၏ အဖွဲ့ဝင် ဖြစ်သည်။",
        "nocreate-loggedin": "သင်သည် စာမျက်နှာအသစ် ဖန်တီးခွင့်မရှိပါ။",
        "sectioneditnotsupported-title": "ခေါင်းစဉ်ခွဲအလိုက် တည်းဖြတ်ခြင်းကို မထောက်ပံ့ထားပါ",
-       "permissionserrors": "ခွင့်ပြုချက်အမှားများ",
+       "permissionserrors": "ခွင့်ပြုချက်အမှား",
        "permissionserrorstext": "အောက်ပါ {{PLURAL:$1|အကြောင်းပြချက်|အကြောင်းပြချက်များ}}ကြောင့် ထိုအရာအတွက် ခွင့်ပြုချက်မရှိပါ -",
        "permissionserrorstext-withaction": "အောက်ပါ အကြောင်းပြချက် {{PLURAL:$1|ခု|ခု}} ကြောင့် $2 အတွက် ခွင့်ပြုချက်မရှိပါ -",
        "recreate-moveddeleted-warn": "'''သတိပေးချက်။ သင်သည် ယခင်က ဖျက်ထားသော စာမျက်နှာတစ်ခုကို ပြန်လည်ဖန်တီးနေသည်။'''\n\nသင့်အနေနှင့် ဤစာမျက်နှာကို ဆက်လက်တည်းဖြတ်ရန် သင့်တော်မည် မသင့်တော်မည်ကို စဉ်းစားသင့်သည်။\nဖျက်ထားခြင်း နှင့် ရွှေ့ထားခြင်းတို့၏ မှတ်တမ်းကို သင့်အတွက် အလွယ်တကူ ကိုးကားနိုင်ရန် ဖော်ပြထားသည်။",
        "post-expand-template-argument-warning": "'''သတိပေးချက် -''' ဤစာမျက်နှာတွင် ပမာဏအားဖြင့် ကြီးမားကျယ်ပြန့်သော template argument တစ်ခုပါဝင်သည်။\nယင်း arguments များကို ဖယ်ထုတ်လိုက်သည်။",
        "post-expand-template-argument-category": "ဖယ်ထုတ်ထားသော template arguments များပါဝင်သည့် စာမျက်နှာများ",
        "parser-template-loop-warning": "တမ်းပလိတ်များ လှည့်ပတ်ဆက်စပ် နေသည်ကို တွေ့ရသည်။ [[$1]]",
+       "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ဆွေးနွေး]]) ၏ $1 အား နောက်ပြန်ပြင်လိုက်သည်",
        "viewpagelogs": "ဤစာမျက်နှာအတွက် မှတ်တမ်းများကို ကြည့်ရန်",
        "nohistory": "ဤစာမျက်နှာတွင် တည်းဖြတ်မှု ရာဇဝင်မရှိပါ",
        "currentrev": "နောက်ဆုံးမူ",
        "currentrev-asof": "$1 က နောက်ဆုံး တည်းဖြတ်မူ",
        "revisionasof": "$1 ရက်နေ့က မူ",
-       "revision-info": "$1 နေ့က $2 တည်းဖြတ်သည့်မူ",
+       "revision-info": "$1 နေ့က {{GENDER:$6|$2}}$7 တည်းဖြတ်သည့်မူ",
        "previousrevision": "မူဟောင်း",
        "nextrevision": "ပိုသစ်သော တည်းဖြတ်မူ →",
        "currentrevisionlink": "နောက်ဆုံး မူ",
        "last": "ယခုမတိုင်မီ",
        "page_first": "ပထမဆုံး",
        "page_last": "အနောက်ဆုံး",
-       "histlegend": "တည်းဖြတ်မူများကို နှိုင်းယှဉ်ရန် radio boxes လေးများကို မှတ်သားပြီးနောက် Enter ရိုက်ချပါ သို့ အောက်ခြေမှ ခလုတ်ကို နှိပ်ပါ။<br />\nLegend: '''({{int:cur}})''' = နောက်ဆုံးမူနှင့် ကွဲပြားချက် '''({{int:last}})''' = ယင်းရှေ့မူနှင့် ကွဲပြားချက်, '''{{int:minoreditletter}}''' = အရေးမကြီးသော ပြုပြင်မှု.",
+       "histlegend": "တည်းဖြတ်မူများကို နှိုင်းယှဉ်ရန် radio boxes လေးများကို မှတ်သားပြီးနောက် Enter ရိုက်ချပါ သို့ အောက်ခြေမှ ခလုတ်ကို နှိပ်ပါ။<br />\nLegend: <strong>({{int:cur}})</strong> = နောက်ဆုံးမူနှင့် ကွဲပြားချက် <strong>({{int:last}})</strong> = ယင်းရှေ့မူနှင့် ကွဲပြားချက်, <strong>{{int:minoreditletter}}</strong> = အရေးမကြီးသော ပြုပြင်မှု.",
        "history-fieldset-title": "ရာဇဝင်ရှာကြည့်ရန်",
        "history-show-deleted": "ဖျက်ထားသည်များသာ",
-       "histfirst": "​အစောဆုံး",
-       "histlast": "á\80\94á\80±á\80¬á\80\80်ဆုံး",
+       "histfirst": "အဟောင်းဆုံး",
+       "histlast": "á\80¡á\80\9eá\80\85်ဆုံး",
        "historyempty": "(ဘာမှမရှိ)",
        "history-feed-title": "မူရာဇဝင်မှတ်တမ်း",
        "history-feed-item-nocomment": "$2 က $1",
        "rev-deleted-comment": "(တည်းဖြတ်မှုအတိုချုပ် ဖယ်ရှားပြီး)",
-       "rev-deleted-user": "(အသုံပြုသူအမည် ဖယ်ရှားပြီး)",
-       "rev-delundel": "á\80\95á\80¼á\80\9bá\80\94á\80º/á\80\9dá\80¾á\80\80á\80ºရန်",
+       "rev-deleted-user": "(á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\99á\80\8aá\80º á\80\96á\80\9aá\80ºá\80\9bá\80¾á\80¬á\80¸á\80\95á\80¼á\80®á\80¸)",
+       "rev-delundel": "á\80¡á\80\99á\80¼á\80\84á\80ºá\80\95á\80¯á\80¶á\80\85á\80¶ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "rev-showdeleted": "ပြ",
        "revisiondelete": "မူများကို ဖျက်ရန်/မဖျက်တော့ရန်",
        "revdelete-nooldid-title": "တရားမဝင်သော မူအမည်",
        "revdelete-no-file": "ဖော်ပြထားသောဖိုင် မရှိပါ။",
        "revdelete-show-file-submit": "မှန်",
        "revdelete-legend": "မြင်နိုင်စွမ်းရှိမှုတို့အား ကန့်သတ်ခြင်းကို သတ်မှတ်ရန်",
-       "revdelete-hide-text": "တည်းဖြတ်မူမှ စာသားများကို ဝှက်ရန်",
+       "revdelete-hide-text": "တည်းဖြတ်မူမှ စာသား",
        "revdelete-hide-image": "ဖိုင်ပါ အေကြာင်းအရာများကို ဝှက်ရန်",
-       "revdelete-hide-comment": "á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\99á\80¾á\80¯á\80¡á\80\80á\80»á\80\89á\80ºá\80¸á\80\81á\80»á\80¯á\80\95á\80ºá\80\80á\80­á\80¯ á\80\9dá\80¾á\80\80်ရန်",
-       "revdelete-hide-user": "တည်းဖြတ်သူ၏ အသုံးပြုသူအမည်/IP address တို့ကို ဝှက်ရန်",
+       "revdelete-hide-comment": "á\80¡á\80\80á\80»á\80\89á\80ºá\80¸á\80\81á\80»á\80¯á\80\95á\80ºá\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90်ရန်",
+       "revdelete-hide-user": "တည်းဖြတ်သူ၏ အသုံးပြုသူအမည်/အိုင်ပီလိပ်စာ",
        "revdelete-radio-same": "(မပြောင်းလဲ)",
-       "revdelete-radio-set": "á\80\99á\80¾á\80\94်",
-       "revdelete-radio-unset": "á\80\99á\80¾á\80¬á\80¸",
+       "revdelete-radio-set": "á\80\9dá\80¾á\80\80်",
+       "revdelete-radio-unset": "á\80\99á\80¼á\80\84á\80º",
        "revdelete-unsuppress": "ပြန်လည်ထိန်းသိမ်းထားသော မူများမှ ကန့်သတ်ချက်များကို ဖယ်ရှားရန်",
        "revdelete-log": "အ​ကြောင်း​ပြ​ချက်:",
        "revdelete-submit": "ရွေးချယ်ထားသော {{PLURAL:$1|မူ|မူများ}}ကို သက်ရောက်စေရန်",
        "mergehistory-reason": "​ကြောင်း​ပြ​ချက် -",
        "mergelog": "ပေါင်းလိုက်သော မှတ်တမ်း",
        "revertmerge": "ပြန်ခွဲထုတ်ရန်",
-       "history-title": "\"$1\" ၏ တည်းဖြတ်မူ ရာဇဝင်များ",
+       "history-title": "\"$1\"၏ တည်းဖြတ်မှု ရာဇဝင်",
        "difference-multipage": "(စာမျက်နှာများကြားမှ ကွဲပြားချက်များ)",
        "lineno": "စာကြောင်း $1 -",
        "compareselectedversions": "ရွေးချယ်ထားသော မူများကို နှိုင်းယှဉ်ရန်",
        "shown-title": "စာမျက်နှာတစ်ခုလျှင် ရလဒ် $1 {{PLURAL:$1|ခု|ခု}} ပြရန်",
        "viewprevnext": "($1 {{int:မှ}} $2) အထိကြား ရလဒ် ($3) ခုကို ကြည့်ရန်",
        "searchmenu-exists": "'''ဤဝီကီတွင် \"[[:$1]]\" အမည်နှင့် စာမျက်နှာတစ်ခုရှိသည်။'''",
-       "searchmenu-new": "'''ဤဝီကီတွင် \"[[:$1]]\" အမည်နှင့် စာမျက်နှာကို ဖန်တီးပါ။'''",
+       "searchmenu-new": "<strong>ဤဝီကီတွင် \"[[:$1]]\" စာမျက်နှာကို ဖန်တီးပါ!</strong> {{PLURAL:$2|0=|သင့်ရှာဖွေမှုနှင့် စာမျက်နှာကိုလည်း ကြည့်ပါ။|ရှာဖွေမှု ရလဒ်များကိုလည်း ကြည်ါပါ။}}",
        "searchprofile-articles": "မာတိကာစာမျက်နှာများ",
        "searchprofile-images": "မာလတီမီဒီယာ",
        "searchprofile-everything": "အားလုံး",
        "search-section": "(အပိုင်း $1)",
        "search-suggest": "$1 ဟု ဆိုလိုပါသလား။",
        "search-interwiki-caption": "ညီအစ်မ ပရောဂျက်များ",
-       "search-interwiki-default": "ရလဒ် $1 ခု -",
+       "search-interwiki-default": "$1 မှ ရလဒ်များ -",
        "search-interwiki-more": "(နောက်ထပ်)",
        "search-relatedarticle": "ဆက်နွယ်သော",
        "searchrelated": "ဆက်နွယ်သော",
        "prefs-rc": "လတ်​တ​လောအ​ပြောင်း​အ​လဲ​",
        "prefs-watchlist": "စောင့်ကြည့်စာရင်း",
        "prefs-watchlist-days": "စောင့်ကြည့်စာရင်းတွင် ပြရန်နေ့များ",
-       "prefs-watchlist-days-max": "Maximum $1 {{PLURAL:$1|day|days}}",
+       "prefs-watchlist-days-max": "အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}",
        "prefs-watchlist-edits": "ချဲ့ထားသော စောင့်ကြည့်စာရင်းတွင် ပြရန် အပြောင်းအလဲတို့၏ အများဆုံး အရေအတွက်",
        "prefs-watchlist-edits-max": "အများဆုံးအရေအတွက် - ၁ဝဝဝ",
        "prefs-watchlist-token": "စောင့်ကြည့်စာရင်း တိုကင် -",
        "prefs-misc": "အသေးအမွှား",
        "prefs-resetpass": "စကားဝှက် ပြောင်းရန်",
+       "prefs-changeemail": "အီးမေးလိပ်စာ ပြင်ဆင် သို့ ဖယ်ရှားရန်",
        "prefs-email": "အီးမေးအတွက် ရွေးချယ်စရာ",
        "prefs-rendering": "ပုံပန်းသွင်ပြင်",
        "saveprefs": "သိမ်းရန်",
-       "restoreprefs": "á\80\99á\80°á\80\9cá\80\86á\80\80á\80ºá\80\90á\80\84á\80ºá\80\99á\80»á\80¬á\80¸á\80\9eá\80­á\80¯á\80· á\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸ á\80\95á\80¼á\80\94á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º",
+       "restoreprefs": "á\80\99á\80°á\80\9cá\80¡á\80\95á\80¼á\80\84á\80ºá\80¡á\80\86á\80\84á\80ºá\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸á\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º (á\80¡á\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸á\80\90á\80½á\80\84á\80º)",
        "prefs-editing": "တည်းဖြတ်ခြင်း",
        "rows": "အလျားလိုက်တန်း -",
        "columns": "ဒေါင်လိုက်တန်း -",
        "recentchangescount": "ပုံသေအားဖြင့် ပြရန် တည်းဖြတ်မှုအရေအတွက် -",
        "prefs-help-recentchangescount": "ဤသည်တွင်ပါဝင်သည်မှာ လတ်တလောအပြောင်းအလဲများ၊ စာမျက်နှာမှတ်တမ်းနှင့် မှတ်တမ်းများဖြစ်သည်။",
        "savedprefs": "သင့်ရွေးချယ်မှုတို့ကို သိမ်းပြီးပါပြီ။",
+       "savedrights": "{{GENDER:$1|$1}}၏ အသုံးပြု အခွင့်အရေးများကို သိမ်းပြီးပါပြီ။",
        "timezonelegend": "အချိန်ဇုန် -",
        "localtime": "ပြည်တွင်းအချိန် -",
-       "timezoneuseserverdefault": "á\80\86á\80¬á\80\97á\80¬á\80\95á\80¯á\80¶á\80\99á\80¾á\80\94á\80ºá\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\80á\80­á\80¯ á\80\9eá\80¯á\80¶á\80¸á\80\9bá\80\94á\80º",
+       "timezoneuseserverdefault": "á\80\9dá\80®á\80\80á\80®á\80¡á\80\95á\80¼á\80\84á\80ºá\80¡á\80\86á\80\84á\80ºá\80\80á\80­á\80¯ á\80\9eá\80¯á\80¶á\80¸á\80\9bá\80\94á\80º ($1)",
        "timezoneuseoffset": "အခြား (တန်ဖိုးသတ်မှတ်ပေးရန်)",
        "servertime": "ဆာဗာအချိန် -",
        "guesstimezone": "ဘရောက်ဇာမှ ဖြည့်ရန်",
        "timezoneregion-indian": "အိန္ဒိယသမုဒ္ဒရာ",
        "timezoneregion-pacific": "ပစိဖိတ်သမုဒ္ဒရာ",
        "allowemail": "အခြားအသုံးပြုသူများထံမှ အီးမေးများကို လက်ခံရန်",
-       "prefs-searchoptions": "ရှာဖွေရန် ရွေးချယ်မှု",
+       "prefs-searchoptions": "ရှာ​ဖွေ​ရန်​",
        "prefs-namespaces": "အမည်ညွှန်း",
        "default": "ပုံမှန်အားဖြင့်",
        "prefs-files": "ဖိုင်",
        "prefs-custom-js": "စိတ်ကြိုက် Javascript",
        "prefs-emailconfirm-label": "အီးမေးအတည်ပြုရန်",
        "youremail": "အီး​မေး -",
-       "username": "အသုံးပြုသူအမည် -",
-       "prefs-memberingroups": "{{PLURAL:$1|အုပ်စု|အုပ်စု}}၏ အဖွဲ့ဝင်",
+       "username": "{{GENDER:$1|အသုံးပြုသူအမည်}} -",
+       "prefs-memberingroups": "{{PLURAL:$1|အုပ်စု|အုပ်စုများ}}၏ {{GENDER:$2|အဖွဲ့ဝင်}}",
        "prefs-registration": "မှတ်ပုံတင်သည့် အချိန် -",
        "yourrealname": "နာမည်ရင်း -",
        "yourlanguage": "ဘာသာစကား -",
        "yournick": "လက်မှတ်အသစ် -",
        "badsig": "တရားမဝင်သည့် လက်မှတ်အကြမ်း။\nHTML tags ကို စစ်ဆေးပါ။",
        "badsiglength": "သင့်လက်မှတ်သည် ရှည်လွန်းနေပါသည်။\nယင်းသည် စာလုံး {{PLURAL:$1|လုံး|လုံး}}ထက် မရှည်ရပါ။",
-       "yourgender": "á\80\80á\80»á\80¬á\80¸/á\80\99 -",
-       "gender-unknown": "á\80\96á\80±á\80¬á\80ºá\80\95á\80¼á\80\99á\80\91á\80¬á\80¸",
-       "gender-male": "á\80\80á\80»á\80¬á\80¸",
-       "gender-female": "á\80\99",
+       "yourgender": "á\80\9eá\80\84á\80ºá\80\98á\80\9aá\80ºá\80\9cá\80­á\80¯ á\80\96á\80±á\80¬á\80ºá\80\95á\80¼á\80\85á\80±á\80\81á\80»á\80\84á\80ºá\80\95á\80«á\80\9eá\80\9cá\80²?",
+       "gender-unknown": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80¬á\80¸á\80\9bá\80\8aá\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\9bá\80¬á\80\90á\80½á\80\84á\80º á\80\86á\80±á\80¬á\80·á\80\96á\80ºá\80\9dá\80²á\80\9cá\80ºá\80\9eá\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\95á\80«á\80\80 á\80\98á\80\80á\80ºá\80\99á\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80\8aá\80·á\80º á\80\9cá\80­á\80\84á\80ºá\80¡á\80\9eá\80¯á\80¶á\80¸á\80¡á\80\94á\80¾á\80¯á\80\94á\80ºá\80¸á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9cá\80­á\80\99á\80·á\80ºá\80\99á\80\8aá\80º",
+       "gender-male": "á\80\9eá\80°á\80\9eá\80\8aá\80º á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9eá\80\8aá\80º",
+       "gender-female": "á\80\9eá\80°á\80\99á\80\9eá\80\8aá\80º á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9eá\80\8aá\80º",
        "email": "အီးမေး",
        "prefs-help-email": "အီးမေးလ်လိပ်စာ ပေးမည် မပေးမည်မှာ သင့်သဘောသာ ဖြစ်ပါသည်။ သို့သော်လည်း သင် စကားဝှက်ကို မေ့သွားပါက စကားဝှက်ကို reset လုပ်ရန် အီးမေးလ်လိပ်စာ လိုအပ်ပါလိမ့်မည်။",
        "prefs-help-email-others": "You can also choose to let others contact you by e-mail through a link on your user or talk page.\nသင့်ယူဆာစာမျက်နှာ သို့မဟုတ် ဆွေးနွေးရန်စာမျက်နှာရှိ လင့်မှတဆင့် သင့်ထံ အခြားသူများ အီးမေးမှဆက်သွယ်ရန်လည်း ရွေးချယ်နိုင်သည်။\nYour e-mail address is not revealed when other users contact you.\nအခြားသူများ သင့်ထံဆက်သွယ်သည့်အခါ သင့်အီးမေးကို သူတို့ကို ဖော်ပြမည်မဟုတ်ပါ။",
        "prefs-signature": "လက်မှတ်",
        "prefs-dateformat": "နေ့စွဲပုံစံ",
        "prefs-timeoffset": "အချိန် တန်ဖိုး",
-       "prefs-advancedediting": "á\80¡á\80\86á\80\84á\80·á\80ºá\80\99á\80¼á\80\84á\80·á\80º á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\99á\80¾á\80¯",
+       "prefs-advancedediting": "á\80¡á\80\91á\80½á\80±á\80\91á\80½á\80± á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\85á\80\9bá\80¬á\80\99á\80»á\80¬á\80¸",
        "prefs-advancedrc": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-advancedrendering": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-advancedsearchoptions": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-displayrc": "ပြသရန် ရွေးချယ်မှု",
        "prefs-displaywatchlist": "ပြသရန် ရွေးချယ်မှု",
        "prefs-diffs": "ကွဲပြားချက်",
-       "email-address-validity-valid": "အီးမေးလိပ်စာ တရားဝင်ပုံပေါ်သည်",
-       "email-address-validity-invalid": "တရားဝင်အီးမေးလိပ်စာတစ်ခု ထည့်ပါ",
        "userrights": "အသုံးပြုသူ၏ အခွင့်အရေးများကို စီမံခန့်ခွဲခြင်း",
        "userrights-lookup-user": "အသုံးပြုသူအုပ်စုကို စီမံရန်",
        "userrights-user-editname": "အသုံးပြုသူအမည်တစ်ခုကို ထည့်ပါ -",
        "editusergroup": "အသုံးပြုသူအုပ်စုကို တည်းဖတြ်ရန်",
+       "editinguser": "{{GENDER:$1|အသုံးပြုသူ}} <strong>[[User:$1|$1]]</strong> $2 ၏ အသုံးပြုအခွင့်အရေးများကို ပြောင်းလဲခြင်း",
        "userrights-editusergroup": "အသုံးပြုသူအုပ်စုကို တည်းဖတြ်ရန်",
        "saveusergroups": "အသုံးပြုသူအုပ်စုကို သိမ်းရန်",
        "userrights-groupsmember": "အဖွဲ့ဝင်",
        "userrights-reason": "အ​ကြောင်း​ပြ​ချက်:",
-       "userrights-notallowed": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\9eá\80\8aá\80º á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸á\80¡á\80\95á\80ºá\80\94á\80¾á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\81á\80»á\80\80á\80º မရှိပါ။",
+       "userrights-notallowed": "á\80\9eá\80\84á\80·á\80ºá\80\90á\80½á\80\84á\80º á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸ á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\9bá\80\94á\80º  á\80\9eá\80­á\80¯á\80· á\80\96á\80\9aá\80ºá\80\9bá\80¾á\80¬á\80¸á\80\9bá\80\94á\80º á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸ မရှိပါ။",
        "userrights-changeable-col": "သင်ပြောင်းလဲပေးနိုင်သောအုပ်စုများ",
        "userrights-unchangeable-col": "သင်ပြောင်းလဲမပေးနိုင်သောအုပ်စုများ",
        "group": "အုပ်စု -",
        "group-user": "အသုံးပြုသူများ",
-       "group-autoconfirmed": "အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူ",
+       "group-autoconfirmed": "အလိုအလျောက် အတည်ပြုထားသော အသုံးပြုသူများ",
        "group-bot": "ဘော့များ",
        "group-sysop": "အက်ဒမင်များ",
        "group-bureaucrat": "ဗျူရိုကရက်",
        "group-all": "(အားလုံး)",
-       "group-user-member": "အသုံးပြုသူ",
-       "group-autoconfirmed-member": "အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူ",
-       "group-bot-member": "ဘော့",
-       "group-sysop-member": "အက်ဒမင်",
-       "group-bureaucrat-member": "ဗျူရိုကရက်",
+       "group-user-member": "{{GENDER:$1|အသုံးပြုသူ}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|အလိုအလျောက် အတည်ပြုထားသော အသုံးပြုသူ}}",
+       "group-bot-member": "{{GENDER:$1|ဘော့}}",
+       "group-sysop-member": "{{GENDER:$1|စီမံခန့်ခွဲသူ}}",
+       "group-bureaucrat-member": "{{GENDER:$1|ဗျူရိုကရက်}}",
        "grouppage-user": "{{ns:project}}:အသုံးပြုသူများ",
        "grouppage-autoconfirmed": "{{ns:project}}:အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူများ",
        "grouppage-bot": "{{ns:project}}:ဘော့များ",
        "right-reupload": "ရှိပြီးသားဖိုင်များကို ထပ်ရေးရန်",
        "right-reupload-own": "သင်ကိုယ်တိုင် Upload တင်ထားခဲ့သည့် ရှိပြီးသားဖိုင်ကို ထပ်ရေးရန်",
        "right-upload_by_url": "URL လင့်တစ်ခုမှ ဖိုင်ကို Upload တင်ရန်",
-       "right-autoconfirmed": "á\80\90á\80\85á\80ºá\80\85á\80­á\80\90á\80ºá\80\90á\80\85á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9bá\80\94á\80º",
+       "right-autoconfirmed": "á\80¡á\80­á\80¯á\80\84á\80ºá\80\95á\80®á\80¡á\80\81á\80¼á\80±á\80\95á\80¼á\80¯ á\80\80á\80\94á\80·á\80ºá\80\9eá\80\90á\80ºá\80\81á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\80á\80»á\80­á\80¯á\80¸á\80\99á\80\9eá\80\80á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\95á\80«",
        "right-bot": "အလိုအလျောက်ပြုမူသော ဖြစ်စဉ်အဖြစ်ဆောင်ရွက်ရန်",
+       "right-writeapi": "ရေးသားမှု API ကို သုံးရန်",
        "right-delete": "စာမျက်နှာများကိုဖျက်ပါ။",
        "right-bigdelete": "လွန်စွာများပြားသော ရာဇဝင်များရှိသည့် စာမျက်နှာများကို ဖျက်ရန်",
        "right-browsearchive": "ဖျက်ပစ်လိုက်သော စာမျက်နှာများကို ရှာရန်",
        "right-block": "အခြားအသုံးပြုသူများ တည်းဖြတ်ခြင်းမှ ပိတ်ပင်ရန်",
        "right-blockemail": "အီးမေးပို့ခြင်းမှ အသုံးပြုသူကို တားဆီးရန်",
        "right-hideuser": "အသုံးပြုသူအမည်ကို ပိတ်ပင်ရန်နှင့် ယင်းအမည်ကို အများမမြင်နိုင်အောင် ဝှက်ထားရန်",
-       "right-unblockself": "á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80ºá\80\80á\80­á\80¯ á\80\9eá\80°á\80\90á\80­á\80¯á\80·á\80\98á\80¬á\80\9eá\80¬ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80·်ရန်",
-       "right-protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\99á\80¾á\80¯á\80¡á\80\86á\80\84á\80·á\80º á\80\9cá\80»á\80¾á\80±á\80¬á\80·á\80\81á\80»á\80\9bá\80\94á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80ºစာမျက်နှာများကို တည်းဖြတ်ရန်",
+       "right-unblockself": "á\80\80á\80­á\80¯á\80\9aá\80·á\80ºá\80\80á\80­á\80¯á\80\80á\80­á\80¯á\80\9aá\80º á\80\95á\80¼á\80\94á\80ºá\80\99á\80\95á\80­á\80\90á\80ºá\80\95á\80\84်ရန်",
+       "right-protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\99á\80¾á\80¯á\80¡á\80\86á\80\84á\80·á\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\9bá\80\94á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\9eá\80½á\80\9aá\80ºá\80\96á\80¼á\80¬-á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80º စာမျက်နှာများကို တည်းဖြတ်ရန်",
        "right-editusercss": "အခြားအသုံးပြုသူများ၏ CSS ဖိုင်ကို တည်းဖြတ်ရန်",
        "right-edituserjs": "အခြားအသုံးပြုသူများ၏ JavaScript ဖိုင်ကို တည်းဖြတ်ရန်",
        "right-import": "အခြားဝီကီများမှ စာမျက်နှာများကို ထည့်သွင်းရန်",
        "action-suppressionlog": "ဤကိုယ်ပိုင်မှတ်တမ်းကို ကြည့်ရန်",
        "action-block": "တည်းဖြတ်ခြင်းမှ ဤအသုံးပြုသူကို ပိတ်ပင်ရန်",
        "action-protect": "ဤစာမျက်နှာအတွက် ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲရန်",
-       "action-import": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80¡á\80\81á\80¼á\80¬á\80¸á\80\9dá\80®á\80\80á\80®á\80\99á\80¾ ထည့်သွင်းရန်",
-       "action-importupload": "Upload á\80\90á\80\84á\80ºá\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\99á\80¾ á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ကို ထည့်သွင်းရန်",
+       "action-import": "á\80¡á\80\81á\80¼á\80¬á\80¸á\80\9dá\80®á\80\80á\80®á\80\99á\80»á\80¬á\80¸á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ ထည့်သွင်းရန်",
+       "action-importupload": "Upload á\80\90á\80\84á\80ºá\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸ကို ထည့်သွင်းရန်",
        "action-autopatrol": "သင့်တည်းဖြတ်မှုကို စောင့်ကြပ်စစ်ဆေးနေသည်ဟု မှတ်သားထားရန်",
        "action-unwatchedpages": "စောင့်မကြည့်တော့သော စာမျက်နှာများ၏ စာရင်းကို ကြည့်ရန်",
        "action-mergehistory": "ဤစာမျက်နှာ၏ရာဇဝင်ကို ပေါင်းရန်",
        "action-userrights": "အသုံးပြုသူ၏အခွင့်အရေးများအားလုံးကို တည်းဖြတ်ရန်",
        "action-userrights-interwiki": "အခြားဝီကီများမှ အသုံးပြုသူများ၏ အသုံးပြုသူအခွင့်အရေးများကို တည်းဖြတ်ရန်",
+       "action-sendemail": "အီးမေးများ ပို့ရန်",
+       "action-editmywatchlist": "သင့် စောင့်ကြည့်စာရင်းကို တည်းဖြတ်ရန်",
+       "action-viewmywatchlist": "သင့် စောင့်ကြည့်စာရင်းကို ကြည့်ရန်",
        "nchanges": "ပြောင်းလဲချက် $1 {{PLURAL:$1|ခု|ခု}}",
+       "enhancedrc-history": "ရာဇဝင်",
        "recentchanges": "လတ်​တ​လောအ​ပြောင်း​အ​လဲ​",
        "recentchanges-legend": "လတ်တလောအပြောင်းအလဲများအတွက် ရွေးချယ်စရာများ",
        "recentchanges-summary": "ဤစာမျက်နှာတွင် ဝီကီ၏ လတ်တလောပြောင်းလဲမှုများကို နောက်ကြောင်းခံလိုက်ရန်",
        "recentchanges-label-minor": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "recentchanges-label-bot": "ဤတည်းဖြတ်မှုကို ဘော့က လုပ်ဆောင်သွားသည်။",
        "recentchanges-label-unpatrolled": "ဤတည်းဖြတ်မှုကို မစောင့်ကြပ်မစစ်ဆေးရသေးပါ",
-       "rcnotefrom": "အောက်ပါတို့သည် '''$2''' ကတည်းက အ​ပြောင်းအလဲများ ြဖစ်သည် ('''$1''' ခု ြပထားသည်)။",
+       "recentchanges-label-plusminus": "စာမျက်နှာ အရွယ်အစားမှာ အောက်ပါ ဘိုက်ပမာဏ ပြောင်းလဲသွားခဲ့သည်",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|စာမျက်နှာသစ်များ စာရင်း]]ကိုလည်း ကြည့်ရန်)",
+       "recentchanges-submit": "ပြ",
+       "rcnotefrom": "အောက်ပါတို့မှာ <strong>$3၊ $4</strong> မှစ၍ {{PLURAL:$5|ပြောင်းလဲမှု|ပြောင်းလဲမှုများ}} ဖြစ်သည်  (<strong>$1</strong> အထိ ပြထား)။",
        "rclistfrom": "$3 $2 မှစသော အပြောင်းအလဲအသစ်များကို ပြရန်",
        "rcshowhideminor": "အရေးမကြီးသော ပြင်ဆင်မှု $1ရန်",
+       "rcshowhideminor-show": "ပြ",
+       "rcshowhideminor-hide": "ဝှက်",
        "rcshowhidebots": "ဘော့ $1ရန်",
-       "rcshowhideliu": "logged-in ဝင်နေသော အသုံးပြုသူ $1ရန်",
+       "rcshowhidebots-show": "ပြ",
+       "rcshowhidebots-hide": "ဝှက်",
+       "rcshowhideliu": "$1 မှတ်ပုံတင်ထားသော အသုံးပြုသူများ",
+       "rcshowhideliu-show": "ပြ",
+       "rcshowhideliu-hide": "ဝှက်",
        "rcshowhideanons": "အမည်မသိ အသုံးပြုသူ $1ရန်",
+       "rcshowhideanons-show": "ပြ",
+       "rcshowhideanons-hide": "ဝှက်",
        "rcshowhidepatr": "စောင့်ြကပ်တည်းဖြတ်မှု $1ရန်",
-       "rcshowhidemine": "ကျွနု်ပ်တည်းဖြတ်ထားသည်များ $1ရန်",
+       "rcshowhidepatr-show": "ပြ",
+       "rcshowhidepatr-hide": "ဝှက်",
+       "rcshowhidemine": "ကျွန်ုပ်တည်းဖြတ်ထားသည်များအား $1ရန်",
+       "rcshowhidemine-show": "ပြ",
+       "rcshowhidemine-hide": "ဝှက်",
+       "rcshowhidecategorization-show": "ပြ",
+       "rcshowhidecategorization-hide": "ဝှက်",
        "rclinks": "$2 ရက်အတွင်းမှ နောက်ဆုံးပြင်ဆင်ချက် $1 ခုကို ပြရန်</br> $3",
        "diff": "ကွဲပြားမှု",
        "hist": "မှတ်တမ်း",
        "newpageletter": "အသစ်",
        "boteditletter": "ဘော့",
        "number_of_watching_users_pageview": "[စောင့်ကြည့်နေသော အသုံးပြုသူ $1 {{PLURAL:$1|ဦး|ဦး}}]",
-       "rc_categories_any": "á\80\99á\80\8aá\80ºá\80\9eá\80\8aá\80ºမဆို",
+       "rc_categories_any": "á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\81á\80¶á\80\9bá\80\9eá\80±á\80¬ á\80\99á\80\8aá\80ºá\80\9eá\80°မဆို",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} ပြောင်းလဲပြီးနောက်",
        "newsectionsummary": "/* $1 */ အပိုင်းသစ်",
-       "rc-enhanced-expand": "အသေးစိတ် ပြရန် (JavaScript လိုအပ်သည်)",
+       "rc-enhanced-expand": "အသေးစိတ် ပြရန်",
        "rc-enhanced-hide": "အသေးစိတ် မပြရန်",
+       "rc-old-title": "\"$1\" အဖြစ် မူလက ဖန်တီးခဲ့သည်",
        "recentchangeslinked": "ဆက်​စပ်သော​ အ​ပြောင်း​အ​လဲ​များ​",
        "recentchangeslinked-feed": "ဆက်စပ်သော ​အ​ပြောင်း​အ​လဲ​များ​",
        "recentchangeslinked-toolbox": "ဆက်​စပ်​သော​အ​ပြောင်း​အ​လဲ​များ​",
        "recentchangeslinked-summary": "ဤသည်မှာ သီးသန့်ပြထားသော စာမျက်နှာ (သို့ သီးသန့်ကဏ္ဍများ) မှ ညွှန်းထားသော စာမျက်နှာများ၏ လတ်တလော ပြောင်းလဲမှုများ၏ စာရင်းဖြစ်သည်။ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ စာမျက်နှာများကို စာလုံးမည်းဖြင့် ပြထားသည်။",
        "recentchangeslinked-page": "စာမျက်နှာ အမည် -",
        "recentchangeslinked-to": "ပေးထားသော စာမျက်နှာများအစား လင့်များနှင့် ဆက်စပ်နေသာ စာမျက်နှာများ၏ အပြောင်းအလဲများကို ပြရန်",
+       "recentchanges-page-added-to-category": "ကဏ္ဍထဲသို့ [[:$1]] ကို ပေါင်းထည့်ခဲ့သည်",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာများ $2 ခု}}ကို ကဏ္ဍထဲသို့ ပေါင်းထည့်ခဲ့သည်",
+       "recentchanges-page-removed-from-category": "ကဏ္ဍထဲမှ [[:$1]] ကို ဖယ်ရှားခဲ့သည်",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာများ $2 ခု}}ကို ကဏ္ဍထဲမှ ဖယ်ရှားခဲ့သည်",
        "upload": "ဖိုင်​တင်​ရန်​",
        "uploadbtn": "ဖိုင်​တင်​ရန်​",
        "reuploaddesc": "Upload တင်နေခြင်းကို ဖျက်သိမ်းပြီး upload တင်သည့် ပုံစံသို့ ပြန်သွားရန်",
        "upload-tryagain": "ပြုပြင်ထားသောဖိုင်၏ ဖော်ပြချက်ကို ထည့်သွင်းရန်",
        "uploadnologin": "logged in ဝင်မထားပါ",
-       "uploadnologintext": "ဖိုင်များကို Upload တင်ရန် [[Special:UserLogin|logged in ဝင်ပြီး]] ဖြစ်ရမည်။",
+       "uploadnologintext": "ဖိုင်များကို တင်ရန် ကျေးဇူးပြု၍ $1 ပါ။",
        "uploaderror": "အပ်လုပ်တင်ခြင်း အမှား",
-       "upload-permitted": "ခွင့်ပြုထားသော ဖိုင်အမျိုးအစား - $1။",
-       "upload-preferred": "á\80¡á\80\9cá\80±á\80¸á\80\95á\80±á\80¸á\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸ - $1။",
-       "upload-prohibited": "တားမြစ်ထားသော ဖိုင်အမျိုးအစား - $1။",
+       "upload-permitted": "ခွင့်ပြုထားသော ဖိုင် {{PLURAL:$2|အမျိုးအစား|အမျိုးအစားများ}}: $1။",
+       "upload-preferred": "á\80¡á\80\9cá\80±á\80¸á\80\95á\80±á\80¸á\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80º {{PLURAL:$2|á\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸|á\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸á\80\99á\80»á\80¬á\80¸}}: $1။",
+       "upload-prohibited": "တားမြစ်ထားသော ဖိုင် {{PLURAL:$2|အမျိုးအစား|အမျိုးအစားများ}}: $1။",
        "uploadlogpage": "Upload တင်သည့် မှတ်တမ်း",
        "filename": "ဖိုင်အမည်",
        "filedesc": "အ​ကျဉ်း​ချုပ်​",
        "license-header": "လိုင်စင်သတ်မှတ်ခြင်း",
        "nolicense": "ဘာမှရွေးချယ်မထားပါ",
        "license-nopreview": "(နမူနာ မရနိုင်ပါ)",
-       "upload_source_url": "(တရားဝင်၍ အများပြည်သူ ရယူသုံးစွဲနိုင်သော URL လင့်တစ်ခု)",
-       "upload_source_file": "(သင့်ကွန်ပျူတာမှ ဖိုင်တစ်ခု)",
+       "upload_source_url": "(á\80\9eá\80\84á\80ºá\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\90á\80\9bá\80¬á\80¸á\80\9dá\80\84á\80ºá\81\8d á\80¡á\80\99á\80»á\80¬á\80¸á\80\95á\80¼á\80\8aá\80ºá\80\9eá\80° á\80\9bá\80\9aá\80°á\80\9eá\80¯á\80¶á\80¸á\80\85á\80½á\80²á\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80±á\80¬ URL á\80\9cá\80\84á\80·á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯)",
+       "upload_source_file": "(á\80\9eá\80\84á\80ºá\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\9eá\80\84á\80·á\80ºá\80\80á\80½á\80\94á\80ºá\80\95á\80»á\80°á\80\90á\80¬á\80\99á\80¾ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯)",
        "listfiles_search_for": "မီဒီယာအမည်ကို ရှာရန် -",
        "imgfile": "ဖိုင်",
        "listfiles": "ဖိုင်စာရင်း",
        "filehist-dimensions": "မှတ်တမ်း ဒိုင်မန်းရှင်းများ",
        "filehist-filesize": "ဖိုင်ဆိုက်",
        "filehist-comment": "မှတ်ချက်",
-       "imagelinks": "á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\86á\80\80á\80ºá\80¡á\80\9eá\80½á\80\9aá\80ºá\80\99á\80»á\80¬á\80¸",
+       "imagelinks": "á\80\96á\80­á\80¯á\80\84á\80ºá\80\9eá\80¯á\80¶á\80¸á\80\85á\80½á\80²á\80\99á\80¾á\80¯",
        "linkstoimage": "ဤဖိုင်သို့ အောက်ပါ {{PLURAL:$1|စာမျက်နှာလင့်|စာမျက်နှာလင့် $1 ခု}} -",
        "nolinkstoimage": "ဤဖိုင်သို့လင့်ထားသော စာမျက်နှာမရှိပါ။",
        "morelinkstoimage": "ဤဖိုင်သို့[[Special:WhatLinksHere/$1|နောက်ထပ်လင့်များ]] ကိုကြည့်ပါ။",
        "filepage-nofile-link": "ဤအမည်ဖြင့် မည်သည့်ဖိုင်မှ မရှိပါ။ သိုရာတွင် ယင်းကို [$1 upload တင်]နိုင်သည်။",
        "uploadnewversion-linktext": "ဤဖိုင်၏ နောက်ဆုံး version ကို upload တင်ရန်",
        "shared-repo-from": "$1 ထံမှ",
+       "upload-disallowed-here": "သင်သည် ဤဖိုင်အား ထပ်၍ ရေးသားမရနိုင်ပါ။",
        "filerevert": "$1 ကို ပြန်ပြောင်းရန်",
        "filerevert-legend": "ဖိုင်ကို ပြန်ပြောင်းရန်",
        "filerevert-comment": "အ​ကြောင်း​ပြ​ချက် -",
-       "filerevert-defaultcomment": "$2 ရက်နေ့ $1 အချိန်မှ မူသို့ ပြန်ပြောင်းရန်",
+       "filerevert-defaultcomment": "$2 ရက်နေ့ $1 ($3) အချိန်မှ မူသို့ ပြန်ပြောင်းရန်",
        "filerevert-submit": "ပြောင်းရန်",
        "filedelete": "$1 ကိုဖျက်ပါ",
        "filedelete-legend": "ဖိုင်ကိုဖျက်ပါ",
        "statistics-users": "မှတ်ပုံတင်ထားသော [[Special:ListUsers|အသုံးပြုသူများ]]",
        "statistics-users-active": "လက်ရှိလုပ်ကိုင်နေသော အသုံးပြုသူများ",
        "doubleredirects": "နှစ်ဆင့်ပြန် ပြန်ညွှန်းများ",
-       "double-redirect-fixed-move": "[[$1]] á\80\80á\80­á\80¯ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9aá\80\81á\80¯á\80¡á\80\81á\80« [[$2]] သို့ ပြန်ညွှန်းထားသည်။",
+       "double-redirect-fixed-move": "[[$1]] á\80\80á\80­á\80¯ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\81\8eá\80\84á\80ºá\80¸á\80¡á\80¬á\80¸ á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\95á\80¼á\80®á\80¸ [[$2]] သို့ ပြန်ညွှန်းထားသည်။",
        "brokenredirects": "ကျိုးပျက်နေသော ပြန်ညွှန်းများ",
        "brokenredirectstext": "အောက်ပါ ပြန်ညွှန်းများသည် မရှိသောစာမျက်နှာများသို့ လင့်ထားသည် -",
        "brokenredirects-edit": "ပြင်​ဆင်​ရန်",
        "listusers-editsonly": "တည်းဖြတ်ထားဖူးသော အသုံးပြုသူများကိုသာ ဖော်ပြရန်",
        "listusers-creationsort": "စတင်ရေးသားသည့်ရက်စွဲအလိုက် စီရန်",
        "usereditcount": "တည်းဖြတ်မှု $1 {{PLURAL:$1|ခု|ခု}}",
-       "usercreated": "$1 ရက် $2 အချိန်တွင် ဖန်တီးခဲ့သည်",
+       "usercreated": "$1 $2 အချိန်တွင် {{GENDER:$3|ဖန်တီးခဲ့သည်}}",
        "newpages": "စာမျက်နှာအသစ်",
        "newpages-username": "မှတ်​ပုံ​တင်​အ​မည်:",
        "ancientpages": "အဟောင်းဆုံးစာမျက်နှာ",
        "pager-older-n": "{{PLURAL:$1|ပိုဟောင်းသော တစ်ခု|ပိုဟောင်းသော $1 ခု}}",
        "booksources": "မှီငြမ်း စာအုပ်များ",
        "booksources-search-legend": "စာအုပ်ရင်းမြစ်များကို ရှာရန်",
-       "specialloguserlabel": "အသုံးပြုသူ -",
-       "speciallogtitlelabel": "ခေါင်းစဉ် -",
+       "booksources-search": "ရှာဖွေရန်",
+       "specialloguserlabel": "ဆောင်ရွက်သူ -",
+       "speciallogtitlelabel": "ရည်ရွယ်ရာ (ခေါင်းစဉ် သို့ {{ns:user}}:အသုံးပြုသူအတွက် အသုံးပြုအမည်):",
        "log": "မှတ်​တမ်း​များ​",
        "all-logs-page": "အများနှင့်ဆိုင်သောမှတ်တမ်းအားလုံး",
        "allpages": "စာမျက်နှာအားလုံး",
        "deletedcontributions": "ဖျက်လိုက်သော ပံ့ပိုးမှုများ",
        "deletedcontributions-title": "ဖျက်လိုက်သော ပံ့ပိုးမှုများ",
        "sp-deletedcontributions-contribs": "ပံ့ပိုးထားမှုများ",
-       "linksearch": "ပြင်ပ လင့်များ",
+       "linksearch": "ပြင်ပလင့်ခ်များ ရှာဖွေ",
        "linksearch-pat": "ရှာသည့်ပုံစံ -",
        "linksearch-ns": "အမည်ညွှန်း -",
        "linksearch-ok": "ရှာ​ဖွေ​ရန်​",
        "listgrouprights-addgroup-self-all": "အုပ်စုအားလုံးကို မိမိ၏အကောင့်သို့ ပေါင်းထည့်ရန်",
        "listgrouprights-removegroup-self-all": "မိမိ၏အကောင့်မှ အုပ်စုအားလုံးကို ဖယ်ရှားရန်",
        "mailnologin": "ပို့ရန်လိပ်စာ မရှိပါ",
-       "emailuser": "ဤ​အ​သုံး​ပြု​သူ​အား​အီး​မေး​ပို့​ပါ​",
-       "emailpage": "အီးမေးအသုံးပြုသူ",
-       "defemailsubject": "{{SITENAME}} အီးမေး",
+       "emailuser": "ဤ​အ​သုံး​ပြု​သူ​အား​ အီး​မေး​ပို့​ပါ​",
+       "emailuser-title-target": "{{GENDER:$1|အသုံးပြုသူ}}ကို အီးမေးပို့ရန်",
+       "defemailsubject": "{{SITENAME}} á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80° \"$1\" á\80\91á\80¶á\80\99á\80¾ á\80¡á\80®á\80¸á\80\99á\80±á\80¸",
        "usermaildisabled": "အသုံးပြုသူအီးမေးကို ပိတ်ထားသည်",
        "noemailtitle": "အီးမေးလိပ်စာ မရှိပါ",
        "noemailtext": "ဤအသုံးပြုသူသည် တရားဝင်သော အီးမေးလိပ်စာကို ဖောိပြထားခြင်း မရှိပါ။",
        "watchlistfor2": "$1 အတွက် $2",
        "nowatchlist": "သင့်စောင့်ကြည့်စာရင်းမှာ ဘာမှ မရှိပါ။",
        "watchnologin": "logged in ဝင်မထားပါ",
-       "addedwatchtext": "\"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ [[Special:Watchlist|á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\91á\80² á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\94á\80±á\80¬á\80\80á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80¡á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9cá\80²á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\86á\80\80á\80ºá\80\94á\80½á\80\9aá\80ºá\80\94á\80±á\80\9eá\80±á\80¬ á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9aá\80\84á\80ºá\80¸á\80\94á\80±á\80\9bá\80¬á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\95á\80¼á\80¯á\80\85á\80¯á\80\91á\80¬á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\9b á\80\9cá\80½á\80\9aá\80ºá\80\80á\80°á\80\85á\80±á\80\9bá\80\94á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º [[Special:RecentChanges|á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬ á\80¡á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9cá\80²á\80\99á\80»á\80¬á\80¸á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\9cá\80¯á\80¶á\80¸á\80\99á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\84á\80·á\80º á\80\95á\80±á\80«á\80ºá\80\9cá\80¬á\80\99á\80\8aá\80ºဖြစ်သည်။",
-       "removedwatchtext": "\"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ ဖယ်ထုတ်ပြီး ဖြစ်သည်။",
+       "addedwatchtext": "\"[[:$1]]\" á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9eá\80\84á\80ºá\81\8f [[Special:Watchlist|á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\91á\80²á\80\9eá\80­á\80¯á\80· á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸ဖြစ်သည်။",
+       "removedwatchtext": "\"[[:$1]]\" á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80ºá\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9eá\80\84á\80ºá\81\8f [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ ဖယ်ထုတ်ပြီး ဖြစ်သည်။",
        "watch": "စောင့်ကြည့်ရန်",
        "watchthispage": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "unwatch": "ဆက်လက် စောင့်မကြည့်တော့ရန်",
        "unwatchthispage": "စောင့်ကြည့်ခြင်းကို ရပ်တန့်ရန်",
        "notanarticle": "မာတိကာစာမျက်နှာတစ်ခု မဟုတ်",
-       "watchlist-details": "{{PLURAL:$1|á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ $1 á\80\81á\80¯|á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ $1 á\80\81á\80¯}} á\80\9eá\80\8aá\80º á\80\9eá\80\84á\80·á\80ºá\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\90á\80½á\80\84á\80º á\80\9bá\80¾á\80­á\80\9eá\80\8aá\80ºá\81\8b ဆွေးနွေးချက်စာမျက်နှာများကို ထည့်တွက် မထားပါ။",
+       "watchlist-details": "{{PLURAL:$1|á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ $1 á\80\81á\80¯|á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ $1 á\80\81á\80¯}} á\80\9eá\80\8aá\80º á\80\9eá\80\84á\80·á\80ºá\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\90á\80½á\80\84á\80º á\80\9bá\80¾á\80­á\80\95á\80¼á\80®á\80¸ ဆွေးနွေးချက်စာမျက်နှာများကို ထည့်တွက် မထားပါ။",
        "wlshowlast": "နောက်ဆုံး $1 နာရီ $2 ရက်  ကိုပြရန်",
+       "watchlistall2": "အားလုံး",
        "watchlist-options": "စောင့်ကြည့်စာရင်းအတွက် ရွေးချယ်စရာများ",
        "watching": "စောင့်ကြည့်လျက်ရှိ...",
        "unwatching": "စောင့်မကြည့်တော့...",
        "changed": "ပြောင်းလဲလိုက်သည်",
        "deletepage": "စာမျက်နှာကိုဖျက်ပါ",
        "confirm": "အတည်ပြု",
+       "excontentauthor": "ပါဝင်အကြောင်းအရာမှာ - \"$1\"၊ ဖြစ်ပြီး \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|ဆွေးနွေး]]) သာလျှင် ဆောင်ရွက်ထားသူ ဖြစ်သည်",
        "delete-confirm": "\"$1\"ကို ဖျက်ပါ",
        "delete-legend": "ဖျက်",
        "confirmdeletetext": "သင်သည် စာမျက်နှာတစ်ခုကို ယင်း၏ မှတ်တမ်းများနှင့်တကွ ဖျက်ပစ်တော့မည် ဖြစ်သည်။\nဤသို့ ဖျက်ပစ်ရန် သင် အမှန်တကယ် ရည်ရွယ်လျက်  နောက်ဆက်တွဲ အကျိုးဆက်များကို သိရှိနားလည်ပြီး [[{{MediaWiki:Policy-url}}|မူဝါဒ]] အတိုင်းလုပ်ဆောင်နေခြင်းဖြစ်ကြောင်းကို အတည်ပြုပေးပါ။",
        "protect-locked-access": "သင့်အကောင့်သည် စာမျက်နှာ၏ ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲနိုင်ရန် ခွင့်ပြုချက် မရှိပါ။\nဤသည်မှာ '''$1''' စာမျက်နှာအတွက် လက်ရှိ settings သတ်မှတ်ချက်များ ဖြစ်သည်။",
        "protect-cascadeon": "ပြန်စီစဉ်ခြင်း cascading ကို ကာကွယ်ထားသော အောက်ပါ{{PLURAL:$1|စာမျက်နှာ|စာမျက်နှာများ}} ပါဝင်နေသောကြောင့် ဤစာမျက်နှာကို လက်ရှိတွင် ကာကွယ်ထားသည်။\nဤစာမျက်နှာ၏ ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲသော်လည်း ပြန်စီစဉ်ခြင်းကို အကျိုးသက်ရောက်လိမ့်မည် မဟုတ်။",
        "protect-default": "အသုံးပြုသူ အားလုံးကို ခွင့်ပြုရန်",
-       "protect-fallback": "\"$1\" á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\81á\80»á\80\80á\80º á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºသည်",
-       "protect-level-autoconfirmed": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80° á\80¡á\80\9eá\80\85á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\99á\80¾á\80\90á\80ºá\80\95á\80¯á\80¶á\80\99á\80\90á\80\84á\80ºá\80\9bá\80\9eá\80±á\80¸á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\90á\80¬á\80¸á\80\86á\80®á\80¸á\80\91á\80¬á\80¸á\80\9bá\80\94်",
-       "protect-level-sysop": "á\80¡á\80\80á\80ºá\80\92á\80\99á\80\84á\80ºများသာ",
+       "protect-fallback": "\"$1\" á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸á\80\9bá\80¾á\80­á\80\9eá\80±á\80¬ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9eá\80¬ á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯သည်",
+       "protect-level-autoconfirmed": "á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9eá\80¬ á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\9eá\80\8a်",
+       "protect-level-sysop": "á\80\85á\80®á\80\99á\80¶á\80\81á\80\94á\80·á\80ºá\80\81á\80½á\80²á\80\9eá\80°များသာ",
        "protect-summary-cascade": "အစီအစဉ်ကျအောင် စီနေသည်",
        "protect-expiring": "$1 (UTC) တွင် သက်တမ်းကုန်မည်",
        "protect-cascade": "ဤစာမျက်နှာထဲတွင်ပါသော စာမျက်နှာများကို ကာကွယ်ထားရန် (စီစဥ်ခြင်းကို တားဆီးခြင်း)",
        "namespace": "အမည်ညွှန်း -",
        "invert": "selection ကို ပြောင်းပြန်လှန်ရန်",
        "blanknamespace": "(ပင်မ)",
-       "contributions": "အသုံးပြုသူတို့၏ ပံ့ပိုးပေးမှုများ",
-       "contributions-title": "$1 အတွက် အသုံးပြုသူ၏ ပံ့ပိုးမှုများ",
-       "mycontris": "ပံ့ပိုးထားမှုများ",
-       "contribsub2": "$1အတွက် ($2)",
-       "uctop": "(ထိပ်)",
+       "contributions": "{{GENDER:$1|အသုံးပြုသူ}}၏ ဆောင်ရွက်ချက်များ",
+       "contributions-title": "$1 အတွက် အသုံးပြုသူ၏ ဆောင်ရွက်ချက်များ",
+       "mycontris": "ဆောင်ရွက်ပေးထားမှုများ",
+       "anoncontribs": "ဆောင်ရွက်ချက်များ",
+       "contribsub2": "{{GENDER:$3|$1}}အတွက် ($2)",
+       "uctop": "(လက်ရှိ)",
        "month": "အဆိုပါ လမှစ၍ ( အဆိုပါလထက်လည်း စောသော) :",
        "year": "အဆိုပါ နှစ်မှစ၍ ( အဆိုပါနှစ်ထက်လည်း စောသော) :",
        "sp-contributions-newbies": "အကောင့်အသစ်များ၏ ပံ့ပိုးမှုများကိုသာ ပြရန်",
        "whatlinkshere-hideredirs": "redirect ပြန်ညွှန်း $1 ခု",
        "whatlinkshere-hidetrans": "ထည့်သွင်းကူးယူချက် $1 ခု",
        "whatlinkshere-hidelinks": "လင့် $1 ခု",
-       "whatlinkshere-hideimages": "á\80\95á\80¯á\80¶á\80\9cá\80\84á\80·á\80º $1 ခု",
+       "whatlinkshere-hideimages": "á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\81á\80»á\80­á\80\90á\80ºá\80¡á\80\86á\80\80á\80ºá\80\99á\80»á\80¬á\80¸ $1 ခု",
        "whatlinkshere-filters": "စိစစ်မှုများ",
-       "blockip": "အသုံးပြုသူကို ပိတ်ပင်ရန်",
+       "blockip": "{{GENDER:$1|အသုံးပြုသူ}} ပိတ်ပင်ရန်",
        "blockip-legend": "အသုံးပြုသူကို ပိတ်ပင်ရန်",
        "ipaddressorusername": "အိုင်ပီလိပ်စာ သို့ အသုံးပြုသူအမည် -",
        "ipbexpiry": "သက်တမ်းကုန်လွန်ရက် -",
        "ipb-unblock-addr": "$1 ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ရန်",
        "ipb-unblock": "အသုံးပြုသူအမည် သို့ IP address ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ပေးရန်",
        "ipb-blocklist": "ရှိနှင့်ပြီးသား ပိတ်ပင်မှုများကို ကြည့်ရန်",
-       "ipb-blocklist-contribs": "$1 အတွက် ပံ့ပိုးမှုများ",
-       "unblockip": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9bá\80¬á\80\99á\80¾ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80ºá\80¼á\80·ပေးရန်",
+       "ipb-blocklist-contribs": "{{GENDER:$1|$1}} အတွက် ဆောင်ရွက်ချက်များ",
+       "unblockip": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9bá\80¬á\80\99á\80¾ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80·á\80ºပေးရန်",
        "ipusubmit": "ဤပိတ်ပင်မှုကို ဖယ်ရှားရန်",
        "unblocked": "[[User:$1|$1]] ကို ပိတ်ပင်ထားရာမှ ပြန်ဖွင့်ပေးလိုက်သည်",
        "unblocked-id": "$1 ကို ပိတ်ပင်ထားမှုကို ဖယ်ရှာလိုက်သည်",
+       "blocklist": "ပိတ်ပင်ထားသော အသုံးပြုသူများ",
        "ipblocklist": "ပိတ်ပင်ထားသော အသုံးပြုသူများ",
        "ipblocklist-legend": "ပိတ်ပင်ထားသော အသုံးပြုသူတစ်ဦးကို ရှာရန်",
        "ipblocklist-submit": "ရှာ​ဖွေ​ရန်​",
        "ipb_already_blocked": "\"$1\" ကို အစကတည်းက ပိတ်ထားသည်",
        "move-page": "$1 ကို ရွှေ့ရန်",
        "move-page-legend": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန်",
-       "movepagetext": "အောက်ပါပုံစံကို အသုံးပြုပါက စာမျက်နှာကို အမည်ပြောင်းလဲပေးမည် ဖြစ်ပြီး အမည်သစ်သို့ ယင်း၏ မှတ်တမ်းနှင့်တကွ ရွှေ့ပေးမည် ဖြစ်သည်။\nအမည်ဟောင်းသည် အမည်သစ်သို့ ပြန်ညွှန်းပေးမည် ဖြစ်သည်။\nသင်သည် မူလခေါင်းစဉ်သို့ ပြန်ညွှန်းများကို အလိုအလျောက် အပ်ဒိတ် update လုပ်နိုင်သည်။\nအကယ်၍ မပြုလုပ်လိုပါက [[Special:DoubleRedirects|နှစ်ခါထပ်]][[Special:BrokenRedirects|ပြန်ညွှန်း အပျက်များ]] ကို မှတ်သားရန် မမေ့ပါနှင့်။\nလင့်များ ညွှန်းလိုသည့် နေရာသို့ ညွှန်ပြနေရန် သင့်တွင် တာဝန် ရှိသည်။\n\nအကယ်၍ ခေါင်းစဉ်အသစ်တွင် စာမျက်နှာတစ်ခု ရှိနှင့်ပြီး ဖြစ်ပါက (သို့) ယင်းစာမျက်နှာသည် အလွတ်မဖြစ်ပါက (သို့) ပြန်ညွှန်းတစ်ခု မရှိပါက (သို့) ယခင်က ပြုပြင်ထားသော မှတ်တမ်း မရှိပါက စာမျက်နှာသည် '''ရွေ့မည်မဟုတ်''' သည်ကို သတိပြုပါ။ \nဆိုလိုသည်မှာ သင်သည် အမှားတစ်ခု ပြုလုပ်မိပါက စာမျက်နှာကို ယခင်အမည်ကို ပြန်လည် ပြောင်းလဲပေးနိုင်သည်။ ရှိပြီသားစာမျက်နှာတစ်ခုကို စာမျက်နှာ အသစ်နှင့် ပြန်အုပ် overwrite ခြင်း မပြုနိုင်။\n\n'''သတိပေးချက်!'''\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
-       "movepagetalktext": "ဆက်နွယ်နေသော ဆွေးနွေးချက် စာမျက်နှာကို '''အောက်ပါအကြောင်းများ မရှိခဲ့ပါက''' အလိုအလျောက် ရွှေ့ပစ်မည် ဖြစ်သည်။\n*အကယ်၍ ဗလာမဟုတ်သော ဆွေးနွေးချက်စာမျက်နှာသည် အမည်အသစ်အောက်တွင် ရှိနှင့်ပြီး ဖြစ်ခြင်း (သို့)\n*အောက်ပါ သေတ္တာငယ် box ကို မှတ်သားခြင်း။\n\nဤကိစ္စမျိုး ကြုံလာခဲ့ပါက သင် ဆန္ဒရှိလျှင် စာမျက်နှာကို မိမိကိုယ်တိုင် သွားရောက်ရွှေ့ပြောင်း ပေါင်းစပ်နိုင်သည်။",
-       "movearticle": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန် -",
-       "newtitle": "ခေါင်းစဉ်အသစ်သို့:",
+       "movepagetext": "အောက်ပါပုံစံကို အသုံးပြုခြင်းသည် စာမျက်နှာကို အမည်ပြောင်းလဲပေးမည် ဖြစ်ပြီး အမည်သစ်သို့ ယင်း၏ မှတ်တမ်းနှင့်တကွ ရွှေ့ပေးမည် ဖြစ်သည်။\nအမည်ဟောင်းသည် အမည်သစ်သို့ ပြန်ညွှန်းစာမျက်နှာ ဖြစ်လာမည်။\nသင်သည် မူလခေါင်းစဉ်သို့ ပြန်ညွှန်းများကို အလိုအလျောက် အပ်ဒိတ် update လုပ်နိုင်သည်။\nအကယ်၍ မပြုလုပ်လိုပါက [[Special:DoubleRedirects|နှစ်ခါထပ်]][[Special:BrokenRedirects|ပြန်ညွှန်း အပျက်များ]] ကို မှတ်သားရန် မမေ့ပါနှင့်။\nလင့်များ ညွှန်းလိုသည့် နေရာသို့ ညွှန်ပြနေရန် သင့်တွင် တာဝန် ရှိသည်။\n\nအကယ်၍ ခေါင်းစဉ်အသစ်တွင် စာမျက်နှာတစ်ခု ရှိနှင့်ပြီး ဖြစ်ပါက (သို့) ယင်းစာမျက်နှာသည် အလွတ်မဖြစ်ပါက (သို့) ပြန်ညွှန်းတစ်ခု မရှိပါက (သို့) ယခင်က ပြုပြင်ထားသော မှတ်တမ်း မရှိပါက စာမျက်နှာသည် <strong>ရွေ့မည်မဟုတ်</strong> သည်ကို သတိပြုပါ။ \nဆိုလိုသည်မှာ သင်သည် အမှားတစ်ခု ပြုလုပ်မိပါက စာမျက်နှာကို ယခင်အမည်ကို ပြန်လည် ပြောင်းလဲပေးနိုင်သည်။ ရှိပြီသားစာမျက်နှာတစ်ခုကို စာမျက်နှာ အသစ်နှင့် ပြန်အုပ် overwrite ခြင်း မပြုနိုင်။\n\n<strong>သတိပေးချက်!</strong>\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
+       "movepagetalktext": "ဤအကွက်ကို အမှန်ခြစ်လိုက်ခြင်းဖြင့် ဗလာမဟုတ်သော ဆွေးနွေးချက်စာမျက်နှာသည် ရှိနှင့်ပြီး မဟုတ်လျှင် ဆက်နွယ်နေသော ဆွေးနွေးချက် စာမျက်နှာကို ခေါင်းစဉ်အသစ်သို့  အလိုအလျောက် ရွှေ့ပစ်မည် ဖြစ်သည်။\n\nဤကိစ္စရပ်တွင် သင် ဆန္ဒရှိလျှင် စာမျက်နှာကို မိမိကိုယ်တိုင် သွားရောက်ရွှေ့ပြောင်း ပေါင်းစပ်နိုင်သည်။",
+       "newtitle": "ခေါင်းစဉ်အသစ်:",
        "move-watch": "မူရင်းစာမျက်နှာနှင့် ဦးတည်ထားသော စာမျက်နှာတို့ကို စောင့်ကြည့်ရန်",
        "movepagebtn": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန်",
        "pagemovedsub": "ပြောင်းရွှေ့ခြင်းအောင်မြင်သည်",
        "movelogpage": "ရွှေ့ပြောင်းခြင်း မှတ်တမ်း",
        "movereason": "အ​ကြောင်း​ပြ​ချက် -",
        "revertmove": "ပြောင်းရန်",
-       "delete_and_move": "ဖျက်ပြီးရွှေ့ရန်",
        "delete_and_move_confirm": "ဟုတ်ပါသည်။ စာမျက်နှာကို ဖျက်ပါ။",
        "immobile-source-page": "ဤစာမျက်နှာကို ရွှေ့မရပါ။",
        "export": "စာမျက်နှာများကို Export ထုတ်ရန်",
        "import-noarticle": "မည်သည့်စာမျက်နှာမှ ထည့်သွင်းခြင်းမရှိပါ။",
        "importlogpage": "ထည့်သွင်းသည့် မှတ်တမ်း",
        "tooltip-pt-userpage": "ကိုယ်ပိုင်စာမျက်နှာ",
-       "tooltip-pt-mytalk": "á\80\80á\80»á\80½á\80\94á\80¯á\80ºá\80\95á\80ºá\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸",
+       "tooltip-pt-mytalk": "á\80\9eá\80\84á\80ºá\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬",
        "tooltip-pt-preferences": "ကျွန်​တော့​ရွေး​ချယ်​စ​ရာ​များ​",
        "tooltip-pt-watchlist": "အပြောင်းအလဲများအတွက် စောင့်ကြည့်နေသော စာမျက်နှာများ၏ စာရင်း",
        "tooltip-pt-mycontris": "သင့်ပံ့ပိုးမှုများ၏ စာရင်း",
        "tooltip-pt-login": "မှတ်ပုံတင်ဖြင့် log in ဝင်ရန် အားပေးပါသည်။ သို့သော် မှတ်ပုံမတင်မနေရ မဟုတ်ပါ။",
        "tooltip-pt-logout": "ထွက်​ပါ​",
+       "tooltip-pt-createaccount": "အကောင့်တစ်ခု ဖန်တီးပြီး လော့ဂ်အင်ဝင်ရန် သင့်အား အားပေးပါသည်၊ မဖြစ်မနေ လုပ်ဆောင်ရန် မဟုတ်ပါ",
        "tooltip-ca-talk": "မာတိကာ စာမျက်နှာအတွက် ဆွေးနွေးချက်များ",
-       "tooltip-ca-edit": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\80á\80»á\80±á\80¸á\80\87á\80°á\80¸á\80\95á\80¼á\80¯á\81\8d á\80\99á\80\9eá\80­á\80\99á\80ºá\80¸á\80\81á\80\84á\80º á\80\94á\80\99á\80°á\80\94á\80¬ á\80\81á\80\9cá\80¯á\80\90á\80ºá\80\80á\80­á\80¯á\80\94á\80¾á\80­á\80\95á\80ºá\80\95á\80¼á\80®á\80¸ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80±á\80¸á\80\95á\80«á\81\8b",
+       "tooltip-ca-edit": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\95á\80¼á\80\84á\80ºá\80\9bá\80\94á\80º",
        "tooltip-ca-addsection": "အပိုင်းသစ်တစ်ခု စရန်",
        "tooltip-ca-viewsource": "ဤစာမျက်နှာကို တည်းဖြတ်ခြင်းမှ တားဆီးထားသည်။\nရင်းမြစ် စာသားများကို ကြည့်ရှုနိုင်သည်။",
        "tooltip-ca-history": "ဤစာမျက်နှာ၏ ယခင်မူများ",
        "tooltip-ca-protect": "ဤစာမျက်နှာကို ထိန်းသိမ်းပါ",
-       "tooltip-ca-unprotect": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
+       "tooltip-ca-unprotect": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "tooltip-ca-delete": "ဤစာမျက်နှာဖျက်ပါ",
        "tooltip-ca-move": "ဤစာမျက်နှာကို ရွှေ့ပြောင်းရန်",
        "tooltip-ca-watch": "ဤစာမျက်နှာကို စောင့်ကြည့်စာရင်းသို့ ထည့်ရန်",
        "tooltip-preferences-save": "ရေးချယ်စရာများကို သိမ်းရန်",
        "tooltip-summary": "အတိုချုပ်ထည့်ရန်",
        "others": "အခြား",
+       "pageinfo-toolboxlink": "စာမျက်နှာ အချက်အလက်များ",
        "filedeleteerror-short": "ဖိုင်ဖျက်ရာတွင် အမှားအယွင်း - $1",
        "previousdiff": "← တည်းဖြတ်မူ အဟောင်း",
        "nextdiff": "ပိုသစ်သော တည်းဖြတ်မှု",
        "file-info-size": "$1 × $2 pixels, ဖိုင်အရွယ်အစား - $3, MIME အမျိုးအစား $4",
        "file-nohires": "သည်ထက်ကြီးသော resolution မရှိပါ.",
        "svg-long-desc": "SVG ဖိုင်, $1 × $2 pixels ကို အကြံပြုသည်, ဖိုင်အရွယ်အစား - $3",
-       "show-big-image": "resolution အပြည့်",
+       "show-big-image": "မူရင်းဖိုင်",
+       "show-big-image-preview": "ဤနမူနာ၏ အရွယ်အစား - $1။",
        "newimages": "ပုံအသစ်များပြခန်း",
        "newimages-legend": "စိစစ်မှု",
        "newimages-label": "ဖိုင်အမည် (သို့ ယင်း၏အစိတ်အပိုင်း) -",
        "exif-imagewidth": "အကျယ်",
        "exif-imagelength": "အမြင့်",
        "exif-bitspersample": "အစိတ်အပိုင်းတစ်ခုတွင်ပါဝင်သော အပိုင်းငယ်များ",
+       "exif-xresolution": "အလျားလိုက် ပုံရိပ်ပြတ်သားမှု",
+       "exif-yresolution": "ဒေါင်လိုက် ပုံရိပ်ပြတ်သားမှု",
        "exif-imagedescription": "ပုံခေါင်းစဉ်",
        "exif-make": "ကင်မရာ ထုတ်လုပ်သူ",
        "exif-model": "ကင်မရာ မော်ဒယ်",
        "exif-software": "အသုံးပြုထားသော ဆော့ဝဲ",
        "exif-artist": "ဖန်တီးသူ",
        "exif-copyright": "မူပိုင်ခွင့်ပိုင်ရှင်",
-       "exif-pixelydimension": "á\80\90á\80\9bá\80¬á\80¸á\80\9dá\80\84á\80ºá\80\95á\80¯á\80¶á\80¡á\80\80á\80»á\80\9aá\80º",
-       "exif-pixelxdimension": "á\80\90á\80\9bá\80¬á\80¸á\80\9dá\80\84á\80º á\80\95á\80¯á\80¶á\80¡á\80\99á\80¼á\80\84á\80·á\80º",
+       "exif-pixelydimension": "ပုံအကျယ်",
+       "exif-pixelxdimension": "ပုံအမြင့်",
        "exif-usercomment": "အသုံးပြုသူ မှတ်ချက်များ",
        "exif-relatedsoundfile": "ဆက်နွယ်သော အသံဖိုင်",
        "exif-datetimeoriginal": "ဒေတာဖန်တီးခဲ့သော နေ့စွဲနှင့် အချိန်",
        "exif-exposuretime-format": "$1 စက္ကန့် ($2)",
-       "exif-shutterspeedvalue": "ရှပ်တာ အမြန်နှုန်း",
+       "exif-shutterspeedvalue": "APEX ရှပ်တာ အမြန်နှုန်း",
        "exif-flash": "ဖလက်ရှ်",
        "exif-filesource": "ဖိုင်ရင်းမြစ်",
        "exif-gpslatitude": "လတ္တီကျု",
        "exif-subjectdistancerange-1": "မက်ကရို",
        "exif-gpslongitude-w": "အနောက်လောင်ဂျီကျု",
        "exif-gpsspeed-m": "တစ်နာရီလျှင် ရှိသည့် မိုင်နှုန်း",
+       "exif-dc-contributor": "ဆောင်ရွက်ပေးထားသူများ",
        "namespacesall": "အားလုံး",
        "monthsall": "အားလုံး",
        "confirmemail": "အီးမေးကိုအတည်ပြုပါ",
        "watchlisttools-view": "ကိုက်ညီသော အပြောင်းအလဲများကို ကြည့်ရန်",
        "watchlisttools-edit": "စောင့်ကြည့်စာရင်းများကို ကြည့်ပြီး တည်းဖြတ်ပါ။",
        "watchlisttools-raw": "စောင့်ကြည့်စာရင်း အကြမ်းကို တည်းဖြတ်ရန်",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ဆွေးနွေး]])",
        "duplicate-defaultsort": "'''သတိပေးချက် -''' ပုံမှန် sort key \"$2\" oသည် ယခင်ပုံမှန်ဖြစ်သော sort key \"$1\" ကို override ထပ်ရေးမည်ဖြစ်သည်.",
        "version": "ဗားရှင်း",
        "version-specialpages": "အ​ထူး ​စာ​မျက်​နှာ​များ",
        "version-other": "အခြား",
-       "version-license": "လိုင်စင်",
+       "version-license": "á\80\99á\80®á\80\92á\80®á\80\9aá\80¬á\80\9dá\80®á\80\80á\80® á\80\9cá\80­á\80¯á\80\84á\80ºá\80\85á\80\84á\80º",
        "version-software": "သွင်းထားသော ဆော့ဝဲ",
        "version-software-product": "ထုတ်ကုန်",
        "version-software-version": "ဗားရှင်း",
        "specialpages": "အ​ထူး ​စာ​မျက်​နှာ​များ",
        "specialpages-group-maintenance": "ထိန်းသိမ်းမှု အစီရင်ခံချက်များ",
        "specialpages-group-other": "အခြားအထူးစာမျက်နှာများ",
-       "specialpages-group-login": "Login ဝင်ရန်/ အကောင့်လုပ်ရန်",
+       "specialpages-group-login": "Log in ဝင်ရန်/ အကောင့် ဖန်တီးရန်",
        "specialpages-group-changes": "လတ်တလောအေပြောင်းအလဲနှင့် မှတ်တမ်းများ",
        "specialpages-group-media": "မီဒီယာ အစီရင်ခံချက်များနှင့် Upload တင်ထားသည်များ",
        "specialpages-group-users": "အသုံးပြုသူများနှင့် အခွင့်အရေးများ",
        "specialpages-group-highuse": "အသုံးများသော စာမျက်နှာများ",
        "specialpages-group-pages": "စာမျက်နှာစာရင်းများ",
        "specialpages-group-pagetools": "စာမျက်နှာအတွက် ကိရိယာများ",
-       "specialpages-group-wiki": "á\80\9dá\80®á\80\80á\80®á\80\92á\80±á\80\90á\80¬á\80\94á\80¾á\80\84á\80·á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸",
+       "specialpages-group-wiki": "ဒေတာနှင့် ကိရိယာများ",
        "specialpages-group-redirects": "အထူးစာမျက်နှာများကို ပြန်ညွှန်းနေသည်",
        "specialpages-group-spam": "စပမ်းကိရိယာများ",
        "blankpage": "ဗလာစာမျက်နှာ",
        "tags-title": "အမည်တွဲ",
        "tags-tag": "အမည်တွဲ အမည်",
        "tags-edit": "ပြင်​ဆင်​ရန်",
-       "comparepages": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\94á\80¾á\80¯á\80­င်းယှဉ်ရန်",
+       "comparepages": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\94á\80¾á\80­á\80¯င်းယှဉ်ရန်",
        "compare-page1": "စာမျက်နှာတစ်",
        "compare-page2": "စာမျက်နှာနှစ်",
        "compare-rev1": "မူ တစ်",
        "htmlform-selectorother-other": "အခြား",
        "revdelete-restricted": "အက်ဒမင်များသို့ ကန့်သတ်ချက်များ သက်ရောက်ရန်",
        "revdelete-unrestricted": "အက်ဒမင်များအတွက် ကန့်သတ်ချက်များကို ဖယ်ရှားရန်",
+       "logentry-newusers-create": "အသုံးပြုသူအကောင့် $1 ကို {{GENDER:$2|ဖန်တီးခဲ့သည်}}",
        "rightsnone": "(ဘာမှမရှိ)",
        "revdelete-summary": "အကျဉ်းချုပ်ကို တည်းဖြတ်ရန်",
        "searchsuggest-search": "ရှာဖွေရန်",
index 9694320..466e5cb 100644 (file)
        "october-date": "$1 ottovre",
        "november-date": "$1 nuvembre",
        "december-date": "$1 dicembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categurìa|Categurìe}}",
        "category_header": "Paggene rìnt'a categurìa \"$1\"",
        "subcategories": "Categurìe secunnarie",
        "unblock": "Sblocca l'utente",
        "blockip": "Blocca {{GENDER:$1|utente}}",
        "blockip-legend": "Blocca l'utente",
-       "blockiptext": "Ausa 'o modulo ccà abbascio pe' bluccà l'acciesso 'e scrittura a n'indirizzo IP o utente.\nChisto s'avesse 'a ffà sulamente pe' se pruteggere d' 'o vandalismo, d'accordo ch' [[{{MediaWiki:Policy-url}}|'e reole]].\nMettite pure nu mutivo specifico ccà abbascio (p'esempio, facenno 'o nomme 'e paggene addò se so' fatte 'e vandalisme).",
+       "blockiptext": "Ausa 'o modulo ccà abbascio pe' bluccà l'acciesso 'e scrittura a n'indirizzo IP o utente.\nChisto s'avesse 'a ffà sulamente pe' se pruteggere d' 'o vandalismo, d'accordo ch' [[{{MediaWiki:Policy-url}}|'e reole]].\nMettite pure nu mutivo specifico ccà abbascio (p'esempio, facenno 'o nomme 'e paggene addò se so' fatte 'e vandalisme).\nPutite bluccà ntervalle IP ausanno 'a sintasse [https://it.wikipedia.org/wiki/CIDR CIDR]; l'intervallo cchiù ampio cunzentito è /$1 pe' IPv4 e /$2 pe' IPv6.",
        "ipaddressorusername": "Nnerizzo IP o nomme utente",
        "ipbexpiry": "Ammatura:",
        "ipbreason": "Mutivo:",
        "pageinfo-category-files": "Nummero 'e file",
        "markaspatrolleddiff": "Nzègna comme cuntrullata",
        "markaspatrolledtext": "Nzegna sta paggena comme cuntrullata",
+       "markaspatrolledtext-file": "Nzegna stu file comme verificato",
        "markedaspatrolled": "Nzegnata comme cuntrullata",
        "markedaspatrolledtext": "'A verziona scigliuta 'e [[:$1]] è stata nzegnata comme cuntrullata.",
        "rcpatroldisabled": "Funzione cuntrollo 'e ll'urdeme cagnamiente stutata",
        "newimages-legend": "Filtro",
        "newimages-label": "Nomme d' 'o file (o nu piezz' 'e chesto):",
        "newimages-showbots": "Mmusta cárreche 'e robbot",
+       "newimages-hidepatrolled": "Annascunne 'e carreche verificate",
        "noimages": "Nun nc'è nind' 'a veré.",
        "ilsubmit": "Truova",
        "bydate": "pe' data",
        "watchlisttools-edit": "Vide e cagna l'elenco 'e paggene cuntrullate",
        "watchlisttools-raw": "Cagna l'elenco 'e paggene cuntrullate ncruro",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|chiacchiere]])",
+       "timezone-local": "Lucale",
        "duplicate-defaultsort": "<strong>Attenziò:</strong> A chiave d'arricetto \"$2\" se miette ncuollo a nu valore 'e primma \"$1\".",
        "duplicate-displaytitle": "<strong>Attenziò:</strong> A chiave d'arricetto \"$2\" se scagna p' 'o valore 'e primma \"$1\".",
        "invalid-indicator-name": "<strong>Errore:</strong> attribbuto <code>name</code> 'e ll'innecature d' 'o stato d' 'a paggena nu può rummanè abbacante.",
index 6548aa4..04ff6de 100644 (file)
@@ -20,7 +20,8 @@
                        "बिप्लब आनन्द",
                        "Nirjal stha",
                        "राम प्रसाद जोशी",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "जनक राज भट्ट"
                ]
        },
        "tog-underline": "रेखाङ्कित लिङ्क:",
        "unblock": "प्रयोगकर्ता माथिको प्रतिबन्ध हटाउने",
        "blockip": "{{GENDER:$1|प्रयोगकर्ता}}लाई निषेध गर्ने",
        "blockip-legend": "प्रयोगकर्ता रोक्नुहोस",
-       "blockiptext": "विशेष IP ठेगाना अथवा प्रयोगकर्तालाई रोक लगाउन निम्न प्रपत्र (form) प्रयोग गर्नुहोस्।\nयसो गर्नुको कारण [[{{MediaWiki:Policy-url}}|नीति]] अनुरुप विकिमा गरिने बर्बरताका कार्य रोक्नु मात्र हो।\nविशेष कारण देखाउँदै तलको प्रपत्र भर्नुहोस्  (उदाहरण, बर्बरताको कार्य गरिएको पृष्ठ दर्शाउँदै)",
+       "blockiptext": "विशेष IP ठेगाना अथवा प्रयोगकर्तालाई रोक लगाउन निम्न प्रपत्र (form) प्रयोग गर्नुहोस्।\nयसो गर्नुको कारण [[{{MediaWiki:Policy-url}}|नीति]] अनुरुप विकिमा गरिने बर्बरताका कार्य रोक्नु मात्र हो।\nविशेष कारण देखाउँदै तलको प्रपत्र भर्नुहोस्(उदाहरण, बर्बरताको कार्य गरिएको पृष्ठ दर्शाउँदै)",
        "ipaddressorusername": "आइपी ठेगाना वा प्रयोगकर्ता नाम :",
        "ipbexpiry": "सकिने:",
        "ipbreason": "कारण:",
        "hebrew-calendar-m12-gen": "एलल्",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|वार्ता]])",
        "timezone-utc": "युटिसी(UTC)",
+       "timezone-local": "स्थानीय",
        "duplicate-defaultsort": "'''चेतावनी:''' पूर्व निर्धारित छोटकरी \"$2\" ले पुरानो पूर्वनिर्धारित छोटकरी\"$1\"लाई विस्थापन गरेको छ ।",
        "duplicate-displaytitle": "<strong>चेतावनी:</strong> शीर्षक देखाउने \"$2\" पूर्व देखाइएको शीर्षक \"$1\" मा ओभररेड गरिंदै छ।",
        "invalid-indicator-name": "<strong>त्रुटि:</strong> पृष्ठ स्थिति सङ्केतक नाम गुण रित्तो हुनुहुँदैन।",
index 8408227..4e60293 100644 (file)
        "blanknamespace": "(Piä)",
        "contributions": "{{GENDER:$1|Käyttäi}} kirjutukset",
        "mycontris": "Kirjutukset",
+       "anoncontribs": "Sinun panos",
        "month": "Täs kuus (libo aijembi)",
        "year": "Tänävuon (libo aijembi):",
        "whatlinkshere": "Linkit tänne",
index f1a99dc..f88f127 100644 (file)
        "october-date": "$1 października",
        "november-date": "$1 listopada",
        "december-date": "$1 grudnia",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategoria|Kategorie}}",
        "category_header": "Strony w kategorii „$1”",
        "subcategories": "Podkategorie",
        "recentchangesdays-max": "(maksymalnie $1 {{PLURAL:$1|dzień|dni}})",
        "recentchangescount": "Domyślna liczba wyświetlanych edycji:",
        "prefs-help-recentchangescount": "Uwzględnia ostatnie zmiany, historię stron i rejestry.",
-       "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez Ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli musisz go zresetować]].",
+       "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez Ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli chcesz go zresetować]].",
        "savedprefs": "Twoje preferencje zostały zapisane.",
        "savedrights": "Zapisano uprawnienia {{GENDER:$1|użytkownika $1|użytkowniczki $1}}.",
        "timezonelegend": "Strefa czasowa:",
        "unblock": "Odblokuj użytkownika",
        "blockip": "Zablokuj {{GENDER:$1|użytkownika|użytkowniczkę}}",
        "blockip-legend": "Zablokuj użytkownika",
-       "blockiptext": "Użyj poniższego formularza do zablokowania możliwości edycji spod określonego adresu IP lub konkretnemu użytkownikowi.\nBlokować należy jedynie po to, by zapobiec wandalizmom, zgodnie z [[{{MediaWiki:Policy-url}}|przyjętymi zasadami]].\nPodaj powód (np. umieszczając nazwy stron, na których dopuszczono się wandalizmu).",
+       "blockiptext": "Użyj poniższego formularza do zablokowania możliwości edycji spod określonego adresu IP lub konkretnemu użytkownikowi.\nBlokować należy jedynie po to, by zapobiec wandalizmom, zgodnie z [[{{MediaWiki:Policy-url}}|przyjętymi zasadami]].\nPodaj powód (np. umieszczając nazwy stron, na których dopuszczono się wandalizmu).\nMożesz zablokować zakres adresów stosując składnię [https://pl.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; najszerszym dozwolonym zakresem jest /$1 dla IPv4 i /$2 dla IPv6.",
        "ipaddressorusername": "Adres IP lub nazwa użytkownika:",
        "ipbexpiry": "Czas trwania:",
        "ipbreason": "Powód:",
        "hebrew-calendar-m9-gen": "Siwan",
        "hebrew-calendar-m11-gen": "Aw",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|dyskusja]])",
+       "timezone-local": "Czas lokalny",
        "duplicate-defaultsort": "Uwaga: Domyślnym kluczem sortowania będzie „$2” i zastąpi on wcześniej wykorzystywany klucz „$1”.",
        "duplicate-displaytitle": "<strong>Uwaga:</strong> Wyświetlenie tytułu „$2” powoduje nadpisanie wcześniej wyświetlanego tytułu „$1”.",
        "invalid-indicator-name": "<strong>Błąd:</strong> Atrybut stanu strony <code>name</code> nie może być pusty.",
        "log-name-pagelang": "Rejestr zmian języka",
        "log-description-pagelang": "Rejestr zmian języków przypisanych do poszczególnych stron",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|zmienił|zmieniła}} język strony $3 z „$4” na „$5”.",
+       "default-skin-not-found": "Ups! Domyślna skórka dla Twojej wiki, zdefiniowana jako <code dir=\"ltr\">$wgDefaultSkin</code> jako <code>$1</code>, nie jest dostępna.\n\nTwoja instalacja, jak się wydaje, zawiera {{PLURAL:$4|następującą skórkę|następujące skórki}}. Zobacz [https://www.mediawiki.org/wiki/Manual:Skin_configuration/pl Podręcznik:Konfiguracja skórki] z informacjami o tym, jak {{PLURAL:$4|ją włączyć|je włączyć i wybrać domyślną}}.\n\n$2\n\n; Jeśli zainstalowałeś właśnie MediaWiki:\n: Prawdopodobnie zrobiłeś to z Git lub bezpośrednio z kodu źródłowego z wykorzystaniem innej metody. Wtedy jest to możliwe. Spróbuj zainstalować niektóre skórki z [https://www.mediawiki.org/wiki/Category:All_skins/pl folderu skórek serwisu mediawiki.org]:\n:* pobierając [https://www.mediawiki.org/wiki/Download/pl archiwum plików instalacyjnych], zawierające kilka skórek i rozszerzeń. Możesz skopiować i wkleić z niego folder <code>skins/</code>;\n:* pobierając archiwa poszczególnych skórek z [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org];\n:* [https://www.mediawiki.org/wiki/Download_from_Git/pl#Korzystanie_z_Git_do_pobrania_rozszerzeń_MediaWiki Używając Git do pobrania skórek].\n: Jeśli jesteś programistą MediaWiki, nie powinno to zaszkodzić twojemu repozytorium Git.\n\n\n; Jeśli tylko aktualizowałeś MediaWiki:\n: MediaWiki w wersji 1.24 i nowszej nie zawiera automatycznie zainstalowanych skórek (zobacz [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual:Skin autodiscovery]).\nMożna wstawić {{PLURAL:$5|następujący linię|następujące linie}} do <code>LocalSettings.php</code>, aby włączyć {{PLURAL:$5|zainstalowaną skórkę|wszystkie zainstalowane skórki}}: \n\n<pre dir=\"ltr\">$3</pre>\n\n; Jeśli właśnie zmodyfikowałeś <code>LocalSettings.php</code>:\n: Dokładnie sprawdź nazwy skórek pod kątem literówek.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (włączone)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''wyłączone''')",
        "mediastatistics": "Statystyki mediów",
index 92745fc..b9f133d 100644 (file)
        "october-date": "A date in the Gregorian month of October. $1 is the numerical date, for example \"23\".",
        "november-date": "A date in the Gregorian month of November. $1 is the numerical date, for example \"23\".\n{{Identical|November}}",
        "december-date": "A date in the Gregorian month of December. $1 is the numerical date, for example \"23\".",
+       "period-am": "Text indicating the first period of the day when using a 12-hour calendar.",
+       "period-pm": "Text indicating the second period of the day when using a 12-hour calendar.",
        "pagecategories": "Used in the categories section of pages.\n\nFollowed by a colon and a list of categories.\n\nParameters:\n* $1 - number of categories\n{{Identical|Category}}",
        "pagecategorieslink": "{{notranslate}}",
        "category_header": "In category description page. Parameters:\n* $1 - category name\nSee also:\n* {{msg-mw|Category-media-header}}",
        "upload-dialog-title": "Title of the upload dialog box\n{{Identical|Upload file}}",
        "upload-dialog-button-cancel": "Button to cancel the dialog\n{{Identical|Cancel}}",
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
-       "upload-dialog-button-save": "Button to save the file\n{{Identical|Save}}",
-       "upload-dialog-button-upload": "Button to initiate upload\n{{Identical|Upload}}",
+       "upload-dialog-button-save": "Button to save the file after upload finishes and metadata is filled out, part 2 of a multi-step upload form\n{{Identical|Save}}",
+       "upload-dialog-button-upload": "Button to initiate upload, part 1 of a multi-step upload form\n{{Identical|Upload}}",
        "upload-form-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
        "upload-form-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
        "upload-form-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
        "markaspatrolleddiff": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolledtext}}\n{{Identical|Mark as patrolled}}",
        "markaspatrolledlink": "{{notranslate}}\nParameters:\n* $1 - link which has text {{msg-mw|Markaspatrolledtext}}",
        "markaspatrolledtext": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolleddiff}}",
-       "markaspatrolledtext-file": "Same as markaspatrolledtext, but for files (new versions included) instead of pages.",
+       "markaspatrolledtext-file": "Same as markaspatrolledtext, but for files (new versions included) instead of pages.\n\nSee: {{msg-mw|markaspatrolledtext}}",
        "markedaspatrolled": "Used as title of the message {{msg-mw|Markedaspatrolledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
        "markedaspatrolledtext": "Used when marking a change as patrolled.\n\nThe title for this message is {{msg-mw|Markedaspatrolled}}.\n\nParameters:\n* $1 - page title\n{{Related|Markedaspatrolled}}",
        "rcpatroldisabled": "Used as title of the error message {{msg-mw|Rcpatroldisabledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
        "newimages-legend": "Caption of the fieldset for the filter on [[Special:NewImages]]\n\n{{Identical|Filter}}",
        "newimages-label": "Caption of the filter editbox on [[Special:NewImages]]",
        "newimages-showbots": "Used as label for a checkbox. When checked, [[Special:NewImages]] will also display uploads by users in the bots group.",
-       "newimages-hidepatrolled": "Used as label for a checkbox. When checked, [[Special:NewImages]] will not display patrolled uploads.",
+       "newimages-hidepatrolled": "Used as label for a checkbox. When checked, [[Special:NewImages]] will not display patrolled uploads.\n\nCf. {{msg-mw|tog-hidepatrolled}} and {{msg-mw|apihelp-feedrecentchanges-param-hidepatrolled}}.",
        "noimages": "This is shown on the special page [[Special:NewImages]], when there aren't any recently uploaded files.",
        "ilsubmit": "Used as label for input box in the MIMESearch form on [[Special:MIMESearch]].\n\nSee also:\n* {{msg-mw|Mimesearch|page title}}\n* {{msg-mw|Mimetype|label for input box}}\n{{Identical|Search}}",
        "bydate": "{{Identical|Date}}",
        "signature": "This will be substituted in the signature (~<nowiki></nowiki>~~ or ~~<nowiki></nowiki>~~ excluding timestamp).\n\nParameters:\n* $1 - the username that is currently login\n* $2 - the customized signature which is specified in [[Special:Preferences|user's preferences]] as non-raw\nUse your language default parentheses ({{msg-mw|parentheses}}), but not use the message direct.\n\nSee also:\n* {{msg-mw|Signature-anon}} - signature for anonymous user",
        "signature-anon": "{{notranslate}}\nUsed as signature for anonymous user. Parameters:\n* $1 - username (IP address?)\n* $2 - nickname (IP address?)\nSee also:\n* {{msg-mw|Signature}} - signature for registered user",
        "timezone-utc": "{{optional}}",
+       "timezone-local": "Label to indicate that a time is in the user's local timezone.",
        "duplicate-defaultsort": "See definition of [[w:Sorting|sort key]] on Wikipedia. Parameters:\n* $1 - old default sort key\n* $2 - new default sort key",
        "duplicate-displaytitle": "Warning shown when a page has its display title set multiple times. Parameters:\n* $1 - old display title\n* $2 - new display title",
        "invalid-indicator-name": "Warning shown when the [https://www.mediawiki.org/wiki/Help:Page_status_indicators &lt;indicator name=\"''unique-identifier''\">''content''&lt;/indicator>] parser tag is used incorrectly.",
index 080d35e..1ab11ef 100644 (file)
        "group-bot": "Боты",
        "group-sysop": "Администраторы",
        "group-bureaucrat": "Бюрократы",
-       "group-suppress": "РевизоÑ\80Ñ\8b",
+       "group-suppress": "СкÑ\80Ñ\8bваÑ\8eÑ\89ие",
        "group-all": "(все)",
        "group-user-member": "{{GENDER:$1|участник|участница}}",
        "group-autoconfirmed-member": "{{GENDER:$1|автоподтверждённый участник|автоподтверждённая участница}}",
        "group-bot-member": "{{GENDER:$1|бот}}",
        "group-sysop-member": "{{GENDER:$1|администратор}}",
        "group-bureaucrat-member": "{{GENDER:$1|бюрократ}}",
-       "group-suppress-member": "{{GENDER:$1|Ñ\80евизоÑ\80}}",
+       "group-suppress-member": "{{GENDER:$1|Ñ\81кÑ\80Ñ\8bваÑ\8eÑ\89ий}}",
        "grouppage-user": "{{ns:project}}:Участники",
        "grouppage-autoconfirmed": "{{ns:project}}:Автоподтверждённые участники",
        "grouppage-bot": "{{ns:project}}:Боты",
        "grouppage-sysop": "{{ns:project}}:Администраторы",
        "grouppage-bureaucrat": "{{ns:project}}:Бюрократы",
-       "grouppage-suppress": "{{ns:project}}:РевизоÑ\80Ñ\8b",
+       "grouppage-suppress": "{{ns:project}}:СкÑ\80Ñ\8bваÑ\8eÑ\89ие",
        "right-read": "просмотр страниц",
        "right-edit": "правка страниц",
        "right-createpage": "создание страниц, не являющихся обсуждениями",
        "rcshowhidemine": "$1 свои правки",
        "rcshowhidemine-show": "Показать",
        "rcshowhidemine-hide": "Скрыть",
-       "rcshowhidecategorization": "$1 категоризацию страницы",
+       "rcshowhidecategorization": "$1 категоризацию страниц",
        "rcshowhidecategorization-show": "Показать",
        "rcshowhidecategorization-hide": "Скрыть",
        "rclinks": "Показать последние $1 изменений за $2 дней<br />$3",
        "foreign-structured-upload-form-2-label-noderiv": "Оно не должно <strong>содержать чьей-то чужой работы</strong> или быть вдохновлено ей",
        "foreign-structured-upload-form-2-label-useful": "Оно должно быть <strong>образовательным и полезным</strong> для обучения других",
        "foreign-structured-upload-form-2-label-ccbysa": "Вы должны быть согласны на то, чтобы <strong>опубликовать его в Интернете навсегда</strong> под лицензией [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0]",
+       "foreign-structured-upload-form-2-label-alternative": "Если не всё вышеперечисленное верно, вы все равно можете загрузить этот файл, используя кнопку [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастера загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Загружая данный файл, вы подтверждаете, что являетесь владельцем авторских прав на этот файл и безоговорочно согласны загрузить его на Викисклад под лицензией Creative Commons Attribution-ShareAlike 4.0, а также соглашаетесь с [https://wikimediafoundation.org/wiki/Условия_использования Условиями использования].",
        "foreign-structured-upload-form-3-label-question-website": "Вы скачали это изображение с какого-то сайта или, может быть, нашли его через поиск изображений?",
        "foreign-structured-upload-form-3-label-question-ownwork": "Вы создали это изображение (сделали фото, эскиз, чертёж и т. д.) сами?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Содержит ли он работу, принадлежащую кому-то другому (или вдохновлён ей), например, логотип?",
        "foreign-structured-upload-form-3-label-yes": "Да",
        "foreign-structured-upload-form-3-label-no": "Нет",
+       "foreign-structured-upload-form-3-label-alternative": "К сожалению, в данном случае, этот инструмент не поддерживает загрузку данного файла. Вы все равно можете загрузить этот файл, используя [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастер загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
        "foreign-structured-upload-form-4-label-good": "Используя этот инструмент, вы можете загрузить образовательную графику, которую вы создали, и фотографии, которые вы сняли, если они не содержат работ, принадлежащих кому-то другому.",
        "foreign-structured-upload-form-4-label-bad": "Вы не можете загружать изображения, найденные в поисковой системе или скачанные с других сайтов.",
        "backend-fail-stream": "Не удалось транслировать файл $1.",
        "wlshowhideanons": "анонимных участников",
        "wlshowhidepatr": "проверенные правки",
        "wlshowhidemine": "мои правки",
+       "wlshowhidecategorization": "категоризацию страниц",
        "watchlist-options": "Настройки списка наблюдения",
        "watching": "Добавление в список наблюдения…",
        "unwatching": "Удаление из списка наблюдения…",
        "unblock": "Разблокировка участника",
        "blockip": "Заблокировать {{GENDER:$1|участника}}",
        "blockip-legend": "Блокировка участника",
-       "blockiptext": "Используйте форму ниже, чтобы заблокировать возможность записи с определённого IP-адреса.\nЭто может быть сделано только для предотвращения вандализма и только в соответствии с [[{{MediaWiki:Policy-url}}|правилами]].\nНиже укажите конкретную причину (к примеру, процитируйте некоторые страницы с признаками вандализма).",
+       "blockiptext": "Используйте форму ниже, чтобы заблокировать возможность записи с определённого IP-адреса или имени участника.\nЭто может быть сделано только для предотвращения вандализма и только в соответствии с [[{{MediaWiki:Policy-url}}|правилами]].\nНиже укажите конкретную причину (к примеру, процитируйте некоторые страницы с признаками вандализма).\nВы можете заблокировать диапазоны IP-адресов, используя [https://ru.wikipedia.org/wiki/Бесклассовая_адресация CIDR]-синтаксис. Максимально допустимый диапазон — /$1 для протокола IPv4 и /$2 для протокола IPv6.",
        "ipaddressorusername": "IP-адрес или имя участника:",
        "ipbexpiry": "Закончится через:",
        "ipbreason": "Причина:",
        "pageinfo-category-files": "Количество файлов",
        "markaspatrolleddiff": "Отметить как проверенную",
        "markaspatrolledtext": "Отметить эту статью как проверенную",
+       "markaspatrolledtext-file": "Пометить эту версию файла как отпатрулированную",
        "markedaspatrolled": "Отмечена как проверенная",
        "markedaspatrolledtext": "Выбранная версия статьи [[:$1]] была отмечена как отпатрулированная.",
        "rcpatroldisabled": "Патрулирование последних изменений запрещено",
        "newimages-legend": "Фильтр",
        "newimages-label": "Имя файла (или его часть):",
        "newimages-showbots": "Показать загрузки ботов",
+       "newimages-hidepatrolled": "Скрыть отпатрулированные загрузки",
        "noimages": "Изображения отсутствуют.",
        "ilsubmit": "Найти",
        "bydate": "по дате",
        "tags-deactivate": "отключить",
        "tags-hitcount": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
        "tags-manage-no-permission": "У вас нет прав на управление изменениями меток.",
+       "tags-manage-blocked": "Вы не можете управлять метками правок, пока вы заблокированы.",
        "tags-create-heading": "Создать новую метку",
        "tags-create-explanation": "Вновь созданные метки по умолчанию будут созданы доступными для использования участниками и ботами.",
        "tags-create-tag-name": "Название метки:",
        "tags-deactivate-not-allowed": "Невозможно отключить метку «$1».",
        "tags-deactivate-submit": "Отключить",
        "tags-apply-no-permission": "У вас нет права применять метки изменения к своими изменениям.",
+       "tags-apply-blocked": "Вы не можете применять метки правок к своим правкам, пока вы заблокированы.",
        "tags-apply-not-allowed-one": "Метка «$1» не может быть применена вручную.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Следующая метка не может быть применена|Следующие метки не могут быть применены}} вручную: $1",
        "tags-update-no-permission": "У вас нет права на добавление или изменение меток изменения из отдельных версий или записей журналов.",
+       "tags-update-blocked": "Вы не можете добавлять или удалять метки правок, пока вы заблокированы.",
        "tags-update-add-not-allowed-one": "Тег \"$1\" не может быть добавлен вручную.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Следующий тег|Следующие теги}} нельзя добавлять вручную: $1",
        "tags-update-remove-not-allowed-one": "Метка «$1» не может быть удалена.",
index 67fcdff..0101f66 100644 (file)
        "recentchanges-label-plusminus": "पृष्ठस्य आकारः एतावद्भिः बैट्स्-संख्याभिः परिवर्तितः",
        "recentchanges-legend-heading": "'''विकल्पविषयकम्'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|अत्र नूतनपृष्ठानाम् आवलिः]] अपि दृश्यताम्)",
-       "rcnotefrom": "<strong>$3, $4</strong> तः आरभ्य (<strong>$1</strong> पर्यन्तं) जातानि {{PLURAL:$5|is the change|परिवर्तनानि}} अधः प्रदर्शितानि ।",
+       "rcnotefrom": "<strong>$3, $4</strong> तः आरभ्य (<strong>$1</strong> पर्यन्तं) जातानि {{PLURAL:$5|परिवर्तनानि}} अधः प्रदर्शितानि ।",
        "rclistfrom": "$3 $2 पश्चात् जातानि नूतनानि परिवर्तनानि दृश्यन्ताम्",
        "rcshowhideminor": "$1 लघुसम्पादनानि",
        "rcshowhideminor-show": "दृश्यताम्",
        "intentionallyblankpage": "इदं पृष्ठं बुद्ध्या एव रिक्तं रक्षितमस्ति ।",
        "external_image_whitelist": "# एषा पङ्क्तिः न परिवर्त्यताम् <pre>\n# अत्र केवलं सामान्यचिह्नानाम् उपयोगः क्रियताम् (यथा // इत्यनयोः मध्ये स्थापनीयः भागः)\n# बहिस्तात् आगतानां चित्राणां सार्वसङ्केतैः (U R L) सह एतेषां तुलना भवति\n# यत् चित्रम् अनुकूलं भवति तत् योज्यते, अन्यथा तस्य चित्रस्य परिसन्धिः योज्यते । \n# याः पङ्क्तयः # इत्यस्मात् आरभन्ते, ताः सूचनाः\n# अत्र सर्वं पक्षविगुणं (case-insensitive) वर्तते \n# सर्वान् regex भागान् अस्याः पङ्क्तेः उपरि स्थापयतु । एतां पङ्क्तिम् एवमेव स्थापयतु </pre>",
        "tags": "तर्कसिद्धानि परिवर्तनाङ्कनानि",
-       "tag-filter": "[[Special:Tags|Tag]] शोधनी:",
+       "tag-filter": "[[Special:Tags|अङ्कनम्]] शोधनी :",
        "tag-filter-submit": "शोधनी",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|अङ्कनम्|अङ्कनानि}}]] : $2)",
        "tags-title": "अङ्कनानि",
index 07620e2..42e3441 100644 (file)
        "watchlisttools-edit": "వీక్షణ జాబితాను చూడండి లేదా మార్చండి",
        "watchlisttools-raw": "ముడి వీక్షణ జాబితాలో మార్పులు చెయ్యి",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|చర్చ]])",
+       "timezone-local": "స్థానిక",
        "duplicate-defaultsort": "హెచ్చరిక: డిఫాల్టు పేర్చు కీ \"$2\", గత డిఫాల్టు పేర్చు కీ \"$1\" ని అతిక్రమిస్తుంది.",
        "version": "సంచిక",
        "version-extensions": "స్థాపించిన పొడగింతలు",
        "pagelang-language": "భాష",
        "pagelang-use-default": "అప్రమేయ భాషను వాడు",
        "pagelang-select-lang": "భాషను ఎంచుకోండి",
+       "pagelang-submit": "పంపించు",
        "right-pagelang": "పేజీ భాషను మార్చడం",
        "action-pagelang": "పేజీ భాషను మార్చే",
        "log-name-pagelang": "భాష మార్పుల చిట్టా",
        "mediastatistics-header-video": "వీడియోలు",
        "mediastatistics-header-office": "కార్యాలయం",
        "mediastatistics-header-text": "పాఠ్య",
+       "mediastatistics-header-total": "అన్ని ఫైళ్ళు",
        "json-error-state-mismatch": "చెల్లని లేదా సరికాని JSON",
        "json-error-syntax": "వ్యాకరణ దోషం",
        "headline-anchor-title": "ఈ విభాగానికి లంకె",
index 76344ed..20f9da2 100644 (file)
        "virus-scanfailed": "сканерлау хатасы ($1 коды)",
        "virus-unknownscanner": "билгесез антивирус:",
        "logouttext": "<strong>Сез хисап язмагыздан чыктыгыз.</strong>\n\nКайбер битләр Сез кергән кебек күрсәтелергә мөмкин. Моны бетерү өчен браузер кэшын чистартыгыз.",
-       "welcomeuser": "Ð¥Ñ\83Ñ\88 ÐºÐ¸Ð»Ð´егез, $1!",
+       "welcomeuser": "РÓ\99Ñ\85им Ð¸Ñ\82егез, $1!",
        "yourname": "Кулланучы исеме:",
        "userlogin-yourname": "Кулланучы исеме",
        "userlogin-yourname-ph": "Хисап язмасының исемен кертегез",
        "notextmatches": "Тиңдәш текстлы битләр юк",
        "prevn": "алдагы {{PLURAL:$1|$1}}",
        "nextn": "чираттагы {{PLURAL:$1|$1}}",
+       "prev-page": "алдагы бит",
+       "next-page": "киләсе бит",
        "prevn-title": "Алдагы $1  {{PLURAL:$1|язма}}",
        "nextn-title": "{{PLURAL:$1|Киләсе $1 язма}}",
        "shown-title": "Сәхифәдә $1 {{PLURAL:$1|язма}} күрсәтелсен",
        "columns": "Баганалар:",
        "searchresultshead": "Эзләү",
        "stub-threshold": "Ясалма сылтамаларның бизәлеше буенча чикләүләр ($1):",
+       "stub-threshold-sample-link": "мисал",
        "stub-threshold-disabled": "Ябылган",
        "recentchangesdays": "Соңгы үзгәртүләрне күрсәтүче көннәр саны:",
        "recentchangesdays-max": "(иң күбе $1 {{PLURAL:$1|көн}})",
        "prefs-dateformat": "Вакытың форматы",
        "prefs-timeoffset": "Вакыт билгеләнеше",
        "prefs-advancedediting": "Гомуми көйләүләр",
+       "prefs-editor": "Мөхәррир",
+       "prefs-preview": "Алдан карау",
        "prefs-advancedrc": "Киңәйтелгән көйләүләр",
        "prefs-advancedrendering": "Киңәйтелгән көйләүләр",
        "prefs-advancedsearchoptions": "Киңәйтелгән көйләүләр",
        "prefs-advancedwatchlist": "Киңәйтелгән көйләүләр",
        "prefs-displayrc": "Күрсәтү көйләнмәләре",
        "prefs-displaywatchlist": "Күрсәтү көйләнмәләре",
+       "prefs-tokenwatchlist": "Токен",
        "prefs-diffs": "Юрамалар аермасы",
        "userrights": "Кулланучы хокуклары белән идарә итү",
        "userrights-lookup-user": "Кулланучы төркемнәре белән идарә итү",
        "activeusers-hidesysops": "Идарәчеләрне яшер",
        "activeusers-noresult": "Кулланучылар табылмады.",
        "listgrouprights": "Кулланучы төркемнәренең хокуклары",
+       "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Бирелгән хокуклар</span>\n* <span class=\"listgrouprights-revoked\">Алынган хокуклар</span>",
        "listgrouprights-group": "Төркем",
        "listgrouprights-rights": "Хокуклар",
        "listgrouprights-helppage": "Help:Төркемнәрнең хокуклары",
        "unwatchthispage": "Күзәтүне туктат",
        "notanarticle": "Мәкалә түгел",
        "watchlist-details": "Күзәтү исемлегегездә, бәхәс битләрен санамыйча, {{PLURAL:$1|$1 бит}} бар.",
+       "wlheader-showupdated": "Сезнең соңгы төзәтмәләрдән соң үзгәргән битләр <strong>калын</strong> шрифт белән күрсәтелгән.",
+       "wlnote": "Түбәндә $3 $4 вакыт аралыгының {{PLURAL:$2|соңгы сәгатендә|соңгы <strong>$2</strong> сәгатендә}} ясалган {{PLURAL:$1|ахыргы төзәтмә|ахыргы <strong>$1</strong> төзәтмә}} күрсәтелгән.",
        "wlshowlast": "$1 сәгать $2 көн өчендә күрсәтү",
        "watchlistall2": "барлык",
+       "wlshowtime": "Күрсәтелүче вакыт аралыгы:",
+       "wlshowhideminor": "кече үзгәртүләр",
+       "wlshowhideanons": "аноним кулланучыларныкын",
        "watchlist-options": "Күзәтү исемлеге көйләүләре",
        "watching": "Күзәтү исемлегемә өстәүе…",
        "unwatching": "Күзәтү исемлегемнән чыгаруы…",
        "sp-contributions-talk": "бәхәс",
        "sp-contributions-search": "Кертемне эзләү",
        "sp-contributions-username": "Кулланучының IP адресы яки исеме:",
-       "sp-contributions-toponly": "Соңгы версия булган үзгәртүләрне генә күрсәтелсен",
+       "sp-contributions-toponly": "Соңгы юрамадагы үзгәртүләр генә күрсәтелсен",
        "sp-contributions-newonly": "Битләр ясау үзгәртмәләрен генә күрсәтү",
        "sp-contributions-submit": "Эзләү",
        "whatlinkshere": "Бирегә нәрсә сылтый",
        "newimages": "Яңа сүрәтләр җыелмасы",
        "newimages-legend": "Фильтр",
        "ilsubmit": "Эзләү",
-       "hours": "{{PLURAL:$1|$1 cәгать|$1 cәгать}}",
+       "hours": "{{PLURAL:$1|$1 cәгать}}",
+       "days": "{{PLURAL:$1|$1 көн}}",
        "ago": "$1 элек",
        "hours-ago": "$1 cәгать элек",
        "minutes-ago": "$1 минут элек",
index 356aa9c..14b08ca 100644 (file)
        "october-date": "10月$1日",
        "november-date": "11月$1日",
        "december-date": "12月$1日",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|分类}}",
        "category_header": "分类“$1”中的页面",
        "subcategories": "子分类",
        "unblock": "解封用户",
        "blockip": "封禁{{GENDER:$1|用户}}",
        "blockip-legend": "封禁用户",
-       "blockiptext": "使用下方的表单来禁止来自特定IP地址或用户名的写访问。\n只有在为了防止破坏,并符合[[{{MediaWiki:Policy-url}}|方针]]的情况下才可采取此行动。\n请在下面输入一个具体的理由(例如引述一个被破坏的页面)。",
+       "blockiptext": "使用下方的表单来禁止来自特定IP地址或用户名的写访问。\n只有在为了防止破坏,并符合[[{{MediaWiki:Policy-url}}|方针]]的情况下才可采取此行动。\n请在下面输入一个具体的理由(例如引述一个被破坏的页面)。\n您可以使用[https://zh.wikipedia.org/wiki/无类别域间路由 CIDR]语法封禁IP地址段;允许的最大段是/$1(用于IPv4)和/$2(用于IPv6)。",
        "ipaddressorusername": "IP地址或用户名:",
        "ipbexpiry": "终止时间:",
        "ipbreason": "原因:",
        "pageinfo-category-files": "文件数",
        "markaspatrolleddiff": "标记为已巡查",
        "markaspatrolledtext": "标记此页面为已巡查",
+       "markaspatrolledtext-file": "将此文件版本标记为已巡查",
        "markedaspatrolled": "标记为已检查",
        "markedaspatrolledtext": "[[:$1]]的已选中版本已被标识为已巡查。",
        "rcpatroldisabled": "最新更改检查被关闭",
        "newimages-legend": "过滤",
        "newimages-label": "文件名(或它的一部份):",
        "newimages-showbots": "显示机器人上传",
+       "newimages-hidepatrolled": "隐藏已巡查的上传",
        "noimages": "无可查看文件。",
        "ilsubmit": "搜索",
        "bydate": "按日期",
        "watchlisttools-edit": "查看并编辑监视列表",
        "watchlisttools-raw": "编辑原始监视列表",
        "signature": "[[{{ns:user}}:$1|$2]]([[{{ns:user_talk}}:$1|讨论]])",
+       "timezone-local": "本地",
        "duplicate-defaultsort": "<strong>警告:</strong>默认排序关键词“$2”覆盖了之前的默认排序关键词“$1”。",
        "duplicate-displaytitle": "<strong>警告:</strong>显示的标题“$2”重写了此前显示的标题“$1”。",
        "invalid-indicator-name": "<strong>错误:</strong>页面状态指示器的<code>name</code>属性必须不为空。",
index 608605c..2de2e22 100644 (file)
@@ -116,6 +116,13 @@ class ConvertExtensionToRegistration extends Maintenance {
                        }
                }
 
+               // check, if the extension requires composer libraries
+               if ( $this->needsComposerAutoloader( dirname( $this->getArg( 0 ) ) ) ) {
+                       // set the load composer autoloader automatically property
+                       $this->output( "Detected composer dependencies, setting 'load_composer_autoloader' to true.\n" );
+                       $this->json['load_composer_autoloader'] = true;
+               }
+
                // Move some keys to the top
                $out = array();
                foreach ( $this->promote as $key ) {
@@ -246,6 +253,19 @@ class ConvertExtensionToRegistration extends Maintenance {
                        $this->json['ResourceFileModulePaths'] = $defaults;
                }
        }
+
+       protected function needsComposerAutoloader( $path ) {
+               $path .= '/composer.json';
+               if ( file_exists( $path ) ) {
+                       // assume, that the composer.json file is in the root of the extension path
+                       $composerJson = new ComposerJson( $path );
+                       // check, if there are some dependencies in the require section
+                       if ( $composerJson->getRequiredDependencies() ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
 }
 
 $maintClass = 'ConvertExtensionToRegistration';
diff --git a/maintenance/importTextFiles.php b/maintenance/importTextFiles.php
new file mode 100644 (file)
index 0000000..b26eaa5
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/**
+ * Import pages from text files
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Maintenance script which reads in text files
+ * and imports their content to a page of the wiki.
+ *
+ * @ingroup Maintenance
+ */
+class ImportTextFiles extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = "Reads in text files and imports their content to pages of the wiki";
+               $this->addOption( 'user', 'Username to which edits should be attributed. ' .
+                       'Default: "Maintenance script"', false, true, 'u' );
+               $this->addOption( 'summary', 'Specify edit summary for the edits', false, true, 's' );
+               $this->addOption( 'use-timestamp', 'Use the modification date of the text file ' .
+                       'as the timestamp for the edit' );
+               $this->addArg( 'titles', 'Titles of article to edit' );
+       }
+
+       public function execute() {
+               $userName = $this->getOption( 'user', false );
+               $summary = $this->getOption( 'summary', 'Imported from text file' );
+               $useTimestamp = $this->hasOption( 'use-timestamp' );
+
+               // Get all the arguments. A loop is required since Maintenance doesn't
+               // suppport an arbitrary number of arguments.
+               $files = array();
+               $i = 0;
+               while ( $arg = $this->getArg( $i++ ) ) {
+                       if ( file_exists( $arg ) ) {
+                               $files[$arg] = file_get_contents( $arg );
+                       } else {
+                               $this->error( "Fatal error: The file '$arg' does not exist!", 1 );
+                       }
+               };
+
+               $count = count( $files );
+               $this->output( "Creating $count pages...\n" );
+
+               if ( $userName === false ) {
+                       $user = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
+               } else {
+                       $user = User::newFromName( $userName );
+               }
+
+               if ( !$user ) {
+                       $this->error( "Invalid username\n", true );
+               }
+               if ( $user->isAnon() ) {
+                       $user->addToDatabase();
+               }
+
+               $exit = 0;
+
+               $successCount = 0;
+               $failCount = 0;
+               $skipCount = 0;
+
+               foreach ( $files as $file => $text ) {
+                       $pageName = pathinfo( $file, PATHINFO_FILENAME );
+                       $title = Title::newFromText( $pageName );
+                       if ( !$title ) {
+                               $this->error( "Invalid title $pageName. Skipping.\n" );
+                               $skipCount++;
+                               continue;
+                       }
+
+                       if ( $title->exists() ) {
+                               $actualTitle = $title->getPrefixedText();
+                               $this->output( "Title $pageName already exists. Skipping.\n" );
+                               $skipCount++;
+                               continue;
+                       }
+
+                       $actualTitle = $title->getPrefixedText();
+
+                       $rev = new WikiRevision( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
+                       $rev->setText( $text );
+                       $rev->setTitle( $title );
+                       $rev->setUserObj( $user );
+                       $rev->setComment( $summary );
+                       if ( $useTimestamp ) {
+                               $rev->setTimestamp( wfTimestamp( TS_UNIX, filemtime( $file ) ) );
+                       } else {
+                               $rev->setTimestamp( wfTimestampNow() );
+                       }
+
+                       $status = $rev->importOldRevision();
+                       if ( $status ) {
+                               $this->output( "Successfully created $actualTitle\n" );
+                               $successCount++;
+                       } else {
+                               $actualTitle = $title->getPrefixedText();
+                               $this->output( "Failed to create $actualTitle\n" );
+                               $failCount++;
+                               $exit = 1;
+                       }
+               }
+               $this->output( "Done! $successCount successfully created, $skipCount skipped.\n" );
+               if ( $exit ) {
+                       $this->error( "Import failed with $failCount failed pages.\n", $exit );
+               }
+       }
+}
+
+$maintClass = "ImportTextFiles";
+require_once RUN_MAINTENANCE_IF_MAIN;
index 74eaac5..5a8257c 100644 (file)
@@ -12,7 +12,7 @@
     "grunt-contrib-copy": "0.8.1",
     "grunt-contrib-jshint": "0.11.3",
     "grunt-contrib-watch": "0.6.1",
-    "grunt-jscs": "2.5.0",
+    "grunt-jscs": "2.6.0",
     "grunt-jsonlint": "1.0.7",
     "grunt-karma": "0.12.1",
     "karma": "0.13.19",
index 987b97a..bbcf8b5 100644 (file)
@@ -355,9 +355,6 @@ return array(
                'scripts' => 'resources/lib/jquery/jquery.ba-throttle-debounce.js',
                'targets' => array( 'desktop', 'mobile' ),
        ),
-       'jquery.validate' => array(
-               'scripts' => 'resources/lib/jquery/jquery.validate.js',
-       ),
        'jquery.xmldom' => array(
                'scripts' => 'resources/lib/jquery/jquery.xmldom.js',
        ),
@@ -2068,6 +2065,69 @@ return array(
                ),
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.widgets.datetime' => array(
+               'scripts' => array(
+                       'resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js',
+                       'resources/src/mediawiki.widgets.datetime/CalendarWidget.js',
+                       'resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js',
+                       'resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js',
+                       'resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js',
+               ),
+               'skinStyles' => array(
+                       'default' => array(
+                               'resources/src/mediawiki.widgets.datetime/CalendarWidget.less',
+                               'resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less',
+                       ),
+               ),
+               'messages' => array(
+                       'timezone-utc',
+                       'timezone-local',
+                       'january',
+                       'february',
+                       'march',
+                       'april',
+                       'may_long',
+                       'june',
+                       'july',
+                       'august',
+                       'september',
+                       'october',
+                       'november',
+                       'december',
+                       'jan',
+                       'feb',
+                       'mar',
+                       'apr',
+                       'may',
+                       'jun',
+                       'jul',
+                       'aug',
+                       'sep',
+                       'oct',
+                       'nov',
+                       'dec',
+                       'sunday',
+                       'monday',
+                       'tuesday',
+                       'wednesday',
+                       'thursday',
+                       'friday',
+                       'saturday',
+                       'sun',
+                       'mon',
+                       'tue',
+                       'wed',
+                       'thu',
+                       'fri',
+                       'sat',
+                       'period-am',
+                       'period-pm',
+               ),
+               'dependencies' => array(
+                       'oojs-ui',
+               ),
+               'targets' => array( 'desktop', 'mobile' ),
+       ),
        'mediawiki.widgets.CategorySelector' => array(
                'scripts' => array(
                        'resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js',
diff --git a/resources/lib/jquery/jquery.validate.js b/resources/lib/jquery/jquery.validate.js
deleted file mode 100644 (file)
index 72296a6..0000000
+++ /dev/null
@@ -1,1166 +0,0 @@
-/**
- * jQuery Validation Plugin 1.8.1
- *
- * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
- * http://docs.jquery.com/Plugins/Validation
- *
- * Copyright (c) 2006 - 2011 Jörn Zaefferer
- *
- * Dual licensed under the MIT and GPL licenses:
- *   http://www.opensource.org/licenses/mit-license.php
- *   http://www.gnu.org/licenses/gpl.html
- */
-
-(function($) {
-
-$.extend($.fn, {
-       // http://docs.jquery.com/Plugins/Validation/validate
-       validate: function( options ) {
-
-               // if nothing is selected, return nothing; can't chain anyway
-               if (!this.length) {
-                       options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
-                       return;
-               }
-
-               // check if a validator for this form was already created
-               var validator = $.data(this[0], 'validator');
-               if ( validator ) {
-                       return validator;
-               }
-
-               validator = new $.validator( options, this[0] );
-               $.data(this[0], 'validator', validator);
-
-               if ( validator.settings.onsubmit ) {
-
-                       // allow suppresing validation by adding a cancel class to the submit button
-                       this.find("input, button").filter(".cancel").click(function() {
-                               validator.cancelSubmit = true;
-                       });
-
-                       // when a submitHandler is used, capture the submitting button
-                       if (validator.settings.submitHandler) {
-                               this.find("input, button").filter(":submit").click(function() {
-                                       validator.submitButton = this;
-                               });
-                       }
-
-                       // validate the form on submit
-                       this.submit( function( event ) {
-                               if ( validator.settings.debug )
-                                       // prevent form submit to be able to see console output
-                                       event.preventDefault();
-
-                               function handle() {
-                                       if ( validator.settings.submitHandler ) {
-                                               if (validator.submitButton) {
-                                                       // insert a hidden input as a replacement for the missing submit button
-                                                       var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
-                                               }
-                                               validator.settings.submitHandler.call( validator, validator.currentForm );
-                                               if (validator.submitButton) {
-                                                       // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
-                                                       hidden.remove();
-                                               }
-                                               return false;
-                                       }
-                                       return true;
-                               }
-
-                               // prevent submit for invalid forms or custom submit handlers
-                               if ( validator.cancelSubmit ) {
-                                       validator.cancelSubmit = false;
-                                       return handle();
-                               }
-                               if ( validator.form() ) {
-                                       if ( validator.pendingRequest ) {
-                                               validator.formSubmitted = true;
-                                               return false;
-                                       }
-                                       return handle();
-                               } else {
-                                       validator.focusInvalid();
-                                       return false;
-                               }
-                       });
-               }
-
-               return validator;
-       },
-       // http://docs.jquery.com/Plugins/Validation/valid
-       valid: function() {
-        if ( $(this[0]).is('form')) {
-            return this.validate().form();
-        } else {
-            var valid = true;
-            var validator = $(this[0].form).validate();
-            this.each(function() {
-                               valid &= validator.element(this);
-            });
-            return valid;
-        }
-    },
-       // attributes: space seperated list of attributes to retrieve and remove
-       removeAttrs: function(attributes) {
-               var result = {},
-                       $element = this;
-               $.each(attributes.split(/\s/), function(index, value) {
-                       result[value] = $element.attr(value);
-                       $element.removeAttr(value);
-               });
-               return result;
-       },
-       // http://docs.jquery.com/Plugins/Validation/rules
-       rules: function(command, argument) {
-               var element = this[0];
-
-               if (command) {
-                       var settings = $.data(element.form, 'validator').settings;
-                       var staticRules = settings.rules;
-                       var existingRules = $.validator.staticRules(element);
-                       switch(command) {
-                       case "add":
-                               $.extend(existingRules, $.validator.normalizeRule(argument));
-                               staticRules[element.name] = existingRules;
-                               if (argument.messages)
-                                       settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
-                               break;
-                       case "remove":
-                               if (!argument) {
-                                       delete staticRules[element.name];
-                                       return existingRules;
-                               }
-                               var filtered = {};
-                               $.each(argument.split(/\s/), function(index, method) {
-                                       filtered[method] = existingRules[method];
-                                       delete existingRules[method];
-                               });
-                               return filtered;
-                       }
-               }
-
-               var data = $.validator.normalizeRules(
-               $.extend(
-                       {},
-                       $.validator.metadataRules(element),
-                       $.validator.classRules(element),
-                       $.validator.attributeRules(element),
-                       $.validator.staticRules(element)
-               ), element);
-
-               // make sure required is at front
-               if (data.required) {
-                       var param = data.required;
-                       delete data.required;
-                       data = $.extend({required: param}, data);
-               }
-
-               return data;
-       }
-});
-
-// Custom selectors
-$.extend($.expr[":"], {
-       // http://docs.jquery.com/Plugins/Validation/blank
-       blank: function(a) {return !$.trim("" + a.value);},
-       // http://docs.jquery.com/Plugins/Validation/filled
-       filled: function(a) {return !!$.trim("" + a.value);},
-       // http://docs.jquery.com/Plugins/Validation/unchecked
-       unchecked: function(a) {return !a.checked;}
-});
-
-// constructor for validator
-$.validator = function( options, form ) {
-       this.settings = $.extend( true, {}, $.validator.defaults, options );
-       this.currentForm = form;
-       this.init();
-};
-
-$.validator.format = function(source, params) {
-       if ( arguments.length == 1 )
-               return function() {
-                       var args = $.makeArray(arguments);
-                       args.unshift(source);
-                       return $.validator.format.apply( this, args );
-               };
-       if ( arguments.length > 2 && params.constructor != Array  ) {
-               params = $.makeArray(arguments).slice(1);
-       }
-       if ( params.constructor != Array ) {
-               params = [ params ];
-       }
-       $.each(params, function(i, n) {
-               source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
-       });
-       return source;
-};
-
-$.extend($.validator, {
-
-       defaults: {
-               messages: {},
-               groups: {},
-               rules: {},
-               errorClass: "error",
-               validClass: "valid",
-               errorElement: "label",
-               focusInvalid: true,
-               errorContainer: $( [] ),
-               errorLabelContainer: $( [] ),
-               onsubmit: true,
-               ignore: [],
-               ignoreTitle: false,
-               onfocusin: function(element) {
-                       this.lastActive = element;
-
-                       // hide error label and remove error class on focus if enabled
-                       if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
-                               this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
-                               this.addWrapper(this.errorsFor(element)).hide();
-                       }
-               },
-               onfocusout: function(element) {
-                       if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
-                               this.element(element);
-                       }
-               },
-               onkeyup: function(element) {
-                       if ( element.name in this.submitted || element == this.lastElement ) {
-                               this.element(element);
-                       }
-               },
-               onclick: function(element) {
-                       // click on selects, radiobuttons and checkboxes
-                       if ( element.name in this.submitted )
-                               this.element(element);
-                       // or option elements, check parent select in that case
-                       else if (element.parentNode.name in this.submitted)
-                               this.element(element.parentNode);
-               },
-               highlight: function(element, errorClass, validClass) {
-                       if (element.type === 'radio') {
-                               this.findByName(element.name).addClass(errorClass).removeClass(validClass);
-                       } else {
-                               $(element).addClass(errorClass).removeClass(validClass);
-                       }
-               },
-               unhighlight: function(element, errorClass, validClass) {
-                       if (element.type === 'radio') {
-                               this.findByName(element.name).removeClass(errorClass).addClass(validClass);
-                       } else {
-                               $(element).removeClass(errorClass).addClass(validClass);
-                       }
-               }
-       },
-
-       // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
-       setDefaults: function(settings) {
-               $.extend( $.validator.defaults, settings );
-       },
-
-       messages: {
-               required: "This field is required.",
-               remote: "Please fix this field.",
-               email: "Please enter a valid email address.",
-               url: "Please enter a valid URL.",
-               date: "Please enter a valid date.",
-               dateISO: "Please enter a valid date (ISO).",
-               number: "Please enter a valid number.",
-               digits: "Please enter only digits.",
-               creditcard: "Please enter a valid credit card number.",
-               equalTo: "Please enter the same value again.",
-               accept: "Please enter a value with a valid extension.",
-               maxlength: $.validator.format("Please enter no more than {0} characters."),
-               minlength: $.validator.format("Please enter at least {0} characters."),
-               rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
-               range: $.validator.format("Please enter a value between {0} and {1}."),
-               max: $.validator.format("Please enter a value less than or equal to {0}."),
-               min: $.validator.format("Please enter a value greater than or equal to {0}.")
-       },
-
-       autoCreateRanges: false,
-
-       prototype: {
-
-               init: function() {
-                       this.labelContainer = $(this.settings.errorLabelContainer);
-                       this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
-                       this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
-                       this.submitted = {};
-                       this.valueCache = {};
-                       this.pendingRequest = 0;
-                       this.pending = {};
-                       this.invalid = {};
-                       this.reset();
-
-                       var groups = (this.groups = {});
-                       $.each(this.settings.groups, function(key, value) {
-                               $.each(value.split(/\s/), function(index, name) {
-                                       groups[name] = key;
-                               });
-                       });
-                       var rules = this.settings.rules;
-                       $.each(rules, function(key, value) {
-                               rules[key] = $.validator.normalizeRule(value);
-                       });
-
-                       function delegate(event) {
-                               var validator = $.data(this[0].form, "validator"),
-                                       eventType = "on" + event.type.replace(/^validate/, "");
-                               validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] );
-                       }
-                       $(this.currentForm)
-                               .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
-                               .validateDelegate(":radio, :checkbox, select, option", "click", delegate);
-
-                       if (this.settings.invalidHandler)
-                               $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/form
-               form: function() {
-                       this.checkForm();
-                       $.extend(this.submitted, this.errorMap);
-                       this.invalid = $.extend({}, this.errorMap);
-                       if (!this.valid())
-                               $(this.currentForm).triggerHandler("invalid-form", [this]);
-                       this.showErrors();
-                       return this.valid();
-               },
-
-               checkForm: function() {
-                       this.prepareForm();
-                       for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
-                               this.check( elements[i] );
-                       }
-                       return this.valid();
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/element
-               element: function( element ) {
-                       element = this.clean( element );
-                       this.lastElement = element;
-                       this.prepareElement( element );
-                       this.currentElements = $(element);
-                       var result = this.check( element );
-                       if ( result ) {
-                               delete this.invalid[element.name];
-                       } else {
-                               this.invalid[element.name] = true;
-                       }
-                       if ( !this.numberOfInvalids() ) {
-                               // Hide error containers on last error
-                               this.toHide = this.toHide.add( this.containers );
-                       }
-                       this.showErrors();
-                       return result;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
-               showErrors: function(errors) {
-                       if(errors) {
-                               // add items to error list and map
-                               $.extend( this.errorMap, errors );
-                               this.errorList = [];
-                               for ( var name in errors ) {
-                                       this.errorList.push({
-                                               message: errors[name],
-                                               element: this.findByName(name)[0]
-                                       });
-                               }
-                               // remove items from success list
-                               this.successList = $.grep( this.successList, function(element) {
-                                       return !(element.name in errors);
-                               });
-                       }
-                       this.settings.showErrors
-                               ? this.settings.showErrors.call( this, this.errorMap, this.errorList )
-                               : this.defaultShowErrors();
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
-               resetForm: function() {
-                       if ( $.fn.resetForm )
-                               $( this.currentForm ).resetForm();
-                       this.submitted = {};
-                       this.prepareForm();
-                       this.hideErrors();
-                       this.elements().removeClass( this.settings.errorClass );
-               },
-
-               numberOfInvalids: function() {
-                       return this.objectLength(this.invalid);
-               },
-
-               objectLength: function( obj ) {
-                       var count = 0;
-                       for ( var i in obj )
-                               count++;
-                       return count;
-               },
-
-               hideErrors: function() {
-                       this.addWrapper( this.toHide ).hide();
-               },
-
-               valid: function() {
-                       return this.size() == 0;
-               },
-
-               size: function() {
-                       return this.errorList.length;
-               },
-
-               focusInvalid: function() {
-                       if( this.settings.focusInvalid ) {
-                               try {
-                                       $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
-                                       .filter(":visible")
-                                       .focus()
-                                       // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
-                                       .trigger("focusin");
-                               } catch(e) {
-                                       // ignore IE throwing errors when focusing hidden elements
-                               }
-                       }
-               },
-
-               findLastActive: function() {
-                       var lastActive = this.lastActive;
-                       return lastActive && $.grep(this.errorList, function(n) {
-                               return n.element.name == lastActive.name;
-                       }).length == 1 && lastActive;
-               },
-
-               elements: function() {
-                       var validator = this,
-                               rulesCache = {};
-
-                       // select all valid inputs inside the form (no submit or reset buttons)
-                       return $(this.currentForm)
-                       .find("input, select, textarea")
-                       .not(":submit, :reset, :image, [disabled]")
-                       .not( this.settings.ignore )
-                       .filter(function() {
-                               !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
-
-                               // select only the first element for each name, and only those with rules specified
-                               if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
-                                       return false;
-
-                               rulesCache[this.name] = true;
-                               return true;
-                       });
-               },
-
-               clean: function( selector ) {
-                       return $( selector )[0];
-               },
-
-               errors: function() {
-                       return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
-               },
-
-               reset: function() {
-                       this.successList = [];
-                       this.errorList = [];
-                       this.errorMap = {};
-                       this.toShow = $([]);
-                       this.toHide = $([]);
-                       this.currentElements = $([]);
-               },
-
-               prepareForm: function() {
-                       this.reset();
-                       this.toHide = this.errors().add( this.containers );
-               },
-
-               prepareElement: function( element ) {
-                       this.reset();
-                       this.toHide = this.errorsFor(element);
-               },
-
-               check: function( element ) {
-                       element = this.clean( element );
-
-                       // if radio/checkbox, validate first element in group instead
-                       if (this.checkable(element)) {
-                               element = this.findByName( element.name ).not(this.settings.ignore)[0];
-                       }
-
-                       var rules = $(element).rules();
-                       var dependencyMismatch = false;
-                       for (var method in rules ) {
-                               var rule = { method: method, parameters: rules[method] };
-                               try {
-                                       var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
-
-                                       // if a method indicates that the field is optional and therefore valid,
-                                       // don't mark it as valid when there are no other rules
-                                       if ( result == "dependency-mismatch" ) {
-                                               dependencyMismatch = true;
-                                               continue;
-                                       }
-                                       dependencyMismatch = false;
-
-                                       if ( result == "pending" ) {
-                                               this.toHide = this.toHide.not( this.errorsFor(element) );
-                                               return;
-                                       }
-
-                                       if( !result ) {
-                                               this.formatAndAdd( element, rule );
-                                               return false;
-                                       }
-                               } catch(e) {
-                                       this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
-                                                + ", check the '" + rule.method + "' method", e);
-                                       throw e;
-                               }
-                       }
-                       if (dependencyMismatch)
-                               return;
-                       if ( this.objectLength(rules) )
-                               this.successList.push(element);
-                       return true;
-               },
-
-               // return the custom message for the given element and validation method
-               // specified in the element's "messages" metadata
-               customMetaMessage: function(element, method) {
-                       if (!$.metadata)
-                               return;
-
-                       var meta = this.settings.meta
-                               ? $(element).metadata()[this.settings.meta]
-                               : $(element).metadata();
-
-                       return meta && meta.messages && meta.messages[method];
-               },
-
-               // return the custom message for the given element name and validation method
-               customMessage: function( name, method ) {
-                       var m = this.settings.messages[name];
-                       return m && (m.constructor == String
-                               ? m
-                               : m[method]);
-               },
-
-               // return the first defined argument, allowing empty strings
-               findDefined: function() {
-                       for(var i = 0; i < arguments.length; i++) {
-                               if (arguments[i] !== undefined)
-                                       return arguments[i];
-                       }
-                       return undefined;
-               },
-
-               defaultMessage: function( element, method) {
-                       return this.findDefined(
-                               this.customMessage( element.name, method ),
-                               this.customMetaMessage( element, method ),
-                               // title is never undefined, so handle empty string as undefined
-                               !this.settings.ignoreTitle && element.title || undefined,
-                               $.validator.messages[method],
-                               "<strong>Warning: No message defined for " + element.name + "</strong>"
-                       );
-               },
-
-               formatAndAdd: function( element, rule ) {
-                       var message = this.defaultMessage( element, rule.method ),
-                               theregex = /\$?\{(\d+)\}/g;
-                       if ( typeof message == "function" ) {
-                               message = message.call(this, rule.parameters, element);
-                       } else if (theregex.test(message)) {
-                               message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
-                       }
-                       this.errorList.push({
-                               message: message,
-                               element: element
-                       });
-
-                       this.errorMap[element.name] = message;
-                       this.submitted[element.name] = message;
-               },
-
-               addWrapper: function(toToggle) {
-                       if ( this.settings.wrapper )
-                               toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
-                       return toToggle;
-               },
-
-               defaultShowErrors: function() {
-                       for ( var i = 0; this.errorList[i]; i++ ) {
-                               var error = this.errorList[i];
-                               this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
-                               this.showLabel( error.element, error.message );
-                       }
-                       if( this.errorList.length ) {
-                               this.toShow = this.toShow.add( this.containers );
-                       }
-                       if (this.settings.success) {
-                               for ( var i = 0; this.successList[i]; i++ ) {
-                                       this.showLabel( this.successList[i] );
-                               }
-                       }
-                       if (this.settings.unhighlight) {
-                               for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
-                                       this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
-                               }
-                       }
-                       this.toHide = this.toHide.not( this.toShow );
-                       this.hideErrors();
-                       this.addWrapper( this.toShow ).show();
-               },
-
-               validElements: function() {
-                       return this.currentElements.not(this.invalidElements());
-               },
-
-               invalidElements: function() {
-                       return $(this.errorList).map(function() {
-                               return this.element;
-                       });
-               },
-
-               showLabel: function(element, message) {
-                       var label = this.errorsFor( element );
-                       if ( label.length ) {
-                               // refresh error/success class
-                               label.removeClass().addClass( this.settings.errorClass );
-
-                               // check if we have a generated label, replace the message then
-                               label.attr("generated") && label.html(message);
-                       } else {
-                               // create label
-                               label = $("<" + this.settings.errorElement + "/>")
-                                       .attr({"for":  this.idOrName(element), generated: true})
-                                       .addClass(this.settings.errorClass)
-                                       .html(message || "");
-                               if ( this.settings.wrapper ) {
-                                       // make sure the element is visible, even in IE
-                                       // actually showing the wrapped element is handled elsewhere
-                                       label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
-                               }
-                               if ( !this.labelContainer.append(label).length )
-                                       this.settings.errorPlacement
-                                               ? this.settings.errorPlacement(label, $(element) )
-                                               : label.insertAfter(element);
-                       }
-                       if ( !message && this.settings.success ) {
-                               label.text("");
-                               typeof this.settings.success == "string"
-                                       ? label.addClass( this.settings.success )
-                                       : this.settings.success( label );
-                       }
-                       this.toShow = this.toShow.add(label);
-               },
-
-               errorsFor: function(element) {
-                       var name = this.idOrName(element);
-               return this.errors().filter(function() {
-                               return $(this).attr('for') == name;
-                       });
-               },
-
-               idOrName: function(element) {
-                       return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
-               },
-
-               checkable: function( element ) {
-                       return /radio|checkbox/i.test(element.type);
-               },
-
-               findByName: function( name ) {
-                       // select by name and filter by form for performance over form.find("[name=...]")
-                       var form = this.currentForm;
-                       return $(document.getElementsByName(name)).map(function(index, element) {
-                               return element.form == form && element.name == name && element  || null;
-                       });
-               },
-
-               getLength: function(value, element) {
-                       switch( element.nodeName.toLowerCase() ) {
-                       case 'select':
-                               return $("option:selected", element).length;
-                       case 'input':
-                               if( this.checkable( element) )
-                                       return this.findByName(element.name).filter(':checked').length;
-                       }
-                       return value.length;
-               },
-
-               depend: function(param, element) {
-                       return this.dependTypes[typeof param]
-                               ? this.dependTypes[typeof param](param, element)
-                               : true;
-               },
-
-               dependTypes: {
-                       "boolean": function(param, element) {
-                               return param;
-                       },
-                       "string": function(param, element) {
-                               return !!$(param, element.form).length;
-                       },
-                       "function": function(param, element) {
-                               return param(element);
-                       }
-               },
-
-               optional: function(element) {
-                       return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
-               },
-
-               startRequest: function(element) {
-                       if (!this.pending[element.name]) {
-                               this.pendingRequest++;
-                               this.pending[element.name] = true;
-                       }
-               },
-
-               stopRequest: function(element, valid) {
-                       this.pendingRequest--;
-                       // sometimes synchronization fails, make sure pendingRequest is never < 0
-                       if (this.pendingRequest < 0)
-                               this.pendingRequest = 0;
-                       delete this.pending[element.name];
-                       if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
-                               $(this.currentForm).submit();
-                               this.formSubmitted = false;
-                       } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
-                               $(this.currentForm).triggerHandler("invalid-form", [this]);
-                               this.formSubmitted = false;
-                       }
-               },
-
-               previousValue: function(element) {
-                       return $.data(element, "previousValue") || $.data(element, "previousValue", {
-                               old: null,
-                               valid: true,
-                               message: this.defaultMessage( element, "remote" )
-                       });
-               }
-
-       },
-
-       classRuleSettings: {
-               required: {required: true},
-               email: {email: true},
-               url: {url: true},
-               date: {date: true},
-               dateISO: {dateISO: true},
-               dateDE: {dateDE: true},
-               number: {number: true},
-               numberDE: {numberDE: true},
-               digits: {digits: true},
-               creditcard: {creditcard: true}
-       },
-
-       addClassRules: function(className, rules) {
-               className.constructor == String ?
-                       this.classRuleSettings[className] = rules :
-                       $.extend(this.classRuleSettings, className);
-       },
-
-       classRules: function(element) {
-               var rules = {};
-               var classes = $(element).attr('class');
-               classes && $.each(classes.split(' '), function() {
-                       if (this in $.validator.classRuleSettings) {
-                               $.extend(rules, $.validator.classRuleSettings[this]);
-                       }
-               });
-               return rules;
-       },
-
-       attributeRules: function(element) {
-               var rules = {};
-               var $element = $(element);
-
-               for (var method in $.validator.methods) {
-                       var value = $element.attr(method);
-                       if (value) {
-                               rules[method] = value;
-                       }
-               }
-
-               // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
-               if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
-                       delete rules.maxlength;
-               }
-
-               return rules;
-       },
-
-       metadataRules: function(element) {
-               if (!$.metadata) return {};
-
-               var meta = $.data(element.form, 'validator').settings.meta;
-               return meta ?
-                       $(element).metadata()[meta] :
-                       $(element).metadata();
-       },
-
-       staticRules: function(element) {
-               var rules = {};
-               var validator = $.data(element.form, 'validator');
-               if (validator.settings.rules) {
-                       rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
-               }
-               return rules;
-       },
-
-       normalizeRules: function(rules, element) {
-               // handle dependency check
-               $.each(rules, function(prop, val) {
-                       // ignore rule when param is explicitly false, eg. required:false
-                       if (val === false) {
-                               delete rules[prop];
-                               return;
-                       }
-                       if (val.param || val.depends) {
-                               var keepRule = true;
-                               switch (typeof val.depends) {
-                                       case "string":
-                                               keepRule = !!$(val.depends, element.form).length;
-                                               break;
-                                       case "function":
-                                               keepRule = val.depends.call(element, element);
-                                               break;
-                               }
-                               if (keepRule) {
-                                       rules[prop] = val.param !== undefined ? val.param : true;
-                               } else {
-                                       delete rules[prop];
-                               }
-                       }
-               });
-
-               // evaluate parameters
-               $.each(rules, function(rule, parameter) {
-                       rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
-               });
-
-               // clean number parameters
-               $.each(['minlength', 'maxlength', 'min', 'max'], function() {
-                       if (rules[this]) {
-                               rules[this] = Number(rules[this]);
-                       }
-               });
-               $.each(['rangelength', 'range'], function() {
-                       if (rules[this]) {
-                               rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
-                       }
-               });
-
-               if ($.validator.autoCreateRanges) {
-                       // auto-create ranges
-                       if (rules.min && rules.max) {
-                               rules.range = [rules.min, rules.max];
-                               delete rules.min;
-                               delete rules.max;
-                       }
-                       if (rules.minlength && rules.maxlength) {
-                               rules.rangelength = [rules.minlength, rules.maxlength];
-                               delete rules.minlength;
-                               delete rules.maxlength;
-                       }
-               }
-
-               // To support custom messages in metadata ignore rule methods titled "messages"
-               if (rules.messages) {
-                       delete rules.messages;
-               }
-
-               return rules;
-       },
-
-       // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
-       normalizeRule: function(data) {
-               if( typeof data == "string" ) {
-                       var transformed = {};
-                       $.each(data.split(/\s/), function() {
-                               transformed[this] = true;
-                       });
-                       data = transformed;
-               }
-               return data;
-       },
-
-       // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
-       addMethod: function(name, method, message) {
-               $.validator.methods[name] = method;
-               $.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
-               if (method.length < 3) {
-                       $.validator.addClassRules(name, $.validator.normalizeRule(name));
-               }
-       },
-
-       methods: {
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/required
-               required: function(value, element, param) {
-                       // check if dependency is met
-                       if ( !this.depend(param, element) )
-                               return "dependency-mismatch";
-                       switch( element.nodeName.toLowerCase() ) {
-                       case 'select':
-                               // could be an array for select-multiple or a string, both are fine this way
-                               var val = $(element).val();
-                               return val && val.length > 0;
-                       case 'input':
-                               if ( this.checkable(element) )
-                                       return this.getLength(value, element) > 0;
-                       default:
-                               return $.trim(value).length > 0;
-                       }
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/remote
-               remote: function(value, element, param) {
-                       if ( this.optional(element) )
-                               return "dependency-mismatch";
-
-                       var previous = this.previousValue(element);
-                       if (!this.settings.messages[element.name] )
-                               this.settings.messages[element.name] = {};
-                       previous.originalMessage = this.settings.messages[element.name].remote;
-                       this.settings.messages[element.name].remote = previous.message;
-
-                       param = typeof param == "string" && {url:param} || param;
-
-                       if ( this.pending[element.name] ) {
-                               return "pending";
-                       }
-                       if ( previous.old === value ) {
-                               return previous.valid;
-                       }
-
-                       previous.old = value;
-                       var validator = this;
-                       this.startRequest(element);
-                       var data = {};
-                       data[element.name] = value;
-                       $.ajax($.extend(true, {
-                               url: param,
-                               mode: "abort",
-                               port: "validate" + element.name,
-                               dataType: "json",
-                               data: data,
-                               success: function(response) {
-                                       validator.settings.messages[element.name].remote = previous.originalMessage;
-                                       var valid = response === true;
-                                       if ( valid ) {
-                                               var submitted = validator.formSubmitted;
-                                               validator.prepareElement(element);
-                                               validator.formSubmitted = submitted;
-                                               validator.successList.push(element);
-                                               validator.showErrors();
-                                       } else {
-                                               var errors = {};
-                                               var message = response || validator.defaultMessage( element, "remote" );
-                                               errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
-                                               validator.showErrors(errors);
-                                       }
-                                       previous.valid = valid;
-                                       validator.stopRequest(element, valid);
-                               }
-                       }, param));
-                       return "pending";
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/minlength
-               minlength: function(value, element, param) {
-                       return this.optional(element) || this.getLength($.trim(value), element) >= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
-               maxlength: function(value, element, param) {
-                       return this.optional(element) || this.getLength($.trim(value), element) <= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
-               rangelength: function(value, element, param) {
-                       var length = this.getLength($.trim(value), element);
-                       return this.optional(element) || ( length >= param[0] && length <= param[1] );
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/min
-               min: function( value, element, param ) {
-                       return this.optional(element) || value >= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/max
-               max: function( value, element, param ) {
-                       return this.optional(element) || value <= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/range
-               range: function( value, element, param ) {
-                       return this.optional(element) || ( value >= param[0] && value <= param[1] );
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/email
-               email: function(value, element) {
-                       // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
-                       return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/url
-               url: function(value, element) {
-                       // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
-                       return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/date
-               date: function(value, element) {
-                       return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
-               dateISO: function(value, element) {
-                       return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/number
-               number: function(value, element) {
-                       return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/digits
-               digits: function(value, element) {
-                       return this.optional(element) || /^\d+$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
-               // based on http://en.wikipedia.org/wiki/Luhn
-               creditcard: function(value, element) {
-                       if ( this.optional(element) )
-                               return "dependency-mismatch";
-                       // accept only digits and dashes
-                       if (/[^0-9-]+/.test(value))
-                               return false;
-                       var nCheck = 0,
-                               nDigit = 0,
-                               bEven = false;
-
-                       value = value.replace(/\D/g, "");
-
-                       for (var n = value.length - 1; n >= 0; n--) {
-                               var cDigit = value.charAt(n);
-                               var nDigit = parseInt(cDigit, 10);
-                               if (bEven) {
-                                       if ((nDigit *= 2) > 9)
-                                               nDigit -= 9;
-                               }
-                               nCheck += nDigit;
-                               bEven = !bEven;
-                       }
-
-                       return (nCheck % 10) == 0;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/accept
-               accept: function(value, element, param) {
-                       param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
-                       return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
-               equalTo: function(value, element, param) {
-                       // bind to the blur event of the target in order to revalidate whenever the target field is updated
-                       // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
-                       var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
-                               $(element).valid();
-                       });
-                       return value == target.val();
-               }
-
-       }
-
-});
-
-// deprecated, use $.validator.format instead
-$.format = $.validator.format;
-
-})(jQuery);
-
-// ajax mode: abort
-// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
-// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
-;(function($) {
-       var pendingRequests = {};
-       // Use a prefilter if available (1.5+)
-       if ( $.ajaxPrefilter ) {
-               $.ajaxPrefilter(function(settings, _, xhr) {
-                       var port = settings.port;
-                       if (settings.mode == "abort") {
-                               if ( pendingRequests[port] ) {
-                                       pendingRequests[port].abort();
-                               }
-                               pendingRequests[port] = xhr;
-                       }
-               });
-       } else {
-               // Proxy ajax
-               var ajax = $.ajax;
-               $.ajax = function(settings) {
-                       var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
-                               port = ( "port" in settings ? settings : $.ajaxSettings ).port;
-                       if (mode == "abort") {
-                               if ( pendingRequests[port] ) {
-                                       pendingRequests[port].abort();
-                               }
-                               return (pendingRequests[port] = ajax.apply(this, arguments));
-                       }
-                       return ajax.apply(this, arguments);
-               };
-       }
-})(jQuery);
-
-// provides cross-browser focusin and focusout events
-// IE has native support, in other browsers, use event caputuring (neither bubbles)
-
-// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
-// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
-;(function($) {
-       // only implement if not provided by jQuery core (since 1.4)
-       // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
-       if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
-               $.each({
-                       focus: 'focusin',
-                       blur: 'focusout'
-               }, function( original, fix ){
-                       $.event.special[fix] = {
-                               setup:function() {
-                                       this.addEventListener( original, handler, true );
-                               },
-                               teardown:function() {
-                                       this.removeEventListener( original, handler, true );
-                               },
-                               handler: function(e) {
-                                       arguments[0] = $.event.fix(e);
-                                       arguments[0].type = fix;
-                                       return $.event.handle.apply(this, arguments);
-                               }
-                       };
-                       function handler(e) {
-                               e = $.event.fix(e);
-                               e.type = fix;
-                               return $.event.handle.call(this, e);
-                       }
-               });
-       };
-       $.extend($.fn, {
-               validateDelegate: function(delegate, type, handler) {
-                       return this.bind(type, function(event) {
-                               var target = $(event.target);
-                               if (target.is(delegate)) {
-                                       return handler.apply(target, arguments);
-                               }
-                       });
-               }
-       });
-})(jQuery);
diff --git a/resources/src/mediawiki.widgets.datetime/CalendarWidget.js b/resources/src/mediawiki.widgets.datetime/CalendarWidget.js
new file mode 100644 (file)
index 0000000..31b1cd5
--- /dev/null
@@ -0,0 +1,593 @@
+( function ( $, mw ) {
+
+       /**
+        * CalendarWidget displays a calendar that can be used to select a date. It
+        * uses {@link mw.widgets.datetime.DateTimeFormatter DateTimeFormatter} to get the details of
+        * the calendar.
+        *
+        * This widget is mainly intended to be used as a popup from a
+        * {@link mw.widgets.datetime.DateTimeInputWidget DateTimeInputWidget}, but may also be used
+        * standalone.
+        *
+        * @class
+        * @extends OO.ui.Widget
+        * @mixins OO.ui.mixin.TabIndexedElement
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+        *  mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, or an mw.widgets.datetime.DateTimeFormatter
+        *  instance to use.
+        * @cfg {OO.ui.Widget|null} [widget=null] Widget associated with the calendar.
+        *  Specifying this configures the calendar to be used as a popup from the
+        *  specified widget (e.g. absolute positioning, automatic hiding when clicked
+        *  outside).
+        * @cfg {Date|null} [min=null] Minimum allowed date
+        * @cfg {Date|null} [max=null] Maximum allowed date
+        * @cfg {Date} [focusedDate] Initially focused date.
+        * @cfg {Date|Date[]|null} [selected=null] Selected date(s).
+        */
+       mw.widgets.datetime.CalendarWidget = function MwWidgetsDatetimeCalendarWidget( config ) {
+               var $colgroup, $headTR, headings, i;
+
+               // Configuration initialization
+               config = $.extend( {
+                       min: null,
+                       max: null,
+                       focusedDate: new Date(),
+                       selected: null,
+                       formatter: {}
+               }, config );
+
+               // Parent constructor
+               mw.widgets.datetime.CalendarWidget[ 'super' ].call( this, config );
+
+               // Mixin constructors
+               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$element } ) );
+
+               // Properties
+               if ( config.min instanceof Date && config.min.getTime() >= -62167219200000 ) {
+                       this.min = config.min;
+               } else {
+                       this.min = new Date( -62167219200000 ); // 0000-01-01T00:00:00.000Z
+               }
+               if ( config.max instanceof Date && config.max.getTime() <= 253402300799999 ) {
+                       this.max = config.max;
+               } else {
+                       this.max = new Date( 253402300799999 ); // 9999-12-31T12:59:59.999Z
+               }
+
+               if ( config.focusedDate instanceof Date ) {
+                       this.focusedDate = config.focusedDate;
+               } else {
+                       this.focusedDate = new Date();
+               }
+
+               this.selected = [];
+
+               if ( config.formatter instanceof mw.widgets.datetime.DateTimeFormatter ) {
+                       this.formatter = config.formatter;
+               } else if ( $.isPlainObject( config.formatter ) ) {
+                       this.formatter = new mw.widgets.datetime.ProlepticGregorianDateTimeFormatter( config.formatter );
+               } else {
+                       throw new Error( '"formatter" must be an mw.widgets.datetime.DateTimeFormatter or a plain object' );
+               }
+
+               this.calendarData = null;
+
+               this.widget = config.widget;
+               this.$widget = config.widget ? config.widget.$element : null;
+               this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
+
+               this.$head = $( '<div>' );
+               this.$header = $( '<span>' );
+               this.$table = $( '<table>' );
+               this.cols = [];
+               this.colNullable = [];
+               this.headings = [];
+               this.$tableBody = $( '<tbody>' );
+               this.rows = [];
+               this.buttons = {};
+               this.minWidth = 1;
+               this.daysPerWeek = 0;
+
+               // Events
+               this.$element.on( {
+                       keydown: this.onKeyDown.bind( this )
+               } );
+               this.formatter.connect( this, {
+                       local: 'onLocalChange'
+               } );
+               if ( this.$widget ) {
+                       this.checkFocusHandler = this.checkFocus.bind( this );
+                       this.$element.on( {
+                               focusout: this.onFocusOut.bind( this )
+                       } );
+                       this.$widget.on( {
+                               focusout: this.onFocusOut.bind( this )
+                       } );
+               }
+
+               // Initialization
+               this.$head
+                       .addClass( 'mw-widgets-datetime-calendarWidget-heading' )
+                       .append(
+                               new OO.ui.ButtonWidget( {
+                                       icon: 'previous',
+                                       framed: false,
+                                       classes: [ 'mw-widgets-datetime-calendarWidget-previous' ],
+                                       tabIndex: -1
+                               } ).connect( this, { click: 'onPrevClick' } ).$element,
+                               new OO.ui.ButtonWidget( {
+                                       icon: 'next',
+                                       framed: false,
+                                       classes: [ 'mw-widgets-datetime-calendarWidget-next' ],
+                                       tabIndex: -1
+                               } ).connect( this, { click: 'onNextClick' } ).$element,
+                               this.$header
+                       );
+               $colgroup = $( '<colgroup>' );
+               $headTR = $( '<tr>' );
+               this.$table
+                       .addClass( 'mw-widgets-datetime-calendarWidget-grid' )
+                       .append( $colgroup )
+                       .append( $( '<thead>' ).append( $headTR ) )
+                       .append( this.$tableBody );
+
+               headings = this.formatter.getCalendarHeadings();
+               for ( i = 0; i < headings.length; i++ ) {
+                       this.cols[ i ] = $( '<col>' );
+                       this.headings[ i ] = $( '<th>' );
+                       this.colNullable[ i ] = headings[ i ] === null;
+                       if ( headings[ i ] !== null ) {
+                               this.headings[ i ].text( headings[ i ] );
+                               this.minWidth = Math.max( this.minWidth, headings[ i ].length );
+                               this.daysPerWeek++;
+                       }
+                       $colgroup.append( this.cols[ i ] );
+                       $headTR.append( this.headings[ i ] );
+               }
+
+               this.setSelected( config.selected );
+               this.$element
+                       .addClass( 'mw-widgets-datetime-calendarWidget' )
+                       .append( this.$head, this.$table );
+
+               if ( this.widget ) {
+                       this.$element.addClass( 'mw-widgets-datetime-calendarWidget-dependent' );
+
+                       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+                       // that reference properties not initialized at that time of parent class construction
+                       // TODO: Find a better way to handle post-constructor setup
+                       this.visible = false;
+                       this.$element.addClass( 'oo-ui-element-hidden' );
+               } else {
+                       this.updateUI();
+               }
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.CalendarWidget, OO.ui.Widget );
+       OO.mixinClass( mw.widgets.datetime.CalendarWidget, OO.ui.mixin.TabIndexedElement );
+
+       /* Events */
+
+       /**
+        * A `change` event is emitted when the selected dates change
+        *
+        * @event change
+        */
+
+       /**
+        * A `focusChange` event is emitted when the focused date changes
+        *
+        * @event focusChange
+        */
+
+       /**
+        * A `page` event is emitted when the current "month" changes
+        *
+        * @event page
+        */
+
+       /* Methods */
+
+       /**
+        * Return the current selected dates
+        *
+        * @return {Date[]}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.getSelected = function () {
+               return this.selected;
+       };
+
+       /**
+        * Set the selected dates
+        *
+        * @param {Date|Date[]|null} dates
+        * @fires change
+        * @chainable
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.setSelected = function ( dates ) {
+               var i, changed = false;
+
+               if ( dates instanceof Date ) {
+                       dates = [ dates ];
+               } else if ( Array.isArray( dates ) ) {
+                       dates = $.grep( dates, function ( dt ) { return dt instanceof Date; } );
+                       dates.sort();
+               } else {
+                       dates = [];
+               }
+
+               if ( this.selected.length !== dates.length ) {
+                       changed = true;
+               } else {
+                       for ( i = 0; i < dates.length; i++ ) {
+                               if ( dates[ i ].getTime() !== this.selected[ i ].getTime() ) {
+                                       changed = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if ( changed ) {
+                       this.selected = dates;
+                       this.emit( 'change', dates );
+                       this.updateUI();
+               }
+
+               return this;
+       };
+
+       /**
+        * Return the currently-focused date
+        *
+        * @return {Date}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.getFocusedDate = function () {
+               return this.focusedDate;
+       };
+
+       /**
+        * Set the currently-focused date
+        *
+        * @param {Date} date
+        * @fires page
+        * @chainable
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.setFocusedDate = function ( date ) {
+               var changePage = false,
+                       updateUI = false;
+
+               if ( this.focusedDate.getTime() === date.getTime() ) {
+                       return this;
+               }
+
+               if ( !this.formatter.sameCalendarGrid( this.focusedDate, date ) ) {
+                       changePage = true;
+                       updateUI = true;
+               } else if (
+                       !this.formatter.timePartIsEqual( this.focusedDate, date ) ||
+                       !this.formatter.datePartIsEqual( this.focusedDate, date )
+               ) {
+                       updateUI = true;
+               }
+
+               this.focusedDate = date;
+               this.emit( 'focusChanged', this.focusedDate );
+               if ( changePage ) {
+                       this.emit( 'page', date );
+               }
+               if ( updateUI ) {
+                       this.updateUI();
+               }
+
+               return this;
+       };
+
+       /**
+        * Adjust a date
+        *
+        * @protected
+        * @param {Date} date Date to adjust
+        * @param {string} component Component: 'month', 'week', or 'day'
+        * @param {number} delta Integer, usually -1 or 1
+        * @param {boolean} [enforceRange=true] Whether to enforce this.min and this.max
+        * @return {Date}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.adjustDate = function ( date, component, delta ) {
+               var newDate,
+                       data = this.calendarData;
+
+               if ( !data ) {
+                       return date;
+               }
+
+               switch ( component ) {
+                       case 'month':
+                               newDate = this.formatter.adjustComponent( date, data.monthComponent, delta, 'overflow' );
+                               break;
+
+                       case 'week':
+                               if ( data.weekComponent === undefined ) {
+                                       newDate = this.formatter.adjustComponent(
+                                               date, data.dayComponent, delta * this.daysPerWeek, 'overflow' );
+                               } else {
+                                       newDate = this.formatter.adjustComponent( date, data.weekComponent, delta, 'overflow' );
+                               }
+                               break;
+
+                       case 'day':
+                               newDate = this.formatter.adjustComponent( date, data.dayComponent, delta, 'overflow' );
+                               break;
+
+                       default:
+                               throw new Error( 'Unknown component' );
+               }
+
+               while ( newDate < this.min ) {
+                       newDate = this.formatter.adjustComponent( newDate, data.dayComponent, 1, 'overflow' );
+               }
+               while ( newDate > this.max ) {
+                       newDate = this.formatter.adjustComponent( newDate, data.dayComponent, -1, 'overflow' );
+               }
+
+               return newDate;
+       };
+
+       /**
+        * Update the user interface
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.updateUI = function () {
+               var r, c, row, day, k, $cell,
+                       width = this.minWidth,
+                       nullCols = [],
+                       focusedDate = this.getFocusedDate(),
+                       selected = this.getSelected(),
+                       datePartIsEqual = this.formatter.datePartIsEqual.bind( this.formatter ),
+                       isSelected = function ( dt ) {
+                               return datePartIsEqual( this, dt );
+                       };
+
+               this.calendarData = this.formatter.getCalendarData( focusedDate );
+
+               this.$header.text( this.calendarData.header );
+
+               for ( c = 0; c < this.colNullable.length; c++ ) {
+                       nullCols[ c ] = this.colNullable[ c ];
+                       if ( nullCols[ c ] ) {
+                               for ( r = 0; r < this.calendarData.rows.length; r++ ) {
+                                       if ( this.calendarData.rows[ r ][ c ] ) {
+                                               nullCols[ c ] = false;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               this.$tableBody.children().detach();
+               for ( r = 0; r < this.calendarData.rows.length; r++ ) {
+                       if ( !this.rows[ r ] ) {
+                               this.rows[ r ] = $( '<tr>' );
+                       } else {
+                               this.rows[ r ].children().detach();
+                       }
+                       this.$tableBody.append( this.rows[ r ] );
+                       row = this.calendarData.rows[ r ];
+                       for ( c = 0; c < row.length; c++ ) {
+                               day = row[ c ];
+                               if ( day === null ) {
+                                       k = 'empty-' + r + '-' + c;
+                                       if ( !this.buttons[ k ] ) {
+                                               this.buttons[ k ] = $( '<td>' );
+                                       }
+                                       $cell = this.buttons[ k ];
+                                       $cell.toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+                               } else {
+                                       k = ( day.extra ? day.extra : '' ) + day.display;
+                                       width = Math.max( width, day.display.length );
+                                       if ( !this.buttons[ k ] ) {
+                                               this.buttons[ k ] = new OO.ui.ButtonWidget( {
+                                                       $element: $( '<td>' ),
+                                                       classes: [
+                                                               'mw-widgets-datetime-calendarWidget-cell',
+                                                               day.extra ? 'mw-widgets-datetime-calendarWidget-extra' : ''
+                                                       ],
+                                                       framed: true,
+                                                       label: day.display,
+                                                       tabIndex: -1
+                                               } );
+                                               this.buttons[ k ].connect( this, { click: [ 'onDayClick', this.buttons[ k ] ] } );
+                                       }
+                                       this.buttons[ k ]
+                                               .setData( day.date )
+                                               .setDisabled( day.date < this.min || day.date > this.max );
+                                       $cell = this.buttons[ k ].$element;
+                                       $cell.toggleClass( 'mw-widgets-datetime-calendarWidget-focused',
+                                               this.formatter.datePartIsEqual( focusedDate, day.date ) );
+                                       $cell.toggleClass( 'mw-widgets-datetime-calendarWidget-selected',
+                                               selected.some( isSelected, day.date ) );
+                               }
+                               this.rows[ r ].append( $cell );
+                       }
+               }
+
+               for ( c = 0; c < this.cols.length; c++ ) {
+                       if ( nullCols[ c ] ) {
+                               this.cols[ c ].width( 0 );
+                       } else {
+                               this.cols[ c ].width( width + 'em' );
+                       }
+                       this.cols[ c ].toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+                       this.headings[ c ].toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+               }
+       };
+
+       /**
+        * Handles formatter 'local' flag changing
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onLocalChange = function () {
+               if ( this.formatter.localChangesDatePart( this.getFocusedDate() ) ) {
+                       this.emit( 'page', this.getFocusedDate() );
+               }
+
+               this.updateUI();
+       };
+
+       /**
+        * Handles previous button click
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onPrevClick = function () {
+               this.setFocusedDate( this.adjustDate( this.getFocusedDate(), 'month', -1 ) );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles next button click
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onNextClick = function () {
+               this.setFocusedDate( this.adjustDate( this.getFocusedDate(), 'month', 1 ) );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles day button click
+        *
+        * @protected
+        * @param {OO.ui.ButtonWidget} $button
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onDayClick = function ( $button ) {
+               this.setFocusedDate( $button.getData() );
+               this.setSelected( [ $button.getData() ] );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles document mouse down events.
+        *
+        * @protected
+        * @param {jQuery.Event} e Mouse down event
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onDocumentMouseDown = function ( e ) {
+               if ( this.$widget &&
+                       !OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
+                       !OO.ui.contains( this.$widget[ 0 ], e.target, true )
+               ) {
+                       this.toggle( false );
+               }
+       };
+
+       /**
+        * Handles key presses.
+        *
+        * @protected
+        * @param {jQuery.Event} e Key down event
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onKeyDown = function ( e ) {
+               var focusedDate = this.getFocusedDate();
+
+               if ( !this.isDisabled() ) {
+                       switch ( e.which ) {
+                               case OO.ui.Keys.ENTER:
+                               case OO.ui.Keys.SPACE:
+                                       this.setSelected( [ focusedDate ] );
+                                       return false;
+
+                               case OO.ui.Keys.LEFT:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'day', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.RIGHT:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'day', 1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.UP:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'week', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.DOWN:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'week', 1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.PAGEUP:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'month', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.PAGEDOWN:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'month', 1 ) );
+                                       return false;
+                       }
+               }
+       };
+
+       /**
+        * Handles focusout events in dependent mode
+        *
+        * @private
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onFocusOut = function () {
+               setTimeout( this.checkFocusHandler );
+       };
+
+       /**
+        * When we or our widget lost focus, check if the calendar should be hidden.
+        *
+        * @private
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.checkFocus = function () {
+               var containers = [ this.$element[ 0 ], this.$widget[ 0 ] ],
+                       activeElement = document.activeElement;
+
+               if ( !activeElement || !OO.ui.contains( containers, activeElement, true ) ) {
+                       this.toggle( false );
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.toggle = function ( visible ) {
+               var change;
+
+               visible = ( visible === undefined ? !this.visible : !!visible );
+               change = visible !== this.isVisible();
+
+               // Parent method
+               mw.widgets.datetime.CalendarWidget[ 'super' ].prototype.toggle.call( this, visible );
+
+               if ( change ) {
+                       if ( visible ) {
+                               // Auto-hide
+                               if ( this.$widget ) {
+                                       this.getElementDocument().addEventListener(
+                                               'mousedown', this.onDocumentMouseDownHandler, true
+                                       );
+                               }
+                               this.updateUI();
+                       } else {
+                               this.getElementDocument().removeEventListener(
+                                       'mousedown', this.onDocumentMouseDownHandler, true
+                               );
+                       }
+               }
+
+               return this;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/CalendarWidget.less b/resources/src/mediawiki.widgets.datetime/CalendarWidget.less
new file mode 100644 (file)
index 0000000..a7beb0d
--- /dev/null
@@ -0,0 +1,74 @@
+@import "mediawiki.widgets.datetime.definitions";
+
+.mw-widgets-datetime-calendarWidget {
+       display: inline-block;
+       position: relative;
+       vertical-align: middle;
+       padding: .5em;
+
+       &.mw-widgets-datetime-calendarWidget-dependent {
+               display: block;
+               position: absolute;
+               z-index: 4;
+       }
+
+       &-grid {
+               table-layout: fixed;
+
+               .mw-widgets-datetime-calendarWidget-cell {
+                       display: table-cell;
+                       white-space: nowrap;
+               }
+       }
+
+       background-color: white;
+       border: 1px solid #ccc;
+
+       &.mw-widgets-datetime-calendarWidget-dependent {
+               margin-top: -1px;
+               border-top: 1px solid white;
+       }
+
+       &-heading {
+               text-align: center;
+               vertical-align: middle;
+               font-weight: bold;
+               white-space: nowrap;
+
+               .mw-widgets-datetime-calendarWidget-previous {
+                       float: left;
+               }
+               .mw-widgets-datetime-calendarWidget-next {
+                       float: right;
+               }
+       }
+
+       &-grid {
+               margin: 0 auto;
+
+               .mw-widgets-datetime-calendarWidget-cell {
+                       text-align: center;
+
+                       .oo-ui-buttonElement-button {
+                               width: 100%;
+                               border: 1px dotted rgba(255,255,255,0.0);
+                               .oo-ui-box-sizing( border-box );
+                       }
+
+                       &.mw-widgets-datetime-calendarWidget-extra .oo-ui-buttonElement-button .oo-ui-labelElement-label {
+                               color: #bbb;
+                       }
+
+                       &.mw-widgets-datetime-calendarWidget-selected .oo-ui-buttonElement-button {
+                               background-color: #def;
+                               .oo-ui-labelElement-label {
+                                       color: #38f;
+                               }
+                       }
+               }
+       }
+
+       &:focus &-grid &-cell&-focused .oo-ui-buttonElement-button {
+               border-color: rgba(0,0,0,0.3);
+       }
+}
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
new file mode 100644 (file)
index 0000000..1c54234
--- /dev/null
@@ -0,0 +1,623 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times.
+        *
+        * @class
+        * @abstract
+        * @mixins OO.EventEmitter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {string} [format='@default'] May be a key from the {@link #static-formats static formats},
+        *  or a format specification as defined by {@link #method-parseFieldSpec parseFieldSpec}
+        *  and {@link #method-getFieldForTag getFieldForTag}.
+        * @cfg {boolean} [local=false] Whether dates are local time or UTC
+        * @cfg {string[]} [fullZones] Time zone indicators. Array of 2 strings, for
+        *  UTC and local time.
+        * @cfg {string[]} [shortZones] Abbreviated time zone indicators. Array of 2
+        *  strings, for UTC and local time.
+        * @cfg {Date} [defaultDate] Default date, for filling unspecified components.
+        *  Defaults to the current date and time (with 0 milliseconds).
+        */
+       mw.widgets.datetime.DateTimeFormatter = function MwWidgetsDatetimeDateTimeFormatter( config ) {
+               var statick = this.constructor[ 'static' ];
+
+               statick.setupDefaults();
+
+               config = $.extend( {
+                       format: '@default',
+                       local: false,
+                       fullZones: statick.fullZones,
+                       shortZones: statick.shortZones
+               }, config );
+
+               // Mixin constructors
+               OO.EventEmitter.call( this );
+
+               // Properties
+               if ( statick.formats[ config.format ] ) {
+                       this.format = statick.formats[ config.format ];
+               } else {
+                       this.format = config.format;
+               }
+               this.local = !!config.local;
+               this.fullZones = config.fullZones;
+               this.shortZones = config.shortZones;
+               if ( config.defaultDate instanceof Date ) {
+                       this.defaultDate = config.defaultDate;
+               } else {
+                       this.defaultDate = new Date();
+                       if ( this.local ) {
+                               this.defaultDate.setMilliseconds( 0 );
+                       } else {
+                               this.defaultDate.setUTCMilliseconds( 0 );
+                       }
+               }
+       };
+
+       /* Setup */
+
+       OO.initClass( mw.widgets.datetime.DateTimeFormatter );
+       OO.mixinClass( mw.widgets.datetime.DateTimeFormatter, OO.EventEmitter );
+
+       /* Static */
+
+       /**
+        * Default format specifications. See the {@link #format format} parameter.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].formats = {};
+
+       /**
+        * Default time zone indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].fullZones = null;
+
+       /**
+        * Default abbreviated time zone indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].shortZones = null;
+
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].setupDefaults = function () {
+               if ( !this.fullZones ) {
+                       this.fullZones = [
+                               mw.msg( 'timezone-utc' ),
+                               mw.msg( 'timezone-local' )
+                       ];
+               }
+               if ( !this.shortZones ) {
+                       this.shortZones = [
+                               'Z',
+                               this.fullZones[ 1 ].substr( 0, 1 ).toUpperCase()
+                       ];
+                       if ( this.shortZones[ 1 ] === 'Z' ) {
+                               this.shortZones[ 1 ] = 'L';
+                       }
+               }
+       };
+
+       /* Events */
+
+       /**
+        * A `local` event is emitted when the 'local' flag is changed.
+        *
+        * @event local
+        */
+
+       /* Methods */
+
+       /**
+        * Whether dates are in local time or UTC
+        *
+        * @return {boolean} True if local time
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getLocal = function () {
+               return this.local;
+       };
+
+       /**
+        * Toggle whether dates are in local time or UTC
+        *
+        * @param {boolean} [flag] Set the flag instead of toggling it
+        * @fires local
+        * @chainable
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.toggleLocal = function ( flag ) {
+               if ( flag === undefined ) {
+                       flag = !this.local;
+               } else {
+                       flag = !!flag;
+               }
+               if ( this.local !== flag ) {
+                       this.local = flag;
+                       this.emit( 'local', this.local );
+               }
+               return this;
+       };
+
+       /**
+        * Get the default date
+        *
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getDefaultDate = function () {
+               return new Date( this.defaultDate.getTime() );
+       };
+
+       /**
+        * Fetch the field specification array for this object.
+        *
+        * See {@link #parseFieldSpec parseFieldSpec} for details on the return value structure.
+        *
+        * @return {Array}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getFieldSpec = function () {
+               return this.parseFieldSpec( this.format );
+       };
+
+       /**
+        * Parse a format string into a field specification
+        *
+        * The input is a string containing tags formatted as ${tag|param|param...}
+        * (for editable fields) and $!{tag|param|param...} (for non-editable fields).
+        * Most tags are defined by {@link #getFieldForTag getFieldForTag}, but a few
+        * are defined here:
+        * - ${intercalary|X|text}: Text that is only displayed when the 'intercalary'
+        *   component is X.
+        * - ${not-intercalary|X|text}: Text that is displayed unless the 'intercalary'
+        *   component is X.
+        *
+        * Elements of the returned array are strings or objects. Strings are meant to
+        * be displayed as-is. Objects are as returned by {@link #getFieldForTag getFieldForTag}.
+        *
+        * @protected
+        * @param {string} format
+        * @return {Array}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.parseFieldSpec = function ( format ) {
+               var m, last, tag, params, spec,
+                       ret = [],
+                       re = /(.*?)(\$(!?)\{([^}]+)\})/g;
+
+               last = 0;
+               while ( ( m = re.exec( format ) ) !== null ) {
+                       last = re.lastIndex;
+
+                       if ( m[ 1 ] !== '' ) {
+                               ret.push( m[ 1 ] );
+                       }
+
+                       params = m[ 4 ].split( '|' );
+                       tag = params.shift();
+                       spec = this.getFieldForTag( tag, params );
+                       if ( spec ) {
+                               if ( m[ 3 ] === '!' ) {
+                                       spec.editable = false;
+                               }
+                               ret.push( spec );
+                       } else {
+                               ret.push( m[ 2 ] );
+                       }
+               }
+               if ( last < format.length ) {
+                       ret.push( format.substr( last ) );
+               }
+
+               return ret;
+       };
+
+       /**
+        * Turn a tag into a field specification object
+        *
+        * Fields implemented here are:
+        * - ${intercalary|X|text}: Text that is only displayed when the 'intercalary'
+        *   component is X.
+        * - ${not-intercalary|X|text}: Text that is displayed unless the 'intercalary'
+        *   component is X.
+        * - ${zone|#}: Timezone offset, "+0000" format.
+        * - ${zone|:}: Timezone offset, "+00:00" format.
+        * - ${zone|short}: Timezone from 'shortZones' configuration setting.
+        * - ${zone|full}: Timezone from 'fullZones' configuration setting.
+        *
+        * @protected
+        * @abstract
+        * @param {string} tag
+        * @param {string[]} params
+        * @return {Object|null} Field specification object, or null if the tag+params are unrecognized.
+        * @return {string|null} return.component Date component corresponding to this field, if any.
+        * @return {boolean} return.editable Whether this field is editable.
+        * @return {string} return.type What kind of field this is:
+        *  - 'static': The field is a static string; component will be null.
+        *  - 'number': The field is generally numeric.
+        *  - 'string': The field is generally textual.
+        *  - 'boolean': The field is a boolean.
+        *  - 'toggleLocal': The field represents {@link #getLocal this.getLocal()}.
+        *    Editing should directly call {@link #toggleLocal this.toggleLocal()}.
+        * @return {number} return.size Maximum number of characters in the field (when
+        *  the 'intercalary' component is falsey). If 0, the field should be hidden entirely.
+        * @return {Object.<string,number>} return.intercalarySize Map from
+        *  'intercalary' component values to overridden sizes.
+        * @return {string} return.value For type='static', the string to display.
+        * @return {function(Mixed): string} return.formatValue A function to format a
+        *  component value as a display string.
+        * @return {function(string): Mixed} return.parseValue A function to parse a
+        *  display string into a component value. If parsing fails, returns undefined.
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var c, spec = null;
+
+               switch ( tag ) {
+                       case 'intercalary':
+                       case 'not-intercalary':
+                               if ( params.length < 2 || !params[ 0 ] ) {
+                                       return null;
+                               }
+                               spec = {
+                                       component: null,
+                                       editable: false,
+                                       type: 'static',
+                                       value: params.slice( 1 ).join( '|' ),
+                                       size: 0,
+                                       intercalarySize: {}
+                               };
+                               if ( tag === 'intercalary' ) {
+                                       spec.intercalarySize[ params[ 0 ] ] = spec.value.length;
+                               } else {
+                                       spec.size = spec.value.length;
+                                       spec.intercalarySize[ params[ 0 ] ] = 0;
+                               }
+                               return spec;
+
+                       case 'zone':
+                               switch ( params[ 0 ] ) {
+                                       case '#':
+                                       case ':':
+                                               c = params[ 0 ] === '#' ? '' : ':';
+                                               return {
+                                                       component: 'zone',
+                                                       editable: true,
+                                                       type: 'toggleLocal',
+                                                       size: 5 + c.length,
+                                                       formatValue: function ( v ) {
+                                                               var o, r;
+                                                               if ( v ) {
+                                                                       o = new Date().getTimezoneOffset();
+                                                                       r = String( Math.abs( o ) % 60 );
+                                                                       while ( r.length < 2 ) {
+                                                                               r = '0' + r;
+                                                                       }
+                                                                       r = String( Math.floor( Math.abs( o ) / 60 ) ) + c + r;
+                                                                       while ( r.length < 4 + c.length ) {
+                                                                               r = '0' + r;
+                                                                       }
+                                                                       return ( o <= 0 ? '+' : '−' ) + r;
+                                                               } else {
+                                                                       return '+00' + c + '00';
+                                                               }
+                                                       },
+                                                       parseValue: function ( v ) {
+                                                               var m;
+                                                               v = String( v ).trim();
+                                                               if ( ( m = /^([+-−])([0-9]{1,2}):?([0-9]{2})$/.test( v ) ) ) {
+                                                                       return ( m[ 2 ] * 60 + m[ 3 ] ) * ( m[ 1 ] === '+' ? -1 : 1 );
+                                                               } else {
+                                                                       return undefined;
+                                                               }
+                                                       }
+                                               };
+
+                                       case 'short':
+                                       case 'full':
+                                               spec = {
+                                                       component: 'zone',
+                                                       editable: true,
+                                                       type: 'toggleLocal',
+                                                       values: params[ 0 ] === 'short' ? this.shortZones : this.fullZones,
+                                                       formatValue: this.formatSpecValue,
+                                                       parseValue: this.parseSpecValue
+                                               };
+                                               spec.size = Math.max.apply(
+                                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                                               );
+                                               return spec;
+                               }
+                               return null;
+
+                       default:
+                               return null;
+               }
+       };
+
+       /**
+        * Format a value for a field specification
+        *
+        * 'this' must be the field specification object. The intention is that you
+        * could just assign this function as the 'formatValue' for each field spec.
+        *
+        * Besides the publicly-documented fields, uses the following:
+        * - values: Enumerated values for the field
+        * - zeropad: Whether to pad the number with zeros.
+        *
+        * @protected
+        * @param {Mixed} v
+        * @return {string}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.formatSpecValue = function ( v ) {
+               if ( v === undefined || v === null ) {
+                       return '';
+               }
+
+               if ( typeof v === 'boolean' || this.type === 'toggleLocal' ) {
+                       v = v ? 1 : 0;
+               }
+
+               if ( this.values ) {
+                       return this.values[ v ];
+               }
+
+               v = String( v );
+               if ( this.zeropad ) {
+                       while ( v.length < this.size ) {
+                               v = '0' + v;
+                       }
+               }
+               return v;
+       };
+
+       /**
+        * Parse a value for a field specification
+        *
+        * 'this' must be the field specification object. The intention is that you
+        * could just assign this function as the 'parseValue' for each field spec.
+        *
+        * Besides the publicly-documented fields, uses the following:
+        * - values: Enumerated values for the field
+        *
+        * @protected
+        * @param {string} v
+        * @return {number|string|null}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.parseSpecValue = function ( v ) {
+               var k, re;
+
+               if ( v === '' ) {
+                       return null;
+               }
+
+               if ( !this.values ) {
+                       v = +v;
+                       if ( this.type === 'boolean' || this.type === 'toggleLocal' ) {
+                               return isNaN( v ) ? undefined : !!v;
+                       } else {
+                               return isNaN( v ) ? undefined : v;
+                       }
+               }
+
+               if ( v.normalize ) {
+                       v = v.normalize();
+               }
+               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ), 'i' );
+               for ( k in this.values ) {
+                       k = +k;
+                       if ( !isNaN( k ) && re.test( this.values[ k ] ) ) {
+                               if ( this.type === 'boolean' || this.type === 'toggleLocal' ) {
+                                       return !!k;
+                               } else {
+                                       return k;
+                               }
+                       }
+               }
+               return undefined;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Most specific components are defined by the subclass. "Global" components
+        * are:
+        * - intercalary: {string} Non-falsey values are used to indicate intercalary days.
+        * - zone: {number} Timezone offset in minutes.
+        *
+        * @abstract
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               // Should be overridden by subclass
+               return {
+                       zone: this.local ? date.getTimezoneOffset() : 0
+               };
+       };
+
+       /**
+        * Get a Date object from components
+        *
+        * @param {Object} components Date components
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getDateFromComponents = function ( /* components */ ) {
+               // Should be overridden by subclass
+               return new Date();
+       };
+
+       /**
+        * Adjust a date
+        *
+        * @param {Date|null} date To be adjusted
+        * @param {string} component To adjust
+        * @param {number} delta Adjustment amount
+        * @param {string} mode Adjustment mode:
+        *  - 'overflow': "Jan 32" => "Feb 1", "Jan 33" => "Feb 2", "Feb 0" => "Jan 31", etc.
+        *  - 'wrap': "Jan 32" => "Jan 1", "Jan 33" => "Jan 2", "Jan 0" => "Jan 31", etc.
+        *  - 'clip': "Jan 32" => "Jan 31", "Feb 32" => "Feb 28" (or 29), "Feb 0" => "Feb 1", etc.
+        * @return {Date} Adjusted date
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.adjustComponent = function ( date /*, component, delta, mode */ ) {
+               // Should be overridden by subclass
+               return date;
+       };
+
+       /**
+        * Get the column headings (weekday abbreviations) for a calendar grid
+        *
+        * Null-valued columns are hidden if getCalendarData() returns no "day" object
+        * for all days in that column.
+        *
+        * @abstract
+        * @return {Array} string or null
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getCalendarHeadings = function () {
+               // Should be overridden by subclass
+               return [];
+       };
+
+       /**
+        * Test whether two dates are in the same calendar grid
+        *
+        * @abstract
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               // Should be overridden by subclass
+               return date1.getTime() === date2.getTime();
+       };
+
+       /**
+        * Test whether the date parts of two Dates are equal
+        *
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.datePartIsEqual = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return (
+                               date1.getFullYear() === date2.getFullYear() &&
+                               date1.getMonth() === date2.getMonth() &&
+                               date1.getDate() === date2.getDate()
+                       );
+               } else {
+                       return (
+                               date1.getUTCFullYear() === date2.getUTCFullYear() &&
+                               date1.getUTCMonth() === date2.getUTCMonth() &&
+                               date1.getUTCDate() === date2.getUTCDate()
+                       );
+               }
+       };
+
+       /**
+        * Test whether the time parts of two Dates are equal
+        *
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.timePartIsEqual = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return (
+                               date1.getHours() === date2.getHours() &&
+                               date1.getMinutes() === date2.getMinutes() &&
+                               date1.getSeconds() === date2.getSeconds() &&
+                               date1.getMilliseconds() === date2.getMilliseconds()
+                       );
+               } else {
+                       return (
+                               date1.getUTCHours() === date2.getUTCHours() &&
+                               date1.getUTCMinutes() === date2.getUTCMinutes() &&
+                               date1.getUTCSeconds() === date2.getUTCSeconds() &&
+                               date1.getUTCMilliseconds() === date2.getUTCMilliseconds()
+                       );
+               }
+       };
+
+       /**
+        * Test whether toggleLocal() changes the date part
+        *
+        * @param {Date} date
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.localChangesDatePart = function ( date ) {
+               return (
+                       date.getUTCFullYear() !== date.getFullYear() ||
+                       date.getUTCMonth() !== date.getMonth() ||
+                       date.getUTCDate() !== date.getDate()
+               );
+       };
+
+       /**
+        * Create a new Date by merging the date part from one with the time part from
+        * another.
+        *
+        * @param {Date} datepart
+        * @param {Date} timepart
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.mergeDateAndTime = function ( datepart, timepart ) {
+               var ret = new Date( datepart.getTime() );
+
+               if ( this.local ) {
+                       ret.setHours(
+                               timepart.getHours(),
+                               timepart.getMinutes(),
+                               timepart.getSeconds(),
+                               timepart.getMilliseconds()
+                       );
+               } else {
+                       ret.setUTCHours(
+                               timepart.getUTCHours(),
+                               timepart.getUTCMinutes(),
+                               timepart.getUTCSeconds(),
+                               timepart.getUTCMilliseconds()
+                       );
+               }
+
+               return ret;
+       };
+
+       /**
+        * Get data for a calendar grid
+        *
+        * A "day" object is:
+        * - display: {string} Display text for the day.
+        * - date: {Date} Date to use when the day is selected.
+        * - extra: {string|null} 'prev' or 'next' on days used to fill out the weeks
+        *   at the start and end of the month.
+        *
+        * In any one result object, 'extra' + 'display' will always be unique.
+        *
+        * @abstract
+        * @param {Date|null} current Current date
+        * @return {Object} Data
+        * @return {string} return.header String to display as the calendar header
+        * @return {string} return.monthComponent Component to adjust by ±1 to change months.
+        * @return {string} return.dayComponent Component to adjust by ±1 to change days.
+        * @return {string} [return.weekComponent] Component to adjust by ±1 to change
+        *   weeks. If omitted, the dayComponent should be adjusted by ±the number of
+        *   non-nullable columns returned by this.getCalendarHeadings() to change weeks.
+        * @return {Array} return.rows Array of arrays of "day" objects or null/undefined.
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getCalendarData = function ( /* components */ ) {
+               // Should be overridden by subclass
+               return {
+                       header: '',
+                       monthComponent: 'month',
+                       dayComponent: 'day',
+                       rows: []
+               };
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
new file mode 100644 (file)
index 0000000..df148c7
--- /dev/null
@@ -0,0 +1,812 @@
+( function ( $, mw ) {
+
+       /**
+        * DateTimeInputWidgets can be used to input a date, a time, or a date and
+        * time, in either UTC or the user's local timezone.
+        * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+        *
+        * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+        *
+        *     @example
+        *     // Example of a text input widget
+        *     var dateTimeInput = new mw.widgets.datetime.DateTimeInputWidget( {} )
+        *     $( 'body' ).append( dateTimeInput.$element );
+        *
+        * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+        *
+        * @class
+        * @extends OO.ui.InputWidget
+        * @mixins OO.ui.mixin.IconElement
+        * @mixins OO.ui.mixin.IndicatorElement
+        * @mixins OO.ui.mixin.PendingElement
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {string} [type='datetime'] Whether to act like a 'date', 'time', or 'datetime' input.
+        *  Affects values stored in the relevant <input> and the formatting and
+        *  interpretation of values passed to/from getValue() and setValue(). It's up
+        *  to the user to configure the DateTimeFormatter correctly.
+        * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+        *  mw.widgets.datetime.ProlepticGregorianDateTimeFormatter (with 'format' defaulting to
+        *  '@date', '@time', or '@datetime' depending on 'type'), or an
+        *  mw.widgets.datetime.DateTimeFormatter instance to use.
+        * @cfg {Object|null} [calendar={}] Configuration options for
+        *  mw.widgets.datetime.CalendarWidget; note certain settings will be forced based on the
+        *  settings passed to this widget. Set null to disable the calendar.
+        * @cfg {boolean} [required=false] Whether a value is required.
+        * @cfg {boolean} [clearable=true] Whether to provide for blanking the value.
+        * @cfg {Date|null} [value=null] Default value for the widget
+        * @cfg {Date|string|null} [min=null] Minimum allowed date
+        * @cfg {Date|string|null} [max=null] Maximum allowed date
+        */
+       mw.widgets.datetime.DateTimeInputWidget = function MwWidgetsDatetimeDateTimeInputWidget( config ) {
+               // Configuration initialization
+               config = $.extend( {
+                       type: 'datetime',
+                       clearable: true,
+                       required: false,
+                       min: null,
+                       max: null,
+                       formatter: {},
+                       calendar: {}
+               }, config );
+
+               if ( $.isPlainObject( config.formatter ) && config.formatter.format === undefined ) {
+                       config.formatter.format = '@' + config.type;
+               }
+
+               // Parent constructor
+               mw.widgets.datetime.DateTimeInputWidget[ 'super' ].call( this, config );
+
+               // Mixin constructors
+               OO.ui.mixin.IconElement.call( this, config );
+               OO.ui.mixin.IndicatorElement.call( this, config );
+               OO.ui.mixin.PendingElement.call( this, config );
+
+               // Properties
+               this.type = config.type;
+               this.$handle = $( '<span>' );
+               this.$fields = $( '<span>' );
+               this.fields = [];
+               this.clearable = !!config.clearable;
+               this.required = !!config.required;
+
+               if ( typeof config.min === 'string' ) {
+                       config.min = this.parseDateValue( config.min );
+               }
+               if ( config.min instanceof Date && config.min.getTime() >= -62167219200000 ) {
+                       this.min = config.min;
+               } else {
+                       this.min = new Date( -62167219200000 ); // 0000-01-01T00:00:00.000Z
+               }
+
+               if ( typeof config.max === 'string' ) {
+                       config.max = this.parseDateValue( config.max );
+               }
+               if ( config.max instanceof Date && config.max.getTime() <= 253402300799999 ) {
+                       this.max = config.max;
+               } else {
+                       this.max = new Date( 253402300799999 ); // 9999-12-31T12:59:59.999Z
+               }
+
+               switch ( this.type ) {
+                       case 'date':
+                               this.min.setUTCHours( 0, 0, 0, 0 );
+                               this.max.setUTCHours( 23, 59, 59, 999 );
+                               break;
+                       case 'time':
+                               this.min.setUTCFullYear( 1970, 0, 1 );
+                               this.max.setUTCFullYear( 1970, 0, 1 );
+                               break;
+               }
+               if ( this.min > this.max ) {
+                       throw new Error(
+                               '"min" (' + this.min.toISOString() + ') must not be greater than ' +
+                               '"max" (' + this.max.toISOString() + ')'
+                       );
+               }
+
+               if ( config.formatter instanceof mw.widgets.datetime.DateTimeFormatter ) {
+                       this.formatter = config.formatter;
+               } else if ( $.isPlainObject( config.formatter ) ) {
+                       this.formatter = new mw.widgets.datetime.ProlepticGregorianDateTimeFormatter( config.formatter );
+               } else {
+                       throw new Error( '"formatter" must be an mw.widgets.datetime.DateTimeFormatter or a plain object' );
+               }
+
+               if ( this.type === 'time' || config.calendar === null ) {
+                       this.calendar = null;
+               } else {
+                       config.calendar = $.extend( {}, config.calendar, {
+                               formatter: this.formatter,
+                               widget: this,
+                               min: this.min,
+                               max: this.max
+                       } );
+                       this.calendar = new mw.widgets.datetime.CalendarWidget( config.calendar );
+               }
+
+               // Events
+               this.$handle.on( {
+                       click: this.onHandleClick.bind( this )
+               } );
+               this.connect( this, {
+                       change: 'onChange'
+               } );
+               this.formatter.connect( this, {
+                       local: 'onChange'
+               } );
+               if ( this.calendar ) {
+                       this.calendar.connect( this, {
+                               change: 'onCalendarChange'
+                       } );
+               }
+
+               // Initialization
+               this.setTabIndex( -1 );
+
+               this.$fields.addClass( 'mw-widgets-datetime-dateTimeInputWidget-fields' );
+               this.setupFields();
+
+               this.$handle
+                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget-handle' )
+                       .append( this.$icon, this.$indicator, this.$fields );
+
+               this.$element
+                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget' )
+                       .append( this.$handle );
+
+               if ( this.calendar ) {
+                       this.$element.append( this.calendar.$element );
+               }
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.InputWidget );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IconElement );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IndicatorElement );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.PendingElement );
+
+       /* Static properties */
+
+       mw.widgets.datetime.DateTimeInputWidget[ 'static' ].supportsSimpleLabel = false;
+
+       /* Events */
+
+       /* Methods */
+
+       /**
+        * Convert a date string to a Date
+        *
+        * @private
+        * @param {string} value
+        * @return {Date|null}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.parseDateValue = function ( value ) {
+               var date, m;
+
+               value = String( value );
+               switch ( this.type ) {
+                       case 'date':
+                               value = value + 'T00:00:00Z';
+                               break;
+                       case 'time':
+                               value = '1970-01-01T' + value + 'Z';
+                               break;
+               }
+               m = /^(\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?Z$/.exec( value );
+               if ( m ) {
+                       if ( m[ 7 ] ) {
+                               while ( m[ 7 ].length < 3 ) {
+                                       m[ 7 ] += '0';
+                               }
+                       } else {
+                               m[ 7 ] = 0;
+                       }
+                       date = new Date();
+                       date.setUTCFullYear( m[ 1 ], m[ 2 ] - 1, m[ 3 ] );
+                       date.setUTCHours( m[ 4 ], m[ 5 ], m[ 6 ], m[ 7 ] );
+                       if ( date.getTime() < -62167219200000 || date.getTime() > 253402300799999 ||
+                               date.getUTCFullYear() !== +m[ 1 ] ||
+                               date.getUTCMonth() + 1 !== +m[ 2 ] ||
+                               date.getUTCDate() !== +m[ 3 ] ||
+                               date.getUTCHours() !== +m[ 4 ] ||
+                               date.getUTCMinutes() !== +m[ 5 ] ||
+                               date.getUTCSeconds() !== +m[ 6 ] ||
+                               date.getUTCMilliseconds() !== +m[ 7 ]
+                       ) {
+                               date = null;
+                       }
+               } else {
+                       date = null;
+               }
+
+               return date;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.cleanUpValue = function ( value ) {
+               var date, pad;
+
+               if ( value === '' ) {
+                       return '';
+               }
+
+               if ( value instanceof Date ) {
+                       date = value;
+               } else {
+                       date = this.parseDateValue( value );
+               }
+
+               if ( date instanceof Date ) {
+                       pad = function ( v, l ) {
+                               v = String( v );
+                               while ( v.length < l ) {
+                                       v = '0' + v;
+                               }
+                               return v;
+                       };
+
+                       switch ( this.type ) {
+                               case 'date':
+                                       value = pad( date.getUTCFullYear(), 4 ) +
+                                               '-' + pad( date.getUTCMonth() + 1, 2 ) +
+                                               '-' + pad( date.getUTCDate(), 2 );
+                                       break;
+
+                               case 'time':
+                                       value = pad( date.getUTCHours(), 2 ) +
+                                               ':' + pad( date.getUTCMinutes(), 2 ) +
+                                               ':' + pad( date.getUTCSeconds(), 2 ) +
+                                               '.' + pad( date.getUTCMilliseconds(), 3 );
+                                       value = value.replace( /\.?0+$/, '' );
+                                       break;
+
+                               default:
+                                       value = date.toISOString();
+                                       break;
+                       }
+               } else {
+                       value = '';
+               }
+
+               return value;
+       };
+
+       /**
+        * Get the value of the input as a Date object
+        *
+        * @return {Date|null}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getValueAsDate = function () {
+               return this.parseDateValue( this.getValue() );
+       };
+
+       /**
+        * Set up the UI fields
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.setupFields = function () {
+               var i, $field, spec, placeholder, sz, maxlength,
+                       spanValFunc = function ( v ) {
+                               if ( v === undefined ) {
+                                       return this.data( 'mw-widgets-datetime-dateTimeInputWidget-value' );
+                               } else {
+                                       v = String( v );
+                                       this.data( 'mw-widgets-datetime-dateTimeInputWidget-value', v );
+                                       if ( v === '' ) {
+                                               v = this.data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder' );
+                                       }
+                                       this.text( v );
+                                       return this;
+                               }
+                       },
+                       reduceFunc = function ( k, v ) {
+                               maxlength = Math.max( maxlength, v );
+                       },
+                       disabled = this.isDisabled(),
+                       specs = this.formatter.getFieldSpec();
+
+               this.$fields.empty();
+               this.clearButton = null;
+               this.fields = [];
+
+               for ( i = 0; i < specs.length; i++ ) {
+                       spec = specs[ i ];
+                       if ( typeof spec === 'string' ) {
+                               $( '<span>' )
+                                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' )
+                                       .text( spec )
+                                       .appendTo( this.$fields );
+                               continue;
+                       }
+
+                       placeholder = '';
+                       while ( placeholder.length < spec.size ) {
+                               placeholder += '_';
+                       }
+
+                       if ( spec.type === 'number' ) {
+                               // Numbers ''should'' be the same width. But we need some extra for
+                               // IE, apparently.
+                               sz = ( spec.size * 1.15 ) + 'ch';
+                       } else {
+                               // Add a little for padding
+                               sz = ( spec.size * 1.15 ) + 'ch';
+                       }
+                       if ( spec.editable && spec.type !== 'static' ) {
+                               if ( spec.type === 'boolean' || spec.type === 'toggleLocal' ) {
+                                       $field = $( '<span>' )
+                                               .attr( {
+                                                       tabindex: disabled ? -1 : 0
+                                               } )
+                                               .width( sz )
+                                               .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder );
+                                       $field.on( {
+                                               keydown: this.onFieldKeyDown.bind( this, $field ),
+                                               focus: this.onFieldFocus.bind( this, $field ),
+                                               click: this.onFieldClick.bind( this, $field ),
+                                               'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field )
+                                       } );
+                                       $field.val = spanValFunc;
+                               } else {
+                                       maxlength = spec.size;
+                                       if ( spec.intercalarySize ) {
+                                               $.each( spec.intercalarySize, reduceFunc );
+                                       }
+                                       $field = $( '<input type="text">' )
+                                               .attr( {
+                                                       tabindex: disabled ? -1 : 0,
+                                                       size: spec.size,
+                                                       maxlength: maxlength
+                                               } )
+                                               .prop( {
+                                                       disabled: disabled,
+                                                       placeholder: placeholder
+                                               } )
+                                               .width( sz );
+                                       $field.on( {
+                                               keydown: this.onFieldKeyDown.bind( this, $field ),
+                                               click: this.onFieldClick.bind( this, $field ),
+                                               focus: this.onFieldFocus.bind( this, $field ),
+                                               blur: this.onFieldBlur.bind( this, $field ),
+                                               change: this.onFieldChange.bind( this, $field ),
+                                               'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field )
+                                       } );
+                               }
+                               $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-editField' );
+                       } else {
+                               $field = $( '<span>' )
+                                       .width( sz )
+                                       .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder );
+                               if ( spec.type === 'static' ) {
+                                       $field.text( spec.value );
+                               } else {
+                                       $field.val = spanValFunc;
+                               }
+                       }
+
+                       this.fields.push( $field );
+                       $field
+                               .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' )
+                               .data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec', spec )
+                               .appendTo( this.$fields );
+               }
+
+               if ( this.clearable ) {
+                       this.clearButton = new OO.ui.ButtonWidget( {
+                               classes: [ 'mw-widgets-datetime-dateTimeInputWidget-field', 'mw-widgets-datetime-dateTimeInputWidget-clearButton' ],
+                               framed: false,
+                               icon: 'remove',
+                               disabled: disabled
+                       } ).connect( this, {
+                               click: 'onClearClick'
+                       } );
+                       this.$fields.append( this.clearButton.$element );
+               }
+
+               this.updateFieldsFromValue();
+       };
+
+       /**
+        * Update the UI fields from the current value
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.updateFieldsFromValue = function () {
+               var i, $field, spec, intercalary, sz,
+                       date = this.getValueAsDate();
+
+               if ( date === null ) {
+                       this.components = null;
+
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               $field = this.fields[ i ];
+                               spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+                               $field
+                                       .removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid oo-ui-element-hidden' )
+                                       .val( '' );
+
+                               if ( spec.intercalarySize ) {
+                                       if ( spec.type === 'number' ) {
+                                               // Numbers ''should'' be the same width. But we need some extra for
+                                               // IE, apparently.
+                                               $field.width( ( spec.size * 1.15 ) + 'ch' );
+                                       } else {
+                                               // Add a little for padding
+                                               $field.width( ( spec.size * 1.15 ) + 'ch' );
+                                       }
+                               }
+                       }
+
+                       this.setFlags( { invalid: this.required } );
+               } else {
+                       this.components = this.formatter.getComponentsFromDate( date );
+                       intercalary = this.components.intercalary;
+
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               $field = this.fields[ i ];
+                               $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                               spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+                               if ( spec.type !== 'static' ) {
+                                       $field.val( spec.formatValue( this.components[ spec.component ] ) );
+                               }
+                               if ( spec.intercalarySize ) {
+                                       if ( intercalary && spec.intercalarySize[ intercalary ] !== undefined ) {
+                                               sz = spec.intercalarySize[ intercalary ];
+                                       } else {
+                                               sz = spec.size;
+                                       }
+                                       $field.toggleClass( 'oo-ui-element-hidden', sz <= 0 );
+                                       if ( spec.type === 'number' ) {
+                                               // Numbers ''should'' be the same width. But we need some extra for
+                                               // IE, apparently.
+                                               this.fields[ i ].width( ( sz * 1.15 ) + 'ch' );
+                                       } else {
+                                               // Add a little for padding
+                                               this.fields[ i ].width( ( sz * 1.15 ) + 'ch' );
+                                       }
+                               }
+                       }
+
+                       this.setFlags( { invalid: date < this.min || date > this.max } );
+               }
+
+               this.$element.toggleClass( 'mw-widgets-datetime-dateTimeInputWidget-empty', date === null );
+       };
+
+       /**
+        * Update the value with data from the UI fields
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.updateValueFromFields = function () {
+               var i, v, $field, spec, curDate, newDate,
+                       components = {},
+                       anyInvalid = false,
+                       anyEmpty = false,
+                       allEmpty = true;
+
+               for ( i = 0; i < this.fields.length; i++ ) {
+                       $field = this.fields[ i ];
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+                       if ( spec.editable ) {
+                               $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                               v = $field.val();
+                               if ( v === '' ) {
+                                       $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                                       anyEmpty = true;
+                               } else {
+                                       allEmpty = false;
+                                       v = spec.parseValue( v );
+                                       if ( v === undefined ) {
+                                               $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                                               anyInvalid = true;
+                                       } else {
+                                               components[ spec.component ] = v;
+                                       }
+                               }
+                       }
+               }
+
+               if ( allEmpty ) {
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               this.fields[ i ].removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                       }
+               } else if ( anyEmpty ) {
+                       anyInvalid = true;
+               }
+
+               if ( !anyInvalid ) {
+                       curDate = this.getValueAsDate();
+                       newDate = this.formatter.getDateFromComponents( components );
+                       if ( !curDate || !newDate || curDate.getTime() !== newDate.getTime() ) {
+                               this.setValue( newDate );
+                       }
+               }
+       };
+
+       /**
+        * Handle change event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onChange = function () {
+               var date;
+
+               this.updateFieldsFromValue();
+
+               if ( this.calendar ) {
+                       date = this.getValueAsDate();
+                       this.calendar.setSelected( date );
+                       if ( date ) {
+                               this.calendar.setFocusedDate( date );
+                       }
+               }
+       };
+
+       /**
+        * Handle clear button click event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onClearClick = function () {
+               this.blur();
+               this.setValue( '' );
+       };
+
+       /**
+        * Handle click on the widget background
+        *
+        * @private
+        * @param {jQuery.Event} e Click event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onHandleClick = function () {
+               this.focus();
+       };
+
+       /**
+        * Handle key down events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Key down event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldKeyDown = function ( $field, e ) {
+               var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( !this.isDisabled() ) {
+                       switch ( e.which ) {
+                               case OO.ui.Keys.ENTER:
+                               case OO.ui.Keys.SPACE:
+                                       if ( spec.type === 'boolean' ) {
+                                               this.setValue(
+                                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' )
+                                               );
+                                               return false;
+                                       } else if ( spec.type === 'toggleLocal' ) {
+                                               this.formatter.toggleLocal();
+                                       }
+                                       break;
+
+                               case OO.ui.Keys.UP:
+                               case OO.ui.Keys.DOWN:
+                                       if ( spec.type === 'toggleLocal' ) {
+                                               this.formatter.toggleLocal();
+                                       } else {
+                                               this.setValue(
+                                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component,
+                                                               e.keyCode === OO.ui.Keys.UP ? -1 : 1, 'wrap' )
+                                               );
+                                       }
+                                       if ( $field.is( ':input' ) ) {
+                                               $field.select();
+                                       }
+                                       return false;
+                       }
+               }
+       };
+
+       /**
+        * Handle focus events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Focus event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldFocus = function ( $field ) {
+               if ( !this.isDisabled() ) {
+                       if ( this.getValueAsDate() === null ) {
+                               this.setValue( this.formatter.getDefaultDate() );
+                       }
+                       if ( $field.is( ':input' ) ) {
+                               $field.select();
+                       }
+
+                       if ( this.calendar ) {
+                               this.calendar.toggle( true );
+                       }
+               }
+       };
+
+       /**
+        * Handle click events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Click event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldClick = function ( $field ) {
+               var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( !this.isDisabled() ) {
+                       if ( spec.type === 'boolean' ) {
+                               this.setValue(
+                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' )
+                               );
+                       } else if ( spec.type === 'toggleLocal' ) {
+                               this.formatter.toggleLocal();
+                       }
+               }
+       };
+
+       /**
+        * Handle blur events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Blur event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldBlur = function ( $field ) {
+               var v, date,
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               this.updateValueFromFields();
+
+               // Normalize
+               date = this.getValueAsDate();
+               if ( !date ) {
+                       $field.val( '' );
+               } else {
+                       v = spec.formatValue( this.formatter.getComponentsFromDate( date )[ spec.component ] );
+                       if ( v !== $field.val() ) {
+                               $field.val( v );
+                       }
+               }
+       };
+
+       /**
+        * Handle change events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Change event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldChange = function () {
+               this.updateValueFromFields();
+       };
+
+       /**
+        * Handle wheel events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Change event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldWheel = function ( $field, e ) {
+               var delta = 0,
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( this.isDisabled() ) {
+                       return;
+               }
+
+               // Standard 'wheel' event
+               if ( e.originalEvent.deltaMode !== undefined ) {
+                       this.sawWheelEvent = true;
+               }
+               if ( e.originalEvent.deltaY ) {
+                       delta = -e.originalEvent.deltaY;
+               } else if ( e.originalEvent.deltaX ) {
+                       delta = e.originalEvent.deltaX;
+               }
+
+               // Non-standard events
+               if ( !this.sawWheelEvent ) {
+                       if ( e.originalEvent.wheelDeltaX ) {
+                               delta = -e.originalEvent.wheelDeltaX;
+                       } else if ( e.originalEvent.wheelDeltaY ) {
+                               delta = e.originalEvent.wheelDeltaY;
+                       } else if ( e.originalEvent.wheelDelta ) {
+                               delta = e.originalEvent.wheelDelta;
+                       } else if ( e.originalEvent.detail ) {
+                               delta = -e.originalEvent.detail;
+                       }
+               }
+
+               if ( delta && spec ) {
+                       if ( spec.type === 'toggleLocal' ) {
+                               this.formatter.toggleLocal();
+                       } else {
+                               this.setValue(
+                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, delta < 0 ? -1 : 1, 'wrap' )
+                               );
+                       }
+                       return false;
+               }
+       };
+
+       /**
+        * Handle calendar change event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onCalendarChange = function () {
+               var curDate = this.getValueAsDate(),
+                       newDate = this.calendar.getSelected()[ 0 ];
+
+               if ( newDate ) {
+                       if ( !curDate || newDate.getTime() !== curDate.getTime() ) {
+                               this.setValue( newDate );
+                       }
+               }
+       };
+
+       /**
+        * @inheritdoc
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getInputElement = function () {
+               return $( '<input type="hidden" />' );
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.setDisabled = function ( disabled ) {
+               mw.widgets.datetime.DateTimeInputWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
+
+               // Flag all our fields as disabled
+               if ( this.$fields ) {
+                       this.$fields.find( 'input' ).prop( 'disabled', this.isDisabled() );
+                       this.$fields.find( '[tabindex]' ).attr( 'tabindex', this.isDisabled() ? -1 : 0 );
+               }
+
+               if ( this.clearButton ) {
+                       this.clearButton.setDisabled( disabled );
+               }
+
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.focus = function () {
+               if ( !this.$fields.find( document.activeElement ).length ) {
+                       this.$fields.find( '.mw-widgets-datetime-dateTimeInputWidget-editField' ).first().focus();
+               }
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.blur = function () {
+               this.$fields.find( document.activeElement ).blur();
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.simulateLabelClick = function () {
+               this.focus();
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less
new file mode 100644 (file)
index 0000000..bc387df
--- /dev/null
@@ -0,0 +1,155 @@
+@import "mediawiki.widgets.datetime.definitions";
+
+.mw-widgets-datetime-dateTimeInputWidget {
+       display: inline-block;
+       position: relative;
+       vertical-align: middle;
+
+       &-fields {
+               position: relative;
+               display: table;
+               z-index: 2;
+               .oo-ui-unselectable();
+
+               > .mw-widgets-datetime-dateTimeInputWidget-field {
+                       .oo-ui-box-sizing(border-box);
+
+                       display: table-cell;
+                       white-space: pre;
+               }
+       }
+
+       &-handle {
+               width: 100%;
+               display: inline-block;
+               overflow: hidden;
+
+               // Needed for proper behavior with overflow: hidden.
+               vertical-align: bottom;
+
+               .oo-ui-unselectable();
+               .oo-ui-box-sizing(border-box);
+
+               > .oo-ui-indicatorElement-indicator,
+               > .oo-ui-iconElement-icon {
+                       position: absolute;
+                       background-position: center center;
+                       background-repeat: no-repeat;
+                       z-index: 1;
+               }
+       }
+
+       margin: 0.25em 0;
+       width: 100%;
+       max-width: 50em;
+
+       .oo-ui-inline-spacing(0.5em);
+
+       &-handle {
+               height: 2.5em;
+               border: 1px solid #ccc;
+               padding: 0 1em;
+               margin: 0;
+               background-color: #fff;
+               color: black;
+               border: solid 1px #ccc;
+               box-shadow: inset 0 0 0 0 @progressive;
+               border-radius: 0.1em;
+               .oo-ui-transition(box-shadow @quick-ease);
+               .oo-ui-box-sizing(border-box);
+
+               > .oo-ui-indicatorElement-indicator {
+                       right: 0;
+               }
+
+               > .oo-ui-iconElement-icon {
+                       left: 0.25em;
+               }
+
+               > .oo-ui-indicatorElement-indicator {
+                       top: 0;
+                       width: @indicator-size;
+                       height: @indicator-size;
+                       margin: 0.775em;
+               }
+
+               > .oo-ui-iconElement-icon {
+                       top: 0;
+                       width: @icon-size;
+                       height: @icon-size;
+                       margin: 0.3em;
+               }
+       }
+
+       &-empty &-handle {
+               color: #777;
+       }
+
+       &-field {
+               padding: 0;
+               margin: 0;
+               font-size: inherit;
+               font-family: inherit;
+               background-color: transparent;
+               color: inherit;
+               border: none;
+               box-shadow: none;
+               text-align: center;
+               vertical-align: middle;
+               .oo-ui-box-sizing(border-box);
+       }
+
+       &.oo-ui-widget-disabled {
+               .mw-widgets-datetime-dateTimeInputWidget-handle {
+                       color: #ccc;
+                       text-shadow: 0 1px 1px #fff;
+                       border-color: #ddd;
+                       background-color: #f3f3f3;
+
+                       > .oo-ui-iconElement-icon,
+                       > .oo-ui-indicatorElement-indicator {
+                               opacity: 0.2;
+                       }
+               }
+       }
+
+       &.oo-ui-widget-enabled {
+               .mw-widgets-datetime-dateTimeInputWidget-editField:hover {
+                       background-color: #eee;
+               }
+
+               &.oo-ui-flaggedElement-invalid {
+                       .mw-widgets-datetime-dateTimeInputWidget-handle {
+                               border-color: red;
+                               box-shadow: inset 0 0 0 0 red;
+                       }
+
+                       .mw-widgets-datetime-dateTimeInputWidget-handle:focus {
+                               border-color: red;
+                               box-shadow: inset 0 0 0 0.1em red;
+                       }
+               }
+       }
+
+       input.mw-widgets-datetime-dateTimeInputWidget-field {
+               padding: 0.5em 0;
+       }
+
+       &-editField.mw-widgets-datetime-dateTimeInputWidget-invalid {
+               border: 1px solid red;
+               box-shadow: inset 0 0 0 0 red;
+
+               &:focus {
+                       border: 1px solid red;
+                       box-shadow: inset 0 0 0 0.1em red;
+               }
+       }
+
+       &.oo-ui-iconElement .mw-widgets-datetime-dateTimeInputWidget-handle {
+               padding-left: 3em;
+       }
+
+       &.oo-ui-indicatorElement .mw-widgets-datetime-dateTimeInputWidget-handle {
+               padding-right: 2em;
+       }
+}
diff --git a/resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js
new file mode 100644 (file)
index 0000000..fbf3238
--- /dev/null
@@ -0,0 +1,562 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times. This
+        * implementation implments the [Discordian calendar][1], mainly for testing with
+        * something very different from the usual Gregorian calendar.
+        *
+        * Being intended mainly for testing, niceties like i18n and better
+        * configurability have been omitted.
+        *
+        * [1]: https://en.wikipedia.org/wiki/Discordian_calendar
+        *
+        * @class
+        * @extends mw.widgets.datetime.DateTimeFormatter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter = function MwWidgetsDatetimeDiscordianDateTimeFormatter( config ) {
+               config = $.extend( {}, config );
+
+               // Parent constructor
+               mw.widgets.datetime.DiscordianDateTimeFormatter[ 'super' ].call( this, config );
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.DiscordianDateTimeFormatter, mw.widgets.datetime.DateTimeFormatter );
+
+       /* Static */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter[ 'static' ].formats = {
+               '@time': '${hour|0}:${minute|0}:${second|0}',
+               '@date': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#}',
+               '@datetime': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}',
+               '@default': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
+       };
+
+       /* Methods */
+
+       /**
+        * @inheritdoc
+        *
+        * Additional fields implemented here are:
+        * - ${year|#}: Year as a number
+        * - ${season|#}: Season as a number
+        * - ${season|full}: Season as a string
+        * - ${day|#}: Day of the month as a number
+        * - ${day|0}: Day of the month as a number with leading 0
+        * - ${dow|full}: Day of the week as a string
+        * - ${hour|#}: Hour as a number
+        * - ${hour|0}: Hour as a number with leading 0
+        * - ${minute|#}: Minute as a number
+        * - ${minute|0}: Minute as a number with leading 0
+        * - ${second|#}: Second as a number
+        * - ${second|0}: Second as a number with leading 0
+        * - ${millisecond|#}: Millisecond as a number
+        * - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var spec = null;
+
+               switch ( tag + '|' + params[ 0 ] ) {
+                       case 'year|#':
+                               spec = {
+                                       component: 'Year',
+                                       type: 'number',
+                                       size: 4,
+                                       zeropad: false
+                               };
+                               break;
+
+                       case 'season|#':
+                               spec = {
+                                       component: 'Season',
+                                       type: 'number',
+                                       size: 1,
+                                       intercalarySize: { 1: 0 },
+                                       zeropad: false
+                               };
+                               break;
+
+                       case 'season|full':
+                               spec = {
+                                       component: 'Season',
+                                       type: 'string',
+                                       intercalarySize: { 1: 0 },
+                                       values: {
+                                               1: 'Chaos',
+                                               2: 'Discord',
+                                               3: 'Confusion',
+                                               4: 'Bureaucracy',
+                                               5: 'The Aftermath'
+                                       }
+                               };
+                               break;
+
+                       case 'dow|full':
+                               spec = {
+                                       component: 'DOW',
+                                       editable: false,
+                                       type: 'string',
+                                       intercalarySize: { 1: 0 },
+                                       values: {
+                                               '-1': 'N/A',
+                                               0: 'Sweetmorn',
+                                               1: 'Boomtime',
+                                               2: 'Pungenday',
+                                               3: 'Prickle-Prickle',
+                                               4: 'Setting Orange'
+                                       }
+                               };
+                               break;
+
+                       case 'day|#':
+                       case 'day|0':
+                               spec = {
+                                       component: 'Day',
+                                       type: 'string',
+                                       size: 2,
+                                       intercalarySize: { 1: 13 },
+                                       zeropad: params[ 0 ] === '0',
+                                       formatValue: function ( v ) {
+                                               if ( v === 'tib' ) {
+                                                       return 'St. Tib\'s Day';
+                                               }
+                                               return mw.widgets.datetime.DateTimeFormatter.prototype.formatSpecValue.call( this, v );
+                                       },
+                                       parseValue: function ( v ) {
+                                               if ( /^\s*(st.?\s*)?tib('?s)?(\s*day)?\s*$/i.test( v ) ) {
+                                                       return 'tib';
+                                               }
+                                               return mw.widgets.datetime.DateTimeFormatter.prototype.parseSpecValue.call( this, v );
+                                       }
+                               };
+                               break;
+
+                       case 'hour|#':
+                       case 'hour|0':
+                       case 'minute|#':
+                       case 'minute|0':
+                       case 'second|#':
+                       case 'second|0':
+                               spec = {
+                                       component: tag.charAt( 0 ).toUpperCase() + tag.slice( 1 ),
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'millisecond|#':
+                       case 'millisecond|0':
+                               spec = {
+                                       component: 'Millisecond',
+                                       type: 'number',
+                                       size: 3,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       default:
+                               return mw.widgets.datetime.DiscordianDateTimeFormatter[ 'super' ].prototype.getFieldForTag.call( this, tag, params );
+               }
+
+               if ( spec ) {
+                       if ( spec.editable === undefined ) {
+                               spec.editable = true;
+                       }
+                       if ( spec.component !== 'Day' ) {
+                               spec.formatValue = this.formatSpecValue;
+                               spec.parseValue = this.parseSpecValue;
+                       }
+                       if ( spec.values ) {
+                               spec.size = Math.max.apply(
+                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                               );
+                       }
+               }
+
+               return spec;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Components are:
+        * - Year {number}
+        * - Season {number} 1-5
+        * - Day {number|string} 1-73 or 'tib'
+        * - DOW {number} 0-4, or -1 on St. Tib's Day
+        * - Hour {number} 0-23
+        * - Minute {number} 0-59
+        * - Second {number} 0-59
+        * - Millisecond {number} 0-999
+        * - intercalary {string} '1' on St. Tib's Day
+        *
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               var ret, day, month,
+                       monthDays = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ];
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               if ( this.local ) {
+                       day = date.getDate();
+                       month = date.getMonth();
+                       ret = {
+                               Year: date.getFullYear() + 1166,
+                               Hour: date.getHours(),
+                               Minute: date.getMinutes(),
+                               Second: date.getSeconds(),
+                               Millisecond: date.getMilliseconds(),
+                               zone: date.getTimezoneOffset()
+                       };
+               } else {
+                       day = date.getUTCDate();
+                       month = date.getUTCMonth();
+                       ret = {
+                               Year: date.getUTCFullYear() + 1166,
+                               Hour: date.getUTCHours(),
+                               Minute: date.getUTCMinutes(),
+                               Second: date.getUTCSeconds(),
+                               Millisecond: date.getUTCMilliseconds(),
+                               zone: 0
+                       };
+               }
+
+               if ( month === 1 && day === 29 ) {
+                       ret.Season = 1;
+                       ret.Day = 'tib';
+                       ret.DOW = -1;
+                       ret.intercalary = '1';
+               } else {
+                       day = monthDays[ month ] + day - 1;
+                       ret.Season = Math.floor( day / 73 ) + 1;
+                       ret.Day = ( day % 73 ) + 1;
+                       ret.DOW = day % 5;
+                       ret.intercalary = '';
+               }
+
+               return ret;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.adjustComponent = function ( date, component, delta, mode ) {
+               return this.getDateFromComponents(
+                       this.adjustComponentInternal(
+                               this.getComponentsFromDate( date ), component, delta, mode
+                       )
+               );
+       };
+
+       /**
+        * Adjust the components directly
+        *
+        * @private
+        * @param {Object} components Modified in place
+        * @param {string} component
+        * @param {number} delta
+        * @param {string} mode
+        * @return {Object} components
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.adjustComponentInternal = function ( components, component, delta, mode ) {
+               var i, min, max, range, next, preTib, postTib, wasTib;
+
+               if ( delta === 0 ) {
+                       return components;
+               }
+
+               switch ( component ) {
+                       case 'Year':
+                               min = 1166;
+                               max = 11165;
+                               next = null;
+                               break;
+                       case 'Season':
+                               min = 1;
+                               max = 5;
+                               next = 'Year';
+                               break;
+                       case 'Week':
+                               if ( components.Day === 'tib' ) {
+                                       components.Day = 59; // Could choose either one...
+                                       components.Season = 1;
+                               }
+                               min = 1;
+                               max = 73;
+                               next = 'Season';
+                               break;
+                       case 'Day':
+                               min = 1;
+                               max = 73;
+                               next = 'Season';
+                               break;
+                       case 'Hour':
+                               min = 0;
+                               max = 23;
+                               next = 'Day';
+                               break;
+                       case 'Minute':
+                               min = 0;
+                               max = 59;
+                               next = 'Hour';
+                               break;
+                       case 'Second':
+                               min = 0;
+                               max = 59;
+                               next = 'Minute';
+                               break;
+                       case 'Millisecond':
+                               min = 0;
+                               max = 999;
+                               next = 'Second';
+                               break;
+                       default:
+                               return components;
+               }
+
+               switch ( mode ) {
+                       case 'overflow':
+                       case 'clip':
+                       case 'wrap':
+               }
+
+               if ( component === 'Day' ) {
+                       i = Math.abs( delta );
+                       delta = delta < 0 ? -1 : 1;
+                       preTib = delta > 0 ? 59 : 60;
+                       postTib = delta > 0 ? 60 : 59;
+                       while ( i-- > 0 ) {
+                               if ( components.Day === preTib && components.Season === 1 && this.isLeapYear( components.Year ) ) {
+                                       components.Day = 'tib';
+                               } else if ( components.Day === 'tib' ) {
+                                       components.Day = postTib;
+                                       components.Season = 1;
+                               } else {
+                                       components.Day += delta;
+                                       if ( components.Day < min ) {
+                                               switch ( mode ) {
+                                                       case 'overflow':
+                                                               components.Day = max;
+                                                               this.adjustComponentInternal( components, 'Season', -1, mode );
+                                                               break;
+                                                       case 'wrap':
+                                                               components.Day = max;
+                                                               break;
+                                                       case 'clip':
+                                                               components.Day = min;
+                                                               i = 0;
+                                                               break;
+                                               }
+                                       }
+                                       if ( components.Day > max ) {
+                                               switch ( mode ) {
+                                                       case 'overflow':
+                                                               components.Day = min;
+                                                               this.adjustComponentInternal( components, 'Season', 1, mode );
+                                                               break;
+                                                       case 'wrap':
+                                                               components.Day = min;
+                                                               break;
+                                                       case 'clip':
+                                                               components.Day = max;
+                                                               i = 0;
+                                                               break;
+                                               }
+                                       }
+                               }
+                       }
+               } else {
+                       if ( component === 'Week' ) {
+                               component = 'Day';
+                               delta *= 5;
+                       }
+                       if ( components.Day === 'tib' ) {
+                               // For sanity
+                               components.Season = 1;
+                       }
+                       switch ( mode ) {
+                               case 'overflow':
+                                       if ( components.Day === 'tib' && ( component === 'Season' || component === 'Year' ) ) {
+                                               components.Day = 59; // Could choose either one...
+                                               wasTib = true;
+                                       } else {
+                                               wasTib = false;
+                                       }
+                                       i = Math.abs( delta );
+                                       delta = delta < 0 ? -1 : 1;
+                                       while ( i-- > 0 ) {
+                                               components[ component ] += delta;
+                                               if ( components[ component ] < min ) {
+                                                       components[ component ] = max;
+                                                       components = this.adjustComponentInternal( components, next, -1, mode );
+                                               }
+                                               if ( components[ component ] > max ) {
+                                                       components[ component ] = min;
+                                                       components = this.adjustComponentInternal( components, next, 1, mode );
+                                               }
+                                       }
+                                       if ( wasTib && components.Season === 1 && this.isLeapYear( components.Year ) ) {
+                                               components.Day = 'tib';
+                                       }
+                                       break;
+                               case 'wrap':
+                                       range = max - min + 1;
+                                       components[ component ] += delta;
+                                       while ( components[ component ] < min ) {
+                                               components[ component ] += range;
+                                       }
+                                       while ( components[ component ] > max ) {
+                                               components[ component ] -= range;
+                                       }
+                                       break;
+                               case 'clip':
+                                       components[ component ] += delta;
+                                       if ( components[ component ] < min ) {
+                                               components[ component ] = min;
+                                       }
+                                       if ( components[ component ] > max ) {
+                                               components[ component ] = max;
+                                       }
+                                       break;
+                       }
+                       if ( components.Day === 'tib' &&
+                               ( components.Season !== 1 || !this.isLeapYear( components.Year ) )
+                       ) {
+                               components.Day = 59; // Could choose either one...
+                       }
+               }
+
+               return components;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getDateFromComponents = function ( components ) {
+               var month, day, days,
+                       date = new Date(),
+                       monthDays = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ];
+
+               components = $.extend( {}, this.getComponentsFromDate( null ), components );
+               if ( components.Day === 'tib' ) {
+                       month = 1;
+                       day = 29;
+               } else {
+                       days = components.Season * 73 + components.Day - 74;
+                       month = 0;
+                       while ( days >= monthDays[ month + 1 ] ) {
+                               month++;
+                       }
+                       day = days - monthDays[ month ] + 1;
+               }
+
+               if ( components.zone ) {
+                       // Can't just use the constructor because that's stupid about ancient years.
+                       date.setFullYear( components.Year - 1166, month, day );
+                       date.setHours( components.Hour, components.Minute, components.Second, components.Millisecond );
+               } else {
+                       // Date.UTC() is stupid about ancient years too.
+                       date.setUTCFullYear( components.Year - 1166, month, day );
+                       date.setUTCHours( components.Hour, components.Minute, components.Second, components.Millisecond );
+               }
+
+               return date;
+       };
+
+       /**
+        * Get whether the year is a leap year
+        *
+        * @private
+        * @param {number} year
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.isLeapYear = function ( year ) {
+               year -= 1166;
+               if ( year % 4 ) {
+                       return false;
+               } else if ( year % 100 ) {
+                       return true;
+               }
+               return ( year % 400 ) === 0;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getCalendarHeadings = function () {
+               return [ 'SM', 'BT', 'PD', 'PP', null, 'SO' ];
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               var components1 = this.getComponentsFromDate( date1 ),
+                       components2 = this.getComponentsFromDate( date2 );
+
+               return components1.Year === components2.Year && components1.Season === components2.Season;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getCalendarData = function ( date ) {
+               var dt, components, season, i, row,
+                       ret = {
+                               dayComponent: 'Day',
+                               weekComponent: 'Week',
+                               monthComponent: 'Season'
+                       },
+                       seasons = [ 'Chaos', 'Discord', 'Confusion', 'Bureaucracy', 'The Aftermath' ],
+                       seasonStart = [ 0, -3, -1, -4, -2 ];
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               components = this.getComponentsFromDate( date );
+               components.Day = 1;
+               season = components.Season;
+
+               ret.header = seasons[ season - 1 ] + ' ' + components.Year;
+
+               if ( seasonStart[ season - 1 ] ) {
+                       this.adjustComponentInternal( components, 'Day', seasonStart[ season - 1 ], 'overflow' );
+               }
+
+               ret.rows = [];
+               do {
+                       row = [];
+                       for ( i = 0; i < 6; i++ ) {
+                               dt = this.getDateFromComponents( components );
+                               row[ i ] = {
+                                       display: components.Day === 'tib' ? 'Tib' : String( components.Day ),
+                                       date: dt,
+                                       extra: components.Season < season ? 'prev' : components.Season > season ? 'next' : null
+                               };
+
+                               this.adjustComponentInternal( components, 'Day', 1, 'overflow' );
+                               if ( components.Day !== 'tib' && i === 3 ) {
+                                       row[ ++i ] = null;
+                               }
+                       }
+
+                       ret.rows.push( row );
+               } while ( components.Season === season );
+
+               return ret;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
new file mode 100644 (file)
index 0000000..f60b34b
--- /dev/null
@@ -0,0 +1,661 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times. This
+        * implementation implments the proleptic Gregorian calendar over years
+        * 0000–9999.
+        *
+        * @class
+        * @extends mw.widgets.datetime.DateTimeFormatter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {Object} [fullMonthNames] Mapping 1–12 to full month names.
+        * @cfg {Object} [shortMonthNames] Mapping 1–12 to abbreviated month names.
+        *  If {@link #fullMonthNames fullMonthNames} is given and this is not,
+        *  defaults to the first three characters from that setting.
+        * @cfg {Object} [fullDayNames] Mapping 0–6 to full day of week names. 0 is Sunday, 6 is Saturday.
+        * @cfg {Object} [shortDayNames] Mapping 0–6 to abbreviated day of week names. 0 is Sunday, 6 is Saturday.
+        *  If {@link #fullDayNames fullDayNames} is given and this is not, defaults to
+        *  the first three characters from that setting.
+        * @cfg {string[]} [dayLetters] Weekday column headers for a calendar. Array of 7 strings.
+        *  If {@link #fullDayNames fullDayNames} or {@link #shortDayNames shortDayNames}
+        *  are given and this is not, defaults to the first character from
+        *  shortDayNames.
+        * @cfg {string[]} [hour12Periods] AM and PM texts. Array of 2 strings, AM and PM.
+        * @cfg {number} [weekStartsOn=0] What day the week starts on: 0 is Sunday, 1 is Monday, 6 is Saturday.
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter = function MwWidgetsDatetimeProlepticGregorianDateTimeFormatter( config ) {
+               var statick = this.constructor[ 'static' ];
+
+               statick.setupDefaults();
+
+               config = $.extend( {
+                       weekStartsOn: 0,
+                       hour12Periods: statick.hour12Periods
+               }, config );
+
+               if ( config.fullMonthNames && !config.shortMonthNames ) {
+                       config.shortMonthNames = {};
+                       $.each( config.fullMonthNames, function ( k, v ) {
+                               config.shortMonthNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               if ( config.shortDayNames && !config.dayLetters ) {
+                       config.dayLetters = [];
+                       $.each( config.shortDayNames, function ( k, v ) {
+                               config.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( config.fullDayNames && !config.dayLetters ) {
+                       config.dayLetters = [];
+                       $.each( config.fullDayNames, function ( k, v ) {
+                               config.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( config.fullDayNames && !config.shortDayNames ) {
+                       config.shortDayNames = {};
+                       $.each( config.fullDayNames, function ( k, v ) {
+                               config.shortDayNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               config = $.extend( {
+                       fullMonthNames: statick.fullMonthNames,
+                       shortMonthNames: statick.shortMonthNames,
+                       fullDayNames: statick.fullDayNames,
+                       shortDayNames: statick.shortDayNames,
+                       dayLetters: statick.dayLetters
+               }, config );
+
+               // Parent constructor
+               mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].call( this, config );
+
+               // Properties
+               this.weekStartsOn = config.weekStartsOn % 7;
+               this.fullMonthNames = config.fullMonthNames;
+               this.shortMonthNames = config.shortMonthNames;
+               this.fullDayNames = config.fullDayNames;
+               this.shortDayNames = config.shortDayNames;
+               this.dayLetters = config.dayLetters;
+               this.hour12Periods = config.hour12Periods;
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, mw.widgets.datetime.DateTimeFormatter );
+
+       /* Static */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].formats = {
+               '@time': '${hour|0}:${minute|0}:${second|0}',
+               '@date': '$!{dow|short} ${day|#} ${month|short} ${year|#}',
+               '@datetime': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}',
+               '@default': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
+       };
+
+       /**
+        * Default full month names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].fullMonthNames = null;
+
+       /**
+        * Default abbreviated month names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].shortMonthNames = null;
+
+       /**
+        * Default full day of week names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].fullDayNames = null;
+
+       /**
+        * Default abbreviated day of week names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].shortDayNames = null;
+
+       /**
+        * Default day letters.
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].dayLetters = null;
+
+       /**
+        * Default AM/PM indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].hour12Periods = null;
+
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].setupDefaults = function () {
+               mw.widgets.datetime.DateTimeFormatter[ 'static' ].setupDefaults.call( this );
+
+               if ( this.fullMonthNames && !this.shortMonthNames ) {
+                       this.shortMonthNames = {};
+                       $.each( this.fullMonthNames, function ( k, v ) {
+                               this.shortMonthNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               if ( this.shortDayNames && !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.shortDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( this.fullDayNames && !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.fullDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( this.fullDayNames && !this.shortDayNames ) {
+                       this.shortDayNames = {};
+                       $.each( this.fullDayNames, function ( k, v ) {
+                               this.shortDayNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+
+               if ( !this.fullMonthNames ) {
+                       this.fullMonthNames = {
+                               1: mw.msg( 'january' ),
+                               2: mw.msg( 'february' ),
+                               3: mw.msg( 'march' ),
+                               4: mw.msg( 'april' ),
+                               5: mw.msg( 'may_long' ),
+                               6: mw.msg( 'june' ),
+                               7: mw.msg( 'july' ),
+                               8: mw.msg( 'august' ),
+                               9: mw.msg( 'september' ),
+                               10: mw.msg( 'october' ),
+                               11: mw.msg( 'november' ),
+                               12: mw.msg( 'december' )
+                       };
+               }
+               if ( !this.shortMonthNames ) {
+                       this.shortMonthNames = {
+                               1: mw.msg( 'jan' ),
+                               2: mw.msg( 'feb' ),
+                               3: mw.msg( 'mar' ),
+                               4: mw.msg( 'apr' ),
+                               5: mw.msg( 'may' ),
+                               6: mw.msg( 'jun' ),
+                               7: mw.msg( 'jul' ),
+                               8: mw.msg( 'aug' ),
+                               9: mw.msg( 'sep' ),
+                               10: mw.msg( 'oct' ),
+                               11: mw.msg( 'nov' ),
+                               12: mw.msg( 'dec' )
+                       };
+               }
+
+               if ( !this.fullDayNames ) {
+                       this.fullDayNames = {
+                               0: mw.msg( 'sunday' ),
+                               1: mw.msg( 'monday' ),
+                               2: mw.msg( 'tuesday' ),
+                               3: mw.msg( 'wednesday' ),
+                               4: mw.msg( 'thursday' ),
+                               5: mw.msg( 'friday' ),
+                               6: mw.msg( 'saturday' )
+                       };
+               }
+               if ( !this.shortDayNames ) {
+                       this.shortDayNames = {
+                               0: mw.msg( 'sun' ),
+                               1: mw.msg( 'mon' ),
+                               2: mw.msg( 'tue' ),
+                               3: mw.msg( 'wed' ),
+                               4: mw.msg( 'thu' ),
+                               5: mw.msg( 'fri' ),
+                               6: mw.msg( 'sat' )
+                       };
+               }
+               if ( !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.shortDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+
+               if ( !this.hour12Periods ) {
+                       this.hour12Periods = [
+                               mw.msg( 'period-am' ),
+                               mw.msg( 'period-pm' )
+                       ];
+               }
+       };
+
+       /* Methods */
+
+       /**
+        * @inheritdoc
+        *
+        * Additional fields implemented here are:
+        * - ${year|#}: Year as a number
+        * - ${year|0}: Year as a number, zero-padded to 4 digits
+        * - ${month|#}: Month as a number
+        * - ${month|0}: Month as a number with leading 0
+        * - ${month|short}: Month from 'shortMonthNames' configuration setting
+        * - ${month|full}: Month from 'fullMonthNames' configuration setting
+        * - ${day|#}: Day of the month as a number
+        * - ${day|0}: Day of the month as a number with leading 0
+        * - ${dow|short}: Day of the week from 'shortDayNames' configuration setting
+        * - ${dow|full}: Day of the week from 'fullDayNames' configuration setting
+        * - ${hour|#}: Hour as a number
+        * - ${hour|0}: Hour as a number with leading 0
+        * - ${hour|12}: Hour in a 12-hour clock as a number
+        * - ${hour|012}: Hour in a 12-hour clock as a number, with leading 0
+        * - ${hour|period}: Value from 'hour12Periods' configuration setting
+        * - ${minute|#}: Minute as a number
+        * - ${minute|0}: Minute as a number with leading 0
+        * - ${second|#}: Second as a number
+        * - ${second|0}: Second as a number with leading 0
+        * - ${millisecond|#}: Millisecond as a number
+        * - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var spec = null;
+
+               switch ( tag + '|' + params[ 0 ] ) {
+                       case 'year|#':
+                       case 'year|0':
+                               spec = {
+                                       component: 'year',
+                                       type: 'number',
+                                       size: 4,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'month|short':
+                       case 'month|full':
+                               spec = {
+                                       component: 'month',
+                                       type: 'string',
+                                       values: params[ 0 ] === 'short' ? this.shortMonthNames : this.fullMonthNames
+                               };
+                               break;
+
+                       case 'dow|short':
+                       case 'dow|full':
+                               spec = {
+                                       component: 'dow',
+                                       editable: false,
+                                       type: 'string',
+                                       values: params[ 0 ] === 'short' ? this.shortDayNames : this.fullDayNames
+                               };
+                               break;
+
+                       case 'month|#':
+                       case 'month|0':
+                       case 'day|#':
+                       case 'day|0':
+                       case 'hour|#':
+                       case 'hour|0':
+                       case 'minute|#':
+                       case 'minute|0':
+                       case 'second|#':
+                       case 'second|0':
+                               spec = {
+                                       component: tag,
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'hour|12':
+                       case 'hour|012':
+                               spec = {
+                                       component: 'hour12',
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '012'
+                               };
+                               break;
+
+                       case 'hour|period':
+                               spec = {
+                                       component: 'hour12period',
+                                       type: 'boolean',
+                                       values: this.hour12Periods
+                               };
+                               break;
+
+                       case 'millisecond|#':
+                       case 'millisecond|0':
+                               spec = {
+                                       component: 'millisecond',
+                                       type: 'number',
+                                       size: 3,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       default:
+                               return mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].prototype.getFieldForTag.call( this, tag, params );
+               }
+
+               if ( spec ) {
+                       if ( spec.editable === undefined ) {
+                               spec.editable = true;
+                       }
+                       spec.formatValue = this.formatSpecValue;
+                       spec.parseValue = this.parseSpecValue;
+                       if ( spec.values ) {
+                               spec.size = Math.max.apply(
+                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                               );
+                       }
+               }
+
+               return spec;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Components are:
+        * - year {number}
+        * - month {number} (1-12)
+        * - day {number} (1-31)
+        * - dow {number} (0-6, 0 is Sunday)
+        * - hour {number} (0-23)
+        * - hour12 {number} (1-12)
+        * - hour12period {boolean}
+        * - minute {number} (0-59)
+        * - second {number} (0-59)
+        * - millisecond {number} (0-999)
+        * - zone {number}
+        *
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               var ret;
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               if ( this.local ) {
+                       ret = {
+                               year: date.getFullYear(),
+                               month: date.getMonth() + 1,
+                               day: date.getDate(),
+                               dow: date.getDay() % 7,
+                               hour: date.getHours(),
+                               minute: date.getMinutes(),
+                               second: date.getSeconds(),
+                               millisecond: date.getMilliseconds(),
+                               zone: date.getTimezoneOffset()
+                       };
+               } else {
+                       ret = {
+                               year: date.getUTCFullYear(),
+                               month: date.getUTCMonth() + 1,
+                               day: date.getUTCDate(),
+                               dow: date.getUTCDay() % 7,
+                               hour: date.getUTCHours(),
+                               minute: date.getUTCMinutes(),
+                               second: date.getUTCSeconds(),
+                               millisecond: date.getUTCMilliseconds(),
+                               zone: 0
+                       };
+               }
+
+               ret.hour12period = ret.hour >= 12 ? 1 : 0;
+               ret.hour12 = ret.hour % 12;
+               if ( ret.hour12 === 0 ) {
+                       ret.hour12 = 12;
+               }
+
+               return ret;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDateFromComponents = function ( components ) {
+               var date = new Date();
+
+               components = $.extend( {}, components );
+               if ( components.hour === undefined && components.hour12 !== undefined && components.hour12period !== undefined ) {
+                       components.hour = ( components.hour12 % 12 ) + ( components.hour12period ? 12 : 0 );
+               }
+               components = $.extend( {}, this.getComponentsFromDate( null ), components );
+
+               if ( components.zone ) {
+                       // Can't just use the constructor because that's stupid about ancient years.
+                       date.setFullYear( components.year, components.month - 1, components.day );
+                       date.setHours( components.hour, components.minute, components.second, components.millisecond );
+               } else {
+                       // Date.UTC() is stupid about ancient years too.
+                       date.setUTCFullYear( components.year, components.month - 1, components.day );
+                       date.setUTCHours( components.hour, components.minute, components.second, components.millisecond );
+               }
+
+               return date;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.adjustComponent = function ( date, component, delta, mode ) {
+               var min, max, range, components;
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+               components = this.getComponentsFromDate( date );
+
+               switch ( component ) {
+                       case 'year':
+                               min = 0;
+                               max = 9999;
+                               break;
+                       case 'month':
+                               min = 1;
+                               max = 12;
+                               break;
+                       case 'day':
+                               min = 1;
+                               max = this.getDaysInMonth( components.month, components.year );
+                               break;
+                       case 'hour':
+                               min = 0;
+                               max = 23;
+                               break;
+                       case 'minute':
+                       case 'second':
+                               min = 0;
+                               max = 59;
+                               break;
+                       case 'millisecond':
+                               min = 0;
+                               max = 999;
+                               break;
+                       case 'hour12period':
+                               component = 'hour';
+                               min = 0;
+                               max = 23;
+                               delta *= 12;
+                               break;
+                       case 'hour12':
+                               component = 'hour';
+                               min = components.hour12period ? 12 : 0;
+                               max = components.hour12period ? 23 : 11;
+                               break;
+                       default:
+                               return new Date( date.getTime() );
+               }
+
+               components[ component ] += delta;
+               range = max - min + 1;
+               switch ( mode ) {
+                       case 'overflow':
+                               // Date() will mostly handle it automatically. But months need
+                               // manual handling to prevent e.g. Jan 31 => Mar 3.
+                               if ( component === 'month' || component === 'year' ) {
+                                       while ( components.month < 1 ) {
+                                               components[ component ] += 12;
+                                               components.year--;
+                                       }
+                                       while ( components.month > 12 ) {
+                                               components[ component ] -= 12;
+                                               components.year++;
+                                       }
+                               }
+                               break;
+                       case 'wrap':
+                               while ( components[ component ] < min ) {
+                                       components[ component ] += range;
+                               }
+                               while ( components[ component ] > max ) {
+                                       components[ component ] -= range;
+                               }
+                               break;
+                       case 'clip':
+                               if ( components[ component ] < min ) {
+                                       components[ component ] = min;
+                               }
+                               if ( components[ component ] < max ) {
+                                       components[ component ] = max;
+                               }
+                               break;
+               }
+               if ( component === 'month' || component === 'year' ) {
+                       components.day = Math.min( components.day, this.getDaysInMonth( components.month, components.year ) );
+               }
+
+               return this.getDateFromComponents( components );
+       };
+
+       /**
+        * Get the number of days in a month
+        *
+        * @protected
+        * @param {number} month
+        * @param {number} year
+        * @return {number}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDaysInMonth = function ( month, year ) {
+               switch ( month ) {
+                       case 4:
+                       case 6:
+                       case 9:
+                       case 11:
+                               return 30;
+                       case 2:
+                               if ( year % 4 ) {
+                                       return 28;
+                               } else if ( year % 100 ) {
+                                       return 29;
+                               }
+                               return ( year % 400 ) ? 28 : 29;
+                       default:
+                               return 31;
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarHeadings = function () {
+               var a = this.dayLetters;
+
+               if ( this.weekStartsOn ) {
+                       return a.slice( this.weekStartsOn ).concat( a.slice( 0, this.weekStartsOn ) );
+               } else {
+                       return a.slice( 0 ); // clone
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
+               } else {
+                       return date1.getUTCFullYear() === date2.getUTCFullYear() && date1.getUTCMonth() === date2.getUTCMonth();
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarData = function ( date ) {
+               var dt, t, d, e, i, row,
+                       getDate = this.local ? 'getDate' : 'getUTCDate',
+                       setDate = this.local ? 'setDate' : 'setUTCDate',
+                       ret = {
+                               dayComponent: 'day',
+                               monthComponent: 'month'
+                       };
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               dt = new Date( date.getTime() );
+               dt[ setDate ]( 1 );
+               t = dt.getTime();
+
+               if ( this.local ) {
+                       ret.header = this.fullMonthNames[ dt.getMonth() + 1 ] + ' ' + dt.getFullYear();
+                       d = dt.getDay() % 7;
+                       e = this.getDaysInMonth( dt.getMonth() + 1, dt.getFullYear() );
+               } else {
+                       ret.header = this.fullMonthNames[ dt.getUTCMonth() + 1 ] + ' ' + dt.getUTCFullYear();
+                       d = dt.getUTCDay() % 7;
+                       e = this.getDaysInMonth( dt.getUTCMonth() + 1, dt.getUTCFullYear() );
+               }
+
+               if ( this.weekStartsOn ) {
+                       d = ( d + 7 - this.weekStartsOn ) % 7;
+               }
+               d = 1 - d;
+
+               ret.rows = [];
+               while ( d <= e ) {
+                       row = [];
+                       for ( i = 0; i < 7; i++, d++ ) {
+                               dt = new Date( t );
+                               dt[ setDate ]( d );
+                               row[ i ] = {
+                                       display: String( dt[ getDate ]() ),
+                                       date: dt,
+                                       extra: d < 1 ? 'prev' : d > e ? 'next' : null
+                               };
+                       }
+                       ret.rows.push( row );
+               }
+
+               return ret;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less b/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less
new file mode 100644 (file)
index 0000000..ee0e66e
--- /dev/null
@@ -0,0 +1,37 @@
+/*!
+ * OOJS-UI defines used by the existing CSS (will make it easier to put this
+ * widget in OOJS-UI once OOJS-UI is capable of handling it)
+ */
+
+.oo-ui-box-sizing( @type: border-box ) {
+       -webkit-box-sizing: @type;
+       -moz-box-sizing: @type;
+       box-sizing: @type;
+}
+
+.oo-ui-unselectable() {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+       -moz-user-select: none;
+       -ms-user-select: none;
+       user-select: none;
+}
+
+.oo-ui-inline-spacing( @spacing, @cancelled-spacing: 0 ) {
+       margin-right: @spacing;
+       &:last-child {
+               margin-right: @cancelled-spacing;
+       }
+}
+
+.oo-ui-transition( @value1, @value2: X, ... ) {
+       @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+       -webkit-transition: @value;
+       -moz-transition: @value;
+       transition: @value;
+}
+
+@indicator-size: unit(12 / 16 / 0.8, em);
+@icon-size: unit(24 / 16 / 0.8, em);
+@quick-ease: 100ms ease;
+@progressive: #347bff;
diff --git a/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js b/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js
new file mode 100644 (file)
index 0000000..8d4be8c
--- /dev/null
@@ -0,0 +1,2 @@
+// Create the namespace object
+mediaWiki.widgets.datetime = {};
index 4385a2e..9b3458b 100644 (file)
@@ -36,7 +36,7 @@
 
        // Things outside the wikipage content
        $( function () {
-               var $nodes;
+               var $nodes, $oouiNodes;
 
                if ( !supportsPlaceholder ) {
                        // Exclude content to avoid hitting it twice for the (first) wikipage content
                $nodes.updateTooltipAccessKeys();
 
                // Infuse OOUI widgets, if any are present
-               $nodes = $( '[data-ooui]' );
-               if ( $nodes.length ) {
+               $oouiNodes = $( '[data-ooui]' );
+               if ( $oouiNodes.length ) {
                        // FIXME: We should only load the widgets that are being infused
                        mw.loader.using( [ 'mediawiki.widgets', 'mediawiki.widgets.UserInputWidget' ] ).done( function () {
-                               $nodes.each( function () {
+                               $oouiNodes.each( function () {
                                        OO.ui.infuse( this );
                                } );
                        } );
index cf65a0d..6a95205 100644 (file)
@@ -512,6 +512,7 @@ class ParserTest {
                        $ok = true;
 
                        foreach ( $filenames as $filename ) {
+                               echo "Running parser tests from: $filename\n";
                                $tests = new TestFileIterator( $filename, $this );
                                $ok = $this->runTests( $tests ) && $ok;
                        }