Merge "RCFilters UI: Adjust popup positioning again"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 15 Mar 2017 11:23:53 +0000 (11:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 15 Mar 2017 11:23:54 +0000 (11:23 +0000)
121 files changed:
.mailmap
CREDITS
RELEASE-NOTES-1.29
autoload.php
docs/hooks.txt
includes/DefaultSettings.php
includes/Revision.php
includes/Setup.php
includes/TemplateParser.php
includes/Title.php
includes/api/i18n/de.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/ko.json
includes/cache/MessageCache.php
includes/changes/ChangesList.php
includes/changes/ChangesListBooleanFilter.php [new file with mode: 0644]
includes/changes/ChangesListBooleanFilterGroup.php [new file with mode: 0644]
includes/changes/ChangesListFilter.php [new file with mode: 0644]
includes/changes/ChangesListFilterGroup.php [new file with mode: 0644]
includes/changes/ChangesListStringOptionsFilter.php [new file with mode: 0644]
includes/changes/ChangesListStringOptionsFilterGroup.php [new file with mode: 0644]
includes/changes/EnhancedChangesList.php
includes/installer/LocalSettingsGenerator.php
includes/installer/MssqlUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/br.json
includes/libs/filebackend/SwiftFileBackend.php
includes/objectcache/ObjectCache.php
includes/specialpage/ChangesListSpecialPage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialWatchlist.php
includes/widget/SearchInputWidget.php
includes/widget/search/InterwikiSearchResultSetWidget.php
languages/i18n/aeb-arab.json
languages/i18n/af.json
languages/i18n/ar.json
languages/i18n/arq.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ceb.json
languages/i18n/ckb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/en-gb.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/id.json
languages/i18n/ilo.json
languages/i18n/it.json
languages/i18n/ka.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/mg.json
languages/i18n/mk.json
languages/i18n/nan.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sco.json
languages/i18n/se.json
languages/i18n/sh.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/uk.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/archives/patch-image-user-index-2.sql [new file with mode: 0644]
maintenance/archives/patch-image-user-index.sql
maintenance/mssql/archives/patch-alter-table-oldimage.sql [new file with mode: 0644]
maintenance/mssql/tables.sql
maintenance/parse.php
maintenance/tables.sql
resources/Resources.php
resources/src/mediawiki.action/mediawiki.action.view.postEdit.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js
resources/src/mediawiki.special/mediawiki.special.search.interwikiwidget.styles.less
resources/src/mediawiki.special/mediawiki.special.search.styles.css
resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
resources/src/mediawiki/mediawiki.js
tests/common/TestsAutoLoader.php
tests/phpunit/includes/TemplateParserTest.php
tests/phpunit/includes/changes/ChangesListBooleanFilterGroupTest.php [new file with mode: 0644]
tests/phpunit/includes/changes/ChangesListBooleanFilterTest.php [new file with mode: 0644]
tests/phpunit/includes/changes/ChangesListStringOptionsFilterGroupTest.php [new file with mode: 0644]
tests/phpunit/includes/search/SearchIndexFieldTest.php
tests/phpunit/includes/specialpage/AbstractChangesListSpecialPageTestCase.php [new file with mode: 0644]
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php [new file with mode: 0644]
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js

index 232824d..f056219 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -32,6 +32,7 @@ addshore <addshorewiki@gmail.com>
 Aditya Sastry <ganeshaditya1@gmail.com>
 Adrian Heine <adrian.heine@wikimedia.de>
 Alex Z. <mrzmanwiki@gmail.com> <mrzman@users.mediawiki.org>
+Aleksey Bekh-Ivanov <aleksey.bekh-ivanov@wikimedia.de>
 Alexandre Emsenhuber <ialex.wiki@gmail.com>
 Alexandre Emsenhuber <ialex.wiki@gmail.com> <ialex@users.mediawiki.org>
 Alexandre Emsenhuber <ialex.wiki@gmail.com> <mediawiki@emsenhuber.ch>
@@ -131,6 +132,7 @@ Dereckson <dereckson@espace-win.org>
 Derk-Jan Hartman <hartman@videolan.org>
 Derk-Jan Hartman <hartman@videolan.org> <hartman.wiki@gmail.com>
 Derk-Jan Hartman <hartman@videolan.org> <hartman@users.mediawiki.org>
+Devi Krishnan <devikrishnan67@gmail.com>
 Diederik van Liere <dvanliere@gmail.com> <diederik@users.mediawiki.org>
 Domas Mituzas <domas.mituzas@gmail.com> <midom@users.mediawiki.org>
 Douglas Gardner <douglas@chippy.ch>
diff --git a/CREDITS b/CREDITS
index e8af23c..6a51e08 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1,5 +1,5 @@
 {{int:version-credits-summary}} <!--
-MediaWiki 1.28 is a collaborative project released under the
+MediaWiki 1.29 is a collaborative project released under the
 GNU General Public License v2. We would like to recognize the
 following names for their contribution to the product.
 
@@ -23,12 +23,14 @@ The following list can be found parsed under Special:Version/Credits -->
 * Adrian Heine
 * Adrian Lang
 * Ævar Arnfjörð Bjarmason
+* Aftab
 * Agbad
 * Ahmad Sherif
 * Ajayrahul P
 * Alangi Derick
 * Albert221
 * Alejandro Mery
+* Aleksey Bekh-Ivanov
 * AlephNull
 * Alex Ivanov
 * Alex Shih-Han Lin
@@ -42,6 +44,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Amalthea
 * Amir E. Aharoni
 * Amir Sarabadani
+* amritsreekumar
 * ananay
 * Anders Wegge Jakobsen
 * Andre Engels
@@ -70,6 +73,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Aryeh Gregor
 * Asher Feldman
 * Asier Lostalé
+* awu42
 * ayush_garg
 * Azliq7
 * Bagariavivek
@@ -86,6 +90,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Bill Traynor
 * Billinghurst
 * billm
+* blackspirit96
 * blotmandroid
 * Bogdan Stancescu
 * Boris Nagaev
@@ -116,9 +121,11 @@ The following list can be found parsed under Special:Version/Credits -->
 * Chris McMahon
 * Chris Seaton
 * Chris Steipp
+* Chrisludt
 * Christian Aistleitner
 * Christian List
 * Christian Neubauer
+* Christoph Jauera
 * Christopher Johnson
 * church of emacs
 * Cindy Cicalese
@@ -145,7 +152,9 @@ The following list can be found parsed under Special:Version/Credits -->
 * Darian Anthony Patrick
 * Darkdragon09
 * DaSch
+* datguy
 * David Baumgarten
+* David Causse
 * David Chan
 * David E. Narváez
 * David Lynch
@@ -161,18 +170,20 @@ The following list can be found parsed under Special:Version/Credits -->
 * Dévai Tamás
 * Devi Krishnan
 * Diederik van Liere
+* divadsn
 * Domas Mituzas
 * Douglas Gardner
 * DPStokesNZ
 * dr0ptp4kt
 * Ebrahim Byagowi
 * Ed Sanders
+* Eddie Greiner-Petter
 * Edward Chernenko
 * Edward Z. Yang
-* Eddie Greiner-Petter
 * Elisabeth Bauer
 * Elliott Eggleston
 * Elvis Stansvik
+* Emad Elwany
 * Emil Podlaszewski
 * Emmanuel Engelhart
 * Emmanuel Gil Peyrot
@@ -208,6 +219,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * georggi
 * Gergő Tisza
 * Gero Scholz
+* Ghybu
 * gicode
 * Giftpflanze
 * Gilles Dubuc
@@ -221,6 +233,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Grunny
 * Guillaume Blanchard
 * Guy Van den Broeck
+* Haikal Izzuddin
 * Happy-melon
 * haritha28
 * Harry Burt
@@ -260,6 +273,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Jaska Zedlik
 * Jason Richey
 * jeblad
+* Jeff Hobson
 * Jeff Janes
 * jeff303
 * Jens Frank
@@ -276,12 +290,15 @@ The following list can be found parsed under Special:Version/Credits -->
 * Jidanni
 * Jimmy Collins
 * Jimmy Xu
+* jo12bar
 * joakin
 * Joan Creus
 * Joel Natividad
+* Joel Sahleen
 * Joerg
 * Johan Dahlin
 * John Du Hart
+* John Erling Blad
 * John N
 * Jon Harald Søby
 * Jon Robson
@@ -294,6 +311,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * JuneHyeon Bae
 * Jure Kajzer
 * Justin Du
+* Kai Nissen
 * Kai_WMDE
 * kaligula
 * Kartik Mistry
@@ -330,6 +348,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Liam Edwards-Playne
 * liangent
 * Lisa Ridley
+* Liuxinyu970226
 * Ljudusika
 * Lojjik Braughler
 * Louperivois
@@ -339,6 +358,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Luigi Corsaro
 * Luis Felipe Schenone
 * Luke Faraone
+* Luke Welling
 * Lupin
 * Lupo
 * lwelling
@@ -367,6 +387,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Martin Urbanec
 * Massaf
 * Matěj Grabovský
+* Matěj Suchánek
 * matejsuchanek
 * Mathias Ertl
 * mati
@@ -376,6 +397,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Matthew Bowker
 * Matthew Britton
 * Matthew Flaschen
+* Matthew Walker
 * Matthias Jordan
 * Matthias Mullie
 * MatthiasDD
@@ -415,6 +437,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Mwalker
 * mwjames
 * mybugs.mail
+* mynk-96
 * MZMcBride
 * nadeesha
 * Nakon
@@ -427,8 +450,10 @@ The following list can be found parsed under Special:Version/Credits -->
 * Nicholas Pisarro, Jr
 * Nick Jenkins
 * nicoco007
+* Nicolaie Constantinescu
 * Nicolas Dumazet
 * Nicolas Weeger
+* Niharika Kohli
 * Nik
 * Nik Everett
 * Niklas Laxström
@@ -443,6 +468,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Nuria Ruiz
 * Nx.devnull
 * Ocean behind ears
+* Od1n
 * Olaf Lenz
 * Olivier Finlay Beaton
 * onei
@@ -464,6 +490,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Pavel Selitskas
 * Pcoombe
 * Perside Rosalie
+* Peter Coombe
 * Peter Gehres
 * Peter Hedenskog
 * Peter Potrowl
@@ -471,6 +498,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Petr Kadlec
 * Petr Onderka
 * Petr Pchelko
+* Phantom42
 * Philip Tzou
 * physikerwelt (Moritz Schubotz)
 * PieRRoMaN
@@ -479,6 +507,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Platonides
 * Pmlineditor
 * pmolina
+* pppery
 * prageck
 * Pranav Ravichandran
 * PranavK
@@ -501,6 +530,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Ricordisamoa
 * rillke
 * River Tarnell
+* rlot
 * Roan Kattouw
 * Rob Church
 * Rob Lanphier
@@ -532,7 +562,9 @@ The following list can be found parsed under Special:Version/Credits -->
 * Salvatore Ingala
 * Sam Reed
 * Sam Smith
+* Sam Wilson
 * Santhosh Thottingal
+* saptaks
 * Schnark
 * Scimonster
 * scnd
@@ -548,6 +580,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Shahyar
 * Shane Gibbons
 * Shane King
+* shanika
 * Shinjiman
 * shirayuki
 * Sidhant Gupta
@@ -570,7 +603,9 @@ The following list can be found parsed under Special:Version/Credits -->
 * Stephen Liang
 * Steve Sanbeg
 * Steven Roddis
+* Steven Walling
 * Str4nd
+* Subin Siby
 * Subramanya Sastry
 * Sumit Asthana
 * svip
@@ -607,9 +642,11 @@ The following list can be found parsed under Special:Version/Credits -->
 * Tomasz W. Kozlowski
 * Tomasz Wegrzanowski
 * tomek
+* Toni Hermoso Pulido
 * Tony Thomas
 * Tpt
 * Trevor Parscal
+* Trey Jones
 * TyA
 * Tychay
 * Tyler Anthony Romeo
@@ -621,6 +658,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * utkarsh95
 * Van de Bugger
 * Viačeslav
+* Victor Barbu
 * Victor Porton
 * Victor Vasiliev
 * victorbarbu
@@ -642,10 +680,12 @@ The following list can be found parsed under Special:Version/Credits -->
 * Yaron Koren
 * Yaroslav Melnychuk
 * Yesid Carrillo
+* Yifei He
 * Yogesh K S
 * Yongmin Hong
 * yoonghm
 * Yuri Astrakhan
+* Yuriy Shnitkovskiy
 * Yusuke Matsubara
 * Yuvi Panda
 * Zachary Hauri
@@ -653,6 +693,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Željko Filipin
 * Zhaofeng Li
 * Zhengzhu Feng
+* Zhuyifei1999
 * Zppix
 * محمد شعیب
 <!-- END CONTRIBUTOR LIST -->
index 5bc66fd..8eb1f89 100644 (file)
@@ -33,6 +33,8 @@ production.
   feature flag will likely be removed before 1.29 is released.
 * (T158474) "Unknown user" has been added to $wgReservedUsernames.
 * (T156983) $wgRateLimitsExcludedIPs now accepts CIDR ranges as well as single IPs.
+* $wgDummyLanguageCodes is deprecated. Additional language code mappings may be
+  added to $wgExtraLanguageCodes instead.
 
 === New features in 1.29 ===
 * (T5233) A cookie can now be set when a user is autoblocked, to track that user
@@ -54,6 +56,9 @@ production.
   This might affect some forms that used them and only worked because the
   attributes were not actually being set.
 * Expiry times can now be specified when users are added to user groups.
+* Completely new user interface for the RecentChanges page, which
+  structures filters into user-friendly groups.  This has corresponding
+  changes to how filters are registered by core and extensions.
 
 === External library changes in 1.29 ===
 
@@ -245,6 +250,15 @@ changes to languages because of Phabricator reports.
 * User::comparePasswords() (deprecated in 1.24) was removed.
 * ArchivedFile::getUserText() (deprecated in 1.23) was removed.
 * HTMLFileCache::newFromTitle() (deprecated in 1.24) was removed.
+* BREAKING CHANGE: Internal signature changes to ChangesListSpecialPage
+  and subclasses.  It should only break if you call buildMainQueryConds
+  (changed to buildQuery with new signature) or doMainQuery (new
+  signature).  Subclasses are likely to call at least doMainQuery
+  (possibly both), but other classes might too, because they were
+  public.
+
+  Also, some related hooks were deprecated, but this is not yet a
+  breaking change.
 
 == Compatibility ==
 
index 0f79323..4ffaa11 100644 (file)
@@ -237,7 +237,13 @@ $wgAutoloadLocalClasses = [
        'ChangeTagsRevisionList' => __DIR__ . '/includes/changetags/ChangeTagsRevisionList.php',
        'ChangesFeed' => __DIR__ . '/includes/changes/ChangesFeed.php',
        'ChangesList' => __DIR__ . '/includes/changes/ChangesList.php',
+       'ChangesListBooleanFilter' => __DIR__ . '/includes/changes/ChangesListBooleanFilter.php',
+       'ChangesListBooleanFilterGroup' => __DIR__ . '/includes/changes/ChangesListBooleanFilterGroup.php',
+       'ChangesListFilter' => __DIR__ . '/includes/changes/ChangesListFilter.php',
+       'ChangesListFilterGroup' => __DIR__ . '/includes/changes/ChangesListFilterGroup.php',
        'ChangesListSpecialPage' => __DIR__ . '/includes/specialpage/ChangesListSpecialPage.php',
+       'ChangesListStringOptionsFilter' => __DIR__ . '/includes/changes/ChangesListStringOptionsFilter.php',
+       'ChangesListStringOptionsFilterGroup' => __DIR__ . '/includes/changes/ChangesListStringOptionsFilterGroup.php',
        'ChannelFeed' => __DIR__ . '/includes/Feed.php',
        'CheckBadRedirects' => __DIR__ . '/maintenance/checkBadRedirects.php',
        'CheckComposerLockUpToDate' => __DIR__ . '/maintenance/checkComposerLockUpToDate.php',
index 846a073..149ee4b 100644 (file)
@@ -982,7 +982,9 @@ $rows: The data that will be rendered. May be a ResultWrapper instance or
 $unpatrolled: Whether or not we are showing unpatrolled changes.
 $watched: Whether or not the change is watched by the user.
 
-'ChangesListSpecialPageFilters': Called after building form options on pages
+'ChangesListSpecialPageFilters': DEPRECATED! Use 'ChangesListSpecialPageStructuredFilters'
+instead.
+Called after building form options on pages
 inheriting from ChangesListSpecialPage (in core: RecentChanges,
 RecentChangesLinked and Watchlist).
 $special: ChangesListSpecialPage instance
@@ -993,6 +995,15 @@ $special: ChangesListSpecialPage instance
 'ChangesListSpecialPageQuery': Called when building SQL query on pages
 inheriting from ChangesListSpecialPage (in core: RecentChanges,
 RecentChangesLinked and Watchlist).
+
+Do not use this to implement individual filters if they are compatible with the
+ChangesListFilter and ChangesListFilterGroup structure.
+
+Instead, use sub-classes of those classes, in conjunction with the
+ChangesListSpecialPageStructuredFilters hook.
+
+This hook can be used to implement filters that do not implement that structure,
+or custom behavior that is not an individual filter.
 $name: name of the special page, e.g. 'Watchlist'
 &$tables: array of tables to be queried
 &$fields: array of columns to select
@@ -1001,6 +1012,15 @@ $name: name of the special page, e.g. 'Watchlist'
 &$join_conds: join conditions for the tables
 $opts: FormOptions for this request
 
+'ChangesListSpecialPageStructuredFilters': Called to allow extensions to register
+filters for pages inheriting from ChangesListSpecialPage (in core: RecentChanges,
+RecentChangesLinked, and Watchlist).  Generally, you will want to construct
+new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects.  You can
+then either add them to existing ChangesListFilterGroup objects (accessed through
+$special->getFilterGroup), or create your own.  If you create new groups, you
+must register them with $special->registerFilterGroup.
+$special: ChangesListSpecialPage instance
+
 'ChangeTagAfterDelete': Called after a change tag has been deleted (that is,
 removed from all revisions and log entries to which it was applied). This gives
 extensions a chance to take it off their books.
@@ -1093,7 +1113,7 @@ $title: the Title in question
 a given content model name, but no entry for that model exists in
 $wgContentHandlers.
 Note: if your extension implements additional models via this hook, please
-use GetContentModels hook to make them known to core. 
+use GetContentModels hook to make them known to core.
 $modeName: the requested content model name
 &$handler: set this to a ContentHandler object, if desired.
 
@@ -3095,7 +3115,7 @@ use this to change some selection criteria or substitute a different title.
 &$title: If the hook returns false, a Title object to use instead of the
   result from the normal query
 
-'SpecialRecentChangesFilters': DEPRECATED! Use ChangesListSpecialPageFilters
+'SpecialRecentChangesFilters': DEPRECATED! Use ChangesListSpecialPageStructuredFilters
 instead.
 Called after building form options at RecentChanges.
 $special: the special page object
@@ -3108,8 +3128,8 @@ SpecialRecentChanges.
 &$extraOpts: array of added items, to which can be added
 $opts: FormOptions for this request
 
-'SpecialRecentChangesQuery': DEPRECATED! Use ChangesListSpecialPageQuery
-instead.
+'SpecialRecentChangesQuery': DEPRECATED! Use ChangesListSpecialPageStructuredFilters
+or ChangesListSpecialPageQuery instead.
 Called when building SQL query for SpecialRecentChanges and
 SpecialRecentChangesLinked.
 &$conds: array of WHERE conditionals for query
@@ -3211,7 +3231,7 @@ Special:Upload.
 $wgVersion: Current $wgVersion for you to use
 &$versionUrl: Raw url to link to (eg: release notes)
 
-'SpecialWatchlistFilters': DEPRECATED! Use ChangesListSpecialPageFilters
+'SpecialWatchlistFilters': DEPRECATED! Use ChangesListSpecialPageStructuredFilters
 instead.
 Called after building form options at Watchlist.
 $special: the special page object
@@ -3224,7 +3244,8 @@ SpecialWatchlist. Allows extensions to register custom values they have
 inserted to rc_type so they can be returned as part of the watchlist.
 &$nonRevisionTypes: array of values in the rc_type field of recentchanges table
 
-'SpecialWatchlistQuery': DEPRECATED! Use ChangesListSpecialPageQuery instead.
+'SpecialWatchlistQuery': DEPRECATED! Use ChangesListSpecialPageStructuredFilters
+or ChangesListSpecialPageQuery instead.
 Called when building sql query for SpecialWatchlist.
 &$conds: array of WHERE conditionals for query
 &$tables: array of tables to be queried
index dce6d3c..192ad08 100644 (file)
@@ -2904,6 +2904,14 @@ $wgExtraLanguageCodes = [
        'simple' => 'en', // Simple English
 ];
 
+/**
+ * Functionally the same as $wgExtraLanguageCodes, but deprecated. Instead of
+ * appending values to this array, append them to $wgExtraLanguageCodes.
+ *
+ * @deprecated since 1.29
+ */
+$wgDummyLanguageCodes = [];
+
 /**
  * Set this to true to replace Arabic presentation forms with their standard
  * forms in the U+0600-U+06FF block. This only works if $wgLanguageCode is
index 4b9435a..dca2e1b 100644 (file)
@@ -1240,8 +1240,9 @@ class Revision implements IDBAccessObject {
 
        /**
         * Get revision text associated with an old or archive row
-        * $row is usually an object from wfFetchRow(), both the flags and the text
-        * field must be included.
+        *
+        * Both the flags and the text field must be included. Including the old_id
+        * field will activate cache usage as long as the $wiki parameter is not set.
         *
         * @param stdClass $row The text data
         * @param string $prefix Table prefix (default 'old_')
@@ -1252,8 +1253,6 @@ class Revision implements IDBAccessObject {
         * @return string|false Text the text requested or false on failure
         */
        public static function getRevisionText( $row, $prefix = 'old_', $wiki = false ) {
-
-               # Get data
                $textField = $prefix . 'text';
                $flagsField = $prefix . 'flags';
 
@@ -1269,20 +1268,36 @@ class Revision implements IDBAccessObject {
                        return false;
                }
 
-               # Use external methods for external objects, text in table is URL-only then
+               // Use external methods for external objects, text in table is URL-only then
                if ( in_array( 'external', $flags ) ) {
                        $url = $text;
                        $parts = explode( '://', $url, 2 );
                        if ( count( $parts ) == 1 || $parts[1] == '' ) {
                                return false;
                        }
-                       $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
+
+                       if ( isset( $row->old_id ) && $wiki === false ) {
+                               // Make use of the wiki-local revision text cache
+                               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+                               $text = $cache->getWithSetCallback(
+                                       $cache->makeKey( 'revisiontext', 'textid', $row->old_id ),
+                                       self::getCacheTTL( $cache ),
+                                       function () use ( $url, $wiki ) {
+                                               // No negative caching per Revision::loadText()
+                                               return ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
+                                       },
+                                       [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
+                               );
+                       } else {
+                               $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
+                       }
                }
 
                // If the text was fetched without an error, convert it
                if ( $text !== false ) {
                        $text = self::decompressRevisionText( $text, $flags );
                }
+
                return $text;
        }
 
@@ -1559,15 +1574,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Lazy-load the revision's text.
-        * Currently hardcoded to the 'text' table storage engine.
+        * Get the text cache TTL
         *
-        * @return string|bool The revision's text, or false on failure
+        * @param WANObjectCache $cache
+        * @return integer
         */
-       private function loadText() {
+       private static function getCacheTTL( WANObjectCache $cache ) {
                global $wgRevisionCacheExpiry;
 
-               $cache = ObjectCache::getMainWANInstance();
                if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
                        // Do not cache RDBMs blobs in...the RDBMs store
                        $ttl = $cache::TTL_UNCACHEABLE;
@@ -1575,10 +1589,22 @@ class Revision implements IDBAccessObject {
                        $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
                }
 
+               return $ttl;
+       }
+
+       /**
+        * Lazy-load the revision's text.
+        * Currently hardcoded to the 'text' table storage engine.
+        *
+        * @return string|bool The revision's text, or false on failure
+        */
+       private function loadText() {
+               $cache = ObjectCache::getMainWANInstance();
+
                // No negative caching; negative hits on text rows may be due to corrupted replica DBs
                return $cache->getWithSetCallback(
                        $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
-                       $ttl,
+                       self::getCacheTTL( $cache ),
                        function () {
                                return $this->fetchText();
                        },
index e686cd8..5ea96dd 100644 (file)
@@ -403,13 +403,11 @@ if ( is_array( $wgExtraNamespaces ) ) {
        $wgCanonicalNamespaceNames = $wgCanonicalNamespaceNames + $wgExtraNamespaces;
 }
 
-// Merge in the legacy language codes, unless overridden in the config
-if ( !isset( $wgDummyLanguageCodes ) ) {
-       $wgDummyLanguageCodes = [
-               'qqq' => 'qqq', // Used for message documentation
-               'qqx' => 'qqx', // Used for viewing message keys
-       ] + $wgExtraLanguageCodes + LanguageCode::getDeprecatedCodeMapping();
-}
+// Merge in the legacy language codes, incorporating overrides from the config
+$wgDummyLanguageCodes += [
+       'qqq' => 'qqq', // Used for message documentation
+       'qqx' => 'qqx', // Used for viewing message keys
+] + $wgExtraLanguageCodes + LanguageCode::getDeprecatedCodeMapping();
 
 // These are now the same, always
 // To determine the user language, use $wgLang->getCode()
index 470a75c..924c347 100644 (file)
@@ -54,18 +54,11 @@ class TemplateParser {
         * @throws UnexpectedValueException If $templateName attempts upwards directory traversal
         */
        protected function getTemplateFilename( $templateName ) {
-               // Prevent upwards directory traversal using same methods as Title::secureAndSplit
+               // Prevent path traversal. Based on Language::isValidCode().
+               // This is for paranoia. The $templateName should never come from
+               // untrusted input.
                if (
-                       strpos( $templateName, '.' ) !== false &&
-                       (
-                               $templateName === '.' || $templateName === '..' ||
-                               strpos( $templateName, './' ) === 0 ||
-                               strpos( $templateName, '../' ) === 0 ||
-                               strpos( $templateName, '/./' ) !== false ||
-                               strpos( $templateName, '/../' ) !== false ||
-                               substr( $templateName, -2 ) === '/.' ||
-                               substr( $templateName, -3 ) === '/..'
-                       )
+                       strcspn( $templateName, ":/\\\000&<>'\"%" ) !== strlen( $templateName )
                ) {
                        throw new UnexpectedValueException( "Malformed \$templateName: $templateName" );
                }
index 3ed6b8b..a5bb9c6 100644 (file)
@@ -2943,8 +2943,6 @@ class Title implements LinkTarget {
                                        continue;
                                }
 
-                               // This code should be refactored, now that it's being used more generally,
-                               // But I don't really see any harm in leaving it in Block for now -werdna
                                $expiry = $dbr->decodeExpiry( $row->pr_expiry );
 
                                // Only apply the restrictions if they haven't expired!
index d8ca4b1..f8e6acd 100644 (file)
        "apihelp-query+langlinks-param-limit": "Wie viele Sprachlinks zurückgegeben werden sollen.",
        "apihelp-query+langlinks-param-prop": "Zusätzlich zurückzugebende Eigenschaften jedes Interlanguage-Links:",
        "apihelp-query+langlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+langlinks-paramvalue-prop-autonym": "Ergänzt den Namen der Muttersprache.",
        "apihelp-query+langlinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+links-param-limit": "Wie viele Links zurückgegeben werden sollen.",
        "apihelp-query+links-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+linkshere-paramvalue-prop-title": "Titel jeder Seite.",
        "apihelp-query+logevents-description": "Ereignisse von den Logbüchern abrufen.",
        "apihelp-query+logevents-param-prop": "Zurückzugebende Eigenschaften:",
+       "apihelp-query+logevents-paramvalue-prop-ids": "Ergänzt die Kennung des Logbuchereignisses.",
+       "apihelp-query+logevents-paramvalue-prop-title": "Ergänzt den Titel der Seite für das Logbuchereignis.",
        "apihelp-query+logevents-paramvalue-prop-type": "Ergänzt den Typ des Logbuchereignisses.",
+       "apihelp-query+logevents-paramvalue-prop-user": "Ergänzt den verantwortlichen Benutzer für das Logbuchereignis.",
        "apihelp-query+logevents-paramvalue-prop-comment": "Ergänzt den Kommentar des Logbuchereignisses.",
        "apihelp-query+logevents-example-simple": "Listet die letzten Logbuch-Ereignisse auf.",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
index cfb1db8..d698b30 100644 (file)
        "apihelp-query+logevents-paramvalue-prop-details": "Liste les détails supplémentaires sur l’événement.",
        "apihelp-query+logevents-paramvalue-prop-tags": "Liste les balises de l’événement.",
        "apihelp-query+logevents-param-type": "Filtrer les entrées du journal sur ce seul type.",
-       "apihelp-query+logevents-param-action": "Filtrer les actions du journal sur cette seule action. Écrase <var>$1type</var>. Dans le liste des valeurs possibles, les valeurs suivies d'un astérisque, comme <kbd>action/*</kbd>, peuvent avoir différentes chaînes à la place du slash.",
+       "apihelp-query+logevents-param-action": "Filtrer les actions du journal sur cette seule action. Écrase <var>$1type</var>. Dans la liste des valeurs possibles, les valeurs suivies d'un astérisque, comme <kbd>action/*</kbd>, peuvent avoir différentes chaînes après le slash.",
        "apihelp-query+logevents-param-start": "L’horodatage auquel démarrer l’énumération.",
        "apihelp-query+logevents-param-end": "L’horodatage auquel arrêter l’énumération.",
        "apihelp-query+logevents-param-user": "Restreindre aux entrées générées par l’utilisateur spécifié.",
index 24cb77c..183f3cb 100644 (file)
        "apierror-notpatrollable": "A revisión r$1 non pode patrullarse por ser demasiado antiga.",
        "apierror-opensearch-json-warnings": "Non se poden representar os avisos en formato JSON de OpenSearch.",
        "apierror-pagecannotexist": "O espazo de nomes non permite as páxinas actuais.",
+       "apierror-pagedeleted": "A páxina foi borrada dende que obtivo o selo de tempo.",
        "apierror-pagelang-disabled": "Neste wiki non se pode cambiar a lingua dunha páxina.",
        "apierror-paramempty": "O parámetro <var>$1</var> non pode estar baleiro.",
        "apierror-parsetree-notwikitext": "<kbd>prop=parsetree</kbd> só está soportado para o contido wikitexto.",
        "apierror-pastexpiry": "A tempo de caducidade \"$1\" está no pasado.",
        "apierror-permissiondenied": "Non ten permiso para $1.",
        "apierror-permissiondenied-generic": "Permisos rexeitados.",
+       "apierror-permissiondenied-patrolflag": "Necesita o permiso <code>patrol</code> ou <code>patrolmarks</code> para solicitar a marca de patrullado.",
        "apierror-permissiondenied-unblock": "Non ten permiso para desbloquear usuarios.",
        "apierror-prefixsearchdisabled": "A busca de prefixo está desactivada no modo Miser (avaro).",
        "apierror-promised-nonwrite-api": "A cabeceira HTTP <code>Promise-Non-Write-API-Action</code> non se pode enviar a módulos da API en modo escritura.",
index 30dec54..ec20f8a 100644 (file)
        "apihelp-revisiondelete-description": "판을 삭제하거나 되살립니다.",
        "apihelp-revisiondelete-param-reason": "삭제 또는 복구 이유.",
        "apihelp-rollback-param-tags": "되돌리기를 적용하기 위해 태그합니다.",
+       "apihelp-rollback-example-simple": "<kbd>Project:대문</kbd> 문서의 <kbd>예시</kbd>의 마지막 판을 되돌리기",
        "apihelp-setpagelanguage-description": "문서의 언어를 변경합니다.",
        "apihelp-setpagelanguage-description-disabled": "이 위키에서 문서의 언어 변경은 허용되지 않습니다.\n\n이 동작을 사용하려면 <var>[[mw:Manual:$wgPageLanguageUseDB|$wgPageLanguageUseDB]]</var>을 활성화하십시오.",
        "apihelp-setpagelanguage-param-reason": "변경 이유.",
index 4facc20..7cd489a 100644 (file)
@@ -508,7 +508,7 @@ class MessageCache {
 
                $res = $dbr->select(
                        [ 'page', 'revision', 'text' ],
-                       [ 'page_title', 'old_text', 'old_flags' ],
+                       [ 'page_title', 'old_id', 'old_text', 'old_flags' ],
                        $smallConds,
                        __METHOD__ . "($code)-small"
                );
index 3f4ad14..92a3d3f 100644 (file)
@@ -26,6 +26,8 @@ use MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\ResultWrapper;
 
 class ChangesList extends ContextSource {
+       const CSS_CLASS_PREFIX = 'mw-changeslist-';
+
        /**
         * @var Skin
         */
@@ -47,12 +49,18 @@ class ChangesList extends ContextSource {
         */
        protected $linkRenderer;
 
+       /**
+        * @var array
+        */
+       protected $filterGroups;
+
        /**
         * Changeslist constructor
         *
         * @param Skin|IContextSource $obj
+        * @param array $filterGroups Array of ChangesListFilterGroup objects (currently optional)
         */
-       public function __construct( $obj ) {
+       public function __construct( $obj, array $filterGroups = [] ) {
                if ( $obj instanceof IContextSource ) {
                        $this->setContext( $obj );
                        $this->skin = $obj->getSkin();
@@ -63,6 +71,7 @@ class ChangesList extends ContextSource {
                $this->preCacheMessages();
                $this->watchMsgCache = new HashBagOStuff( [ 'maxKeys' => 50 ] );
                $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+               $this->filterGroups = $filterGroups;
        }
 
        /**
@@ -70,16 +79,19 @@ class ChangesList extends ContextSource {
         * Some users might want to use an enhanced list format, for instance
         *
         * @param IContextSource $context
+        * @param array $groups Array of ChangesListFilterGroup objects (currently optional)
         * @return ChangesList
         */
-       public static function newFromContext( IContextSource $context ) {
+       public static function newFromContext( IContextSource $context, array $groups = [] ) {
                $user = $context->getUser();
                $sk = $context->getSkin();
                $list = null;
                if ( Hooks::run( 'FetchChangesList', [ $user, &$sk, &$list ] ) ) {
                        $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
 
-                       return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
+                       return $new ?
+                               new EnhancedChangesList( $context, $groups ) :
+                               new OldChangesList( $context, $groups );
                } else {
                        return $list;
                }
@@ -159,42 +171,40 @@ class ChangesList extends ContextSource {
        protected function getHTMLClasses( $rc, $watched ) {
                $classes = [];
                $logType = $rc->mAttribs['rc_log_type'];
-               $prefix = 'mw-changeslist-';
 
                if ( $logType ) {
-                       $classes[] = Sanitizer::escapeClass( $prefix . 'log-' . $logType );
+                       $classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'log-' . $logType );
                } else {
-                       $classes[] = Sanitizer::escapeClass( $prefix . 'ns' .
+                       $classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'ns' .
                                $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
                }
 
                // Indicate watched status on the line to allow for more
                // comprehensive styling.
                $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
-                       ? $prefix . 'line-watched'
-                       : $prefix . 'line-not-watched';
+                       ? self::CSS_CLASS_PREFIX . 'line-watched'
+                       : self::CSS_CLASS_PREFIX . 'line-not-watched';
 
                $classes = array_merge( $classes, $this->getHTMLClassesForFilters( $rc ) );
 
                return $classes;
        }
 
+       /**
+        * Get an array of CSS classes attributed to filters for this row
+        *
+        * @param RecentChange $rc
+        * @return array Array of CSS classes
+        */
        protected function getHTMLClassesForFilters( $rc ) {
                $classes = [];
-               $prefix = 'mw-changeslist-';
-
-               $classes[] = $prefix . ( $rc->getAttribute( 'rc_bot' ) ? 'bot' : 'human' );
-               $classes[] = $prefix . ( $rc->getAttribute( 'rc_user' ) ? 'liu' : 'anon' );
-               $classes[] = $prefix . ( $rc->getAttribute( 'rc_minor' ) ? 'minor' : 'major' );
-               $classes[] = $prefix .
-                       ( $rc->getAttribute( 'rc_patrolled' ) ? 'patrolled' : 'unpatrolled' );
-               $classes[] = $prefix .
-                       ( $this->getUser()->equals( $rc->getPerformer() ) ? 'self' : 'others' );
-               $classes[] = $prefix . 'src-' . str_replace( '.', '-', $rc->getAttribute( 'rc_source' ) );
-
-               $performer = $rc->getPerformer();
-               if ( $performer && $performer->isLoggedIn() ) {
-                       $classes[] = $prefix . 'user-' . $performer->getExperienceLevel();
+
+               if ( $this->filterGroups !== null ) {
+                       foreach ( $this->filterGroups as $filterGroup ) {
+                               foreach ( $filterGroup->getFilters() as $filter ) {
+                                       $filter->applyCssClassIfNeeded( $this, $rc, $classes );
+                               }
+                       }
                }
 
                return $classes;
diff --git a/includes/changes/ChangesListBooleanFilter.php b/includes/changes/ChangesListBooleanFilter.php
new file mode 100644 (file)
index 0000000..b6be1f9
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Represents a hide-based boolean filter (used on ChangesListSpecialPage and descendants)
+ *
+ * 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
+ * @license GPL 2+
+ * @author Matthew Flaschen
+ */
+
+/**
+ * An individual filter in a boolean group
+ *
+ * @since 1.29
+ */
+class ChangesListBooleanFilter extends ChangesListFilter {
+       /**
+        * Name.  Used as URL parameter
+        *
+        * @var string $name
+        */
+
+       // This can sometimes be different on Special:RecentChanges
+       // and Special:Watchlist, due to the double-legacy hooks
+       // (SpecialRecentChangesFilters and SpecialWatchlistFilters)
+
+       // but there will be separate sets of ChangesListFilterGroup and ChangesListFilter instances
+       // for those pages (it should work even if they're both loaded
+       // at once, but that can't happen).
+       /**
+        * Main unstructured UI i18n key
+        *
+        * @var string $showHide
+        */
+       protected $showHide;
+
+       /**
+        * Whether there is a feature designed to replace this filter available on the
+        * structured UI
+        *
+        * @var bool $isReplacedInStructuredUi
+        */
+       protected $isReplacedInStructuredUi;
+
+       /**
+        * Default
+        *
+        * @var bool $defaultValue
+        */
+       protected $defaultValue;
+
+       /**
+        * Callable used to do the actual query modification; see constructor
+        *
+        * @var callable $queryCallable
+        */
+       protected $queryCallable;
+
+       /**
+        * Create a new filter with the specified configuration.
+        *
+        * It infers which UI (it can be either or both) to display the filter on based on
+        * which messages are provided.
+        *
+        * If 'label' is provided, it will be displayed on the structured UI.  If
+        * 'showHide' is provided, it will be displayed on the unstructured UI.  Thus,
+        * 'label', 'description', and 'showHide' are optional depending on which UI
+        * it's for.
+        *
+        * @param array $filterDefinition ChangesListFilter definition
+        *
+        * $filterDefinition['name'] string Name.  Used as URL parameter.
+        * $filterDefinition['group'] ChangesListFilterGroup Group.  Filter group this
+        *  belongs to.
+        * $filterDefinition['label'] string i18n key of label for structured UI.
+        * $filterDefinition['description'] string i18n key of description for structured
+        *  UI.
+        * $filterDefinition['showHide'] string Main i18n key used for unstructured UI.
+        * $filterDefinition['isReplacedInStructuredUi'] bool Whether there is an
+        *  equivalent feature available in the structured UI; this is optional, defaulting
+        *  to true.  It does not need to be set if the exact same filter is simply visible
+        *  on both.
+        * $filterDefinition['default'] bool Default
+        * $filterDefinition['isAllowedCallable'] callable Callable taking two parameters,
+        *  the class name of the special page and an IContextSource, and returning true
+        *  if and only if the current user is permitted to use this filter on the current
+        *  wiki.  If it returns false, it will both hide the UI (in all UIs) and prevent
+        *  the DB query modification from taking effect. (optional, defaults to allowed)
+        * $filterDefinition['priority'] int Priority integer.  Higher value means higher
+        *  up in the group's filter list.
+        * $filterDefinition['queryCallable'] callable Callable accepting parameters, used
+        *  to implement filter's DB query modification.  Callback parameters:
+        *   string $specialPageClassName Class name of current special page
+        *   IContextSource $context Context, for e.g. user
+        *   IDatabase $dbr Database, for addQuotes, makeList, and similar
+        *   array &$tables Array of tables; see IDatabase::select $table
+        *   array &$fields Array of fields; see IDatabase::select $vars
+        *   array &$conds Array of conditions; see IDatabase::select $conds
+        *   array &$query_options Array of query options; see IDatabase::select $options
+        *   array &$join_conds Array of join conditions; see IDatabase::select $join_conds
+        *   Optional only for legacy filters that still use the query hooks directly
+        */
+       public function __construct( $filterDefinition ) {
+               parent::__construct( $filterDefinition );
+
+               if ( isset( $filterDefinition['showHide'] ) ) {
+                       $this->showHide = $filterDefinition['showHide'];
+               }
+
+               if ( isset( $filterDefinition['isReplacedInStructuredUi'] ) ) {
+                       $this->isReplacedInStructuredUi = $filterDefinition['isReplacedInStructuredUi'];
+               } else {
+                       $this->isReplacedInStructuredUi = false;
+               }
+
+               if ( isset( $filterDefinition['default'] ) ) {
+                       $this->defaultValue = $filterDefinition['default'];
+               } else {
+                       throw new MWException( 'You must set a default' );
+               }
+
+               if ( isset( $filterDefinition['queryCallable'] ) ) {
+                       $this->queryCallable = $filterDefinition['queryCallable'];
+               }
+       }
+
+       /**
+        * @return bool|null Default value
+        */
+       public function getDefault() {
+               return $this->defaultValue;
+       }
+
+       /**
+        * Sets default
+        *
+        * @param bool Default value
+        */
+       public function setDefault( $defaultValue ) {
+               $this->defaultValue = $defaultValue;
+       }
+
+       /**
+        * @return string Main i18n key for unstructured UI
+        */
+       public function getShowHide() {
+               return $this->showHide;
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function displaysOnUnstructuredUi( ChangesListSpecialPage $specialPage ) {
+               return $this->showHide &&
+                       $this->isAllowed( $specialPage );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function isFeatureAvailableOnStructuredUi( ChangesListSpecialPage $specialPage ) {
+               return $this->isReplacedInStructuredUi ||
+                       parent::isFeatureAvailableOnStructuredUi( $specialPage );
+       }
+
+       /**
+        * Modifies the query to include the filter.  This is only called if the filter is
+        * in effect (taking into account the default).
+        *
+        * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
+        * @param ChangesListSpecialPage $specialPage Current special page
+        * @param array &$tables Array of tables; see IDatabase::select $table
+        * @param array &$fields Array of fields; see IDatabase::select $vars
+        * @param array &$conds Array of conditions; see IDatabase::select $conds
+        * @param array &$query_options Array of query options; see IDatabase::select $options
+        * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
+        */
+       public function modifyQuery( IDatabase $dbr, ChangesListSpecialPage $specialPage,
+               &$tables, &$fields, &$conds, &$query_options, &$join_conds ) {
+
+               if ( $this->queryCallable === null ) {
+                       return;
+               }
+
+               call_user_func_array(
+                       $this->queryCallable,
+                       [
+                               get_class( $specialPage ),
+                               $specialPage->getContext(),
+                               $dbr,
+                               &$tables,
+                               &$fields,
+                               &$conds,
+                               &$query_options,
+                               &$join_conds
+                       ]
+               );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function getJsData() {
+               $output = parent::getJsData();
+
+               $output['default'] = $this->defaultValue;
+
+               return $output;
+       }
+
+}
diff --git a/includes/changes/ChangesListBooleanFilterGroup.php b/includes/changes/ChangesListBooleanFilterGroup.php
new file mode 100644 (file)
index 0000000..1fdcd00
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * If the group is active, any unchecked filters will
+ * translate to hide parameters in the URL.  E.g. if 'Human (not bot)' is checked,
+ * but 'Bot' is unchecked, hidebots=1 will be sent.
+ *
+ * @since 1.29
+ */
+class ChangesListBooleanFilterGroup extends ChangesListFilterGroup {
+       /**
+        * Type marker, used by JavaScript
+        */
+       const TYPE = 'send_unselected_if_any';
+
+       /**
+        * Create a new filter group with the specified configuration
+        *
+        * @param array $groupDefinition Configuration of group
+        * * $groupDefinition['name'] string Group name
+        * * $groupDefinition['title'] string i18n key for title (optional, can be omitted
+        * *  only if none of the filters in the group display in the structured UI)
+        * * $groupDefinition['priority'] int Priority integer.  Higher means higher in the
+        * *  group list.
+        * * $groupDefinition['filters'] array Numeric array of filter definitions, each of which
+        * *  is an associative array to be passed to the filter constructor.  However,
+        * *  'priority' is optional for the filters.  Any filter that has priority unset
+        * *  will be put to the bottom, in the order given.
+        */
+       public function __construct( array $groupDefinition ) {
+               $groupDefinition['isFullCoverage'] = true;
+               $groupDefinition['type'] = self::TYPE;
+
+               parent::__construct( $groupDefinition );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function createFilter( array $filterDefinition ) {
+               return new ChangesListBooleanFilter( $filterDefinition );
+       }
+
+       /**
+        * Registers a filter in this group
+        *
+        * @param ChangesListBooleanFilter $filter ChangesListBooleanFilter
+        */
+       public function registerFilter( ChangesListBooleanFilter $filter ) {
+               $this->filters[$filter->getName()] = $filter;
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function isPerGroupRequestParameter() {
+               return false;
+       }
+}
diff --git a/includes/changes/ChangesListFilter.php b/includes/changes/ChangesListFilter.php
new file mode 100644 (file)
index 0000000..4ac6387
--- /dev/null
@@ -0,0 +1,418 @@
+<?php
+/**
+ * Represents a filter (used on ChangesListSpecialPage and descendants)
+ *
+ * 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
+ * @license GPL 2+
+ * @author Matthew Flaschen
+ */
+
+/**
+ * Represents a filter (used on ChangesListSpecialPage and descendants)
+ *
+ * @since 1.29
+ */
+abstract class ChangesListFilter {
+       /**
+        * Filter name
+        *
+        * @var string $name
+        */
+       protected $name;
+
+       /**
+        * CSS class suffix used for attribution, e.g. 'bot'.
+        *
+        * In this example, if bot actions are included in the result set, this CSS class
+        * will then be included in all bot-flagged actions.
+        *
+        * @var string|null $cssClassSuffix
+        */
+       protected $cssClassSuffix;
+
+       /**
+        * Callable that returns true if and only if a row is attributed to this filter
+        *
+        * @var callable $isRowApplicableCallable
+        */
+       protected $isRowApplicableCallable;
+
+       /**
+        * Group.  ChangesListFilterGroup this belongs to
+        *
+        * @var ChangesListFilterGroup $group
+        */
+       protected $group;
+
+       /**
+        * i18n key of label for structured UI
+        *
+        * @var string $label
+        */
+       protected $label;
+
+       /**
+        * i18n key of description for structured UI
+        *
+        * @var string $description
+        */
+       protected $description;
+
+       /**
+        * Callable used to check whether this filter is allowed to take effect
+        *
+        * @var callable $isAllowedCallable
+        */
+       protected $isAllowedCallable;
+
+       /**
+        * List of conflicting groups
+        *
+        * @var array $conflictingGroups Array of associative arrays with conflict
+        *   information.  See setUnidirectionalConflict
+        */
+       protected $conflictingGroups = [];
+
+       /**
+        * List of conflicting filters
+        *
+        * @var array $conflictingFilters Array of associative arrays with conflict
+        *   information.  See setUnidirectionalConflict
+        */
+       protected $conflictingFilters = [];
+
+       /**
+        * List of filters that are a subset of the current filter
+        *
+        * @var array $subsetFilters Array of associative arrays with subset information
+        */
+       protected $subsetFilters = [];
+
+       /**
+        * Priority integer.  Higher value means higher up in the group's filter list.
+        *
+        * @var string $priority
+        */
+       protected $priority;
+
+       /**
+        * Create a new filter with the specified configuration.
+        *
+        * It infers which UI (it can be either or both) to display the filter on based on
+        * which messages are provided.
+        *
+        * If 'label' is provided, it will be displayed on the structured UI.  Thus,
+        * 'label', 'description', and sub-class parameters are optional depending on which
+        * UI it's for.
+        *
+        * @param array $filterDefinition ChangesListFilter definition
+        *
+        * $filterDefinition['name'] string Name of filter
+        * $filterDefinition['cssClassSuffix'] string CSS class suffix, used to mark
+        *  that a particular row belongs to this filter (when a row is included by the
+        *  filter) (optional)
+        * $filterDefinition['isRowApplicableCallable'] Callable taking two parameters, the
+        *  IContextSource, and the RecentChange object for the row, and returning true if
+        *  the row is attributed to this filter.  The above CSS class will then be
+        *  automatically added (optional, required if cssClassSuffix is used).
+        * $filterDefinition['group'] ChangesListFilterGroup Group.  Filter group this
+        *  belongs to.
+        * $filterDefinition['label'] string i18n key of label for structured UI.
+        * $filterDefinition['description'] string i18n key of description for structured
+        *  UI.
+        * $filterDefinition['isAllowedCallable'] callable Callable taking two parameters,
+        *  the class name of the special page and an IContextSource, and returning true
+        *  if and only if the current user is permitted to use this filter on the current
+        *  wiki.  If it returns false, it will both hide the UI (in all UIs) and prevent
+        *  the DB query modification from taking effect. (optional, defaults to allowed)
+        * $filterDefinition['priority'] int Priority integer.  Higher value means higher
+        *  up in the group's filter list.
+        */
+       public function __construct( array $filterDefinition ) {
+               if ( isset( $filterDefinition['group'] ) ) {
+                       $this->group = $filterDefinition['group'];
+               } else {
+                       throw new MWException( 'You must use \'group\' to specify the ' .
+                               'ChangesListFilterGroup this filter belongs to' );
+               }
+
+               $this->name = $filterDefinition['name'];
+
+               if ( isset( $filterDefinition['cssClassSuffix'] ) ) {
+                       $this->cssClassSuffix = $filterDefinition['cssClassSuffix'];
+                       $this->isRowApplicableCallable = $filterDefinition['isRowApplicableCallable'];
+               }
+
+               if ( isset( $filterDefinition['label'] ) ) {
+                       $this->label = $filterDefinition['label'];
+                       $this->description = $filterDefinition['description'];
+               }
+
+               if ( isset( $filterDefinition['isAllowedCallable'] ) ) {
+                       $this->isAllowedCallable = $filterDefinition['isAllowedCallable'];
+               }
+
+               $this->priority = $filterDefinition['priority'];
+
+               $this->group->registerFilter( $this );
+       }
+
+       /**
+        * Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
+        *
+        * WARNING: This means there is a conflict when both things are *shown*
+        * (not filtered out), even for the hide-based filters.  So e.g. conflicting with
+        * 'hideanons' means there is a conflict if only anonymous users are *shown*.
+        *
+        * @param ChangesListFilterGroup|ChangesListFilter $other Other
+        *  ChangesListFilterGroup or ChangesListFilter
+        * @param string $globalKey i18n key for top-level conflict message
+        * @param string $forwardKey i18n key for conflict message in this
+        *  direction (when in UI context of $this object)
+        * @param string $backwardKey i18n key for conflict message in reverse
+        *  direction (when in UI context of $other object)
+        */
+       public function conflictsWith( $other, $globalKey, $forwardKey,
+               $backwardKey ) {
+
+               if ( $globalKey === null || $forwardKey === null ||
+                       $backwardKey === null ) {
+
+                       throw new MWException( 'All messages must be specified' );
+               }
+
+               $this->setUnidirectionalConflict(
+                       $other,
+                       $globalKey,
+                       $forwardKey
+               );
+
+               $other->setUnidirectionalConflict(
+                       $this,
+                       $globalKey,
+                       $backwardKey
+               );
+       }
+
+       /**
+        * Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with
+        * this object.
+        *
+        * Internal use ONLY.
+        *
+        * @param ChangesListFilterGroup|ChangesListFilter $other Other
+        *  ChangesListFilterGroup or ChangesListFilter
+        * @param string $globalDescription i18n key for top-level conflict message
+        * @param string $contextDescription i18n key for conflict message in this
+        *  direction (when in UI context of $this object)
+        */
+       public function setUnidirectionalConflict( $other, $globalDescription,
+               $contextDescription ) {
+
+               if ( $other instanceof ChangesListFilterGroup ) {
+                       $this->conflictingGroups[] = [
+                               'group' => $other->getName(),
+                               'globalDescription' => $globalDescription,
+                               'contextDescription' => $contextDescription,
+                       ];
+               } elseif ( $other instanceof ChangesListFilter ) {
+                       $this->conflictingFilters[] = [
+                               'group' => $other->getGroup()->getName(),
+                               'filter' => $other->getName(),
+                               'globalDescription' => $globalDescription,
+                               'contextDescription' => $contextDescription,
+                       ];
+               } else {
+                       throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
+               }
+       }
+
+       /**
+        * Marks that the current instance is (also) a superset of the filter passed in.
+        * This can be called more than once.
+        *
+        * This means that anything in the results for the other filter is also in the
+        * results for this one.
+        *
+        * @param ChangesListFilter The filter the current instance is a superset of
+        */
+       public function setAsSupersetOf( ChangesListFilter $other ) {
+               if ( $other->getGroup() !== $this->getGroup() ) {
+                       throw new MWException( 'Supersets can only be defined for filters in the same group' );
+               }
+
+               $this->subsetFilters[] = [
+                       // It's always the same group, but this makes the representation
+                       // more consistent with conflicts.
+                       'group' => $other->getGroup()->getName(),
+                       'filter' => $other->getName(),
+               ];
+       }
+
+       /**
+        * @return string Name, e.g. hideanons
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        * @return ChangesListFilterGroup Group this belongs to
+        */
+       public function getGroup() {
+               return $this->group;
+       }
+
+       /**
+        * @return string i18n key of label for structured UI
+        */
+       public function getLabel() {
+               return $this->label;
+       }
+
+       /**
+        * @return string i18n key of description for structured UI
+        */
+       public function getDescription() {
+               return $this->description;
+       }
+
+       /**
+        * Checks whether the filter should display on the unstructured UI
+        *
+        * @param ChangesListSpecialPage $specialPage Current special page
+        * @return bool Whether to display
+        */
+       abstract public function displaysOnUnstructuredUi( ChangesListSpecialPage $specialPage );
+
+       /**
+        * Checks whether the filter should display on the structured UI
+        * This refers to the exact filter.  See also isFeatureAvailableOnStructuredUi.
+        *
+        * @param ChangesListSpecialPage $specialPage Current special page
+        * @return bool Whether to display
+        */
+       public function displaysOnStructuredUi( ChangesListSpecialPage $specialPage ) {
+               return $this->label !== null && $this->isAllowed( $specialPage );
+       }
+
+       /**
+        * Checks whether an equivalent feature for this filter is available on the
+        * structured UI.
+        *
+        * This can either be the exact filter, or a new filter that replaces it.
+        */
+       public function isFeatureAvailableOnStructuredUi( ChangesListSpecialPage $specialPage ) {
+               return $this->displaysOnStructuredUi( $specialPage );
+       }
+
+       /**
+        * @return int Priority.  Higher value means higher up in the group list
+        */
+       public function getPriority() {
+               return $this->priority;
+       }
+
+       /**
+        * Checks whether the filter is allowed for the current context
+        *
+        * @param ChangesListSpecialPage $specialPage Current special page
+        * @return bool Whether it is allowed
+        */
+       public function isAllowed( ChangesListSpecialPage $specialPage ) {
+               if ( $this->isAllowedCallable === null ) {
+                       return true;
+               } else {
+                       return call_user_func(
+                               $this->isAllowedCallable,
+                               get_class( $specialPage ),
+                               $specialPage->getContext()
+                       );
+               }
+       }
+
+       /**
+        * Gets the CSS class
+        *
+        * @return string|null CSS class, or null if not defined
+        */
+       protected function getCssClass() {
+               if ( $this->cssClassSuffix !== null ) {
+                       return ChangesList::CSS_CLASS_PREFIX . $this->cssClassSuffix;
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Add CSS class if needed
+        *
+        * @param IContextSource $ctx Context source
+        * @param RecentChange $rc Recent changes object
+        * @param Non-associative array of CSS class names; appended to if needed
+        */
+       public function applyCssClassIfNeeded( IContextSource $ctx, RecentChange $rc, array &$classes ) {
+               if ( $this->isRowApplicableCallable === null ) {
+                       return;
+               }
+
+               if ( call_user_func( $this->isRowApplicableCallable, $ctx, $rc ) ) {
+                       $classes[] = $this->getCssClass();
+               }
+       }
+
+       /**
+        * Gets the JS data required by the front-end of the structured UI
+        *
+        * @return array Associative array Data required by the front-end.  messageKeys is
+        *  a special top-level value, with the value being an array of the message keys to
+        *  send to the client.
+        */
+       public function getJsData() {
+               $output = [
+                       'name' => $this->getName(),
+                       'label' => $this->getLabel(),
+                       'description' => $this->getDescription(),
+                       'cssClass' => $this->getCssClass(),
+                       'priority' => $this->priority,
+                       'subset' => $this->subsetFilters,
+                       'conflicts' => [],
+               ];
+
+               $output['messageKeys'] = [
+                       $this->getLabel(),
+                       $this->getDescription(),
+               ];
+
+               $conflicts = array_merge(
+                       $this->conflictingGroups,
+                       $this->conflictingFilters
+               );
+
+               foreach ( $conflicts as $conflictInfo ) {
+                       $output['conflicts'][] = $conflictInfo;
+                       array_push(
+                               $output['messageKeys'],
+                               $conflictInfo['globalDescription'],
+                               $conflictInfo['contextDescription']
+                       );
+               }
+
+               return $output;
+       }
+}
diff --git a/includes/changes/ChangesListFilterGroup.php b/includes/changes/ChangesListFilterGroup.php
new file mode 100644 (file)
index 0000000..a4cc287
--- /dev/null
@@ -0,0 +1,394 @@
+<?php
+/**
+ * Represents a filter group (used on ChangesListSpecialPage and descendants)
+ *
+ * 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
+ * @license GPL 2+
+ * @author Matthew Flaschen
+ */
+
+// TODO: Might want to make a super-class or trait to share behavior (especially re
+// conflicts) between ChangesListFilter and ChangesListFilterGroup.
+// What to call it.  FilterStructure?  That would also let me make
+// setUnidirectionalConflict protected.
+
+/**
+ * Represents a filter group (used on ChangesListSpecialPage and descendants)
+ *
+ * @since 1.29
+ */
+abstract class ChangesListFilterGroup {
+       /**
+        * Name (internal identifier)
+        *
+        * @var string $name
+        */
+       protected $name;
+
+       /**
+        * i18n key for title
+        *
+        * @var string $title
+        */
+       protected $title;
+
+       /**
+        * i18n key for header of What's This?
+        *
+        * @var string|null $whatsThisHeader
+        */
+       protected $whatsThisHeader;
+
+       /**
+        * i18n key for body of What's This?
+        *
+        * @var string|null $whatsThisBody
+        */
+       protected $whatsThisBody;
+
+       /**
+        * URL of What's This? link
+        *
+        * @var string|null $whatsThisUrl
+        */
+       protected $whatsThisUrl;
+
+       /**
+        * i18n key for What's This? link
+        *
+        * @var string|null $whatsThisLinkText
+        */
+       protected $whatsThisLinkText;
+
+       /**
+        * Type, from a TYPE constant of a subclass
+        *
+        * @var string $type
+        */
+       protected $type;
+
+       /**
+        * Priority integer.  Higher values means higher up in the
+        * group list.
+        *
+        * @var string $priority
+        */
+       protected $priority;
+
+       /**
+        * Associative array of filters, as ChangesListFilter objects, with filter name as key
+        *
+        * @var array $filters
+        */
+       protected $filters;
+
+       /**
+        * Whether this group is full coverage.  This means that checking every item in the
+        * group means no changes list (e.g. RecentChanges) entries are filtered out.
+        *
+        * @var bool $isFullCoverage
+        */
+       protected $isFullCoverage;
+
+       /**
+        * List of conflicting groups
+        *
+        * @var array $conflictingGroups Array of associative arrays with conflict
+        *   information.  See setUnidirectionalConflict
+        */
+       protected $conflictingGroups = [];
+
+       /**
+        * List of conflicting filters
+        *
+        * @var array $conflictingFilters Array of associative arrays with conflict
+        *   information.  See setUnidirectionalConflict
+        */
+       protected $conflictingFilters = [];
+
+       const DEFAULT_PRIORITY = -100;
+
+       /**
+        * Create a new filter group with the specified configuration
+        *
+        * @param array $groupDefinition Configuration of group
+        * * $groupDefinition['name'] string Group name
+        * * $groupDefinition['title'] string i18n key for title (optional, can be omitted
+        * *  only if none of the filters in the group display in the structured UI)
+        * * $groupDefinition['type'] string A type constant from a subclass of this one
+        * * $groupDefinition['priority'] int Priority integer.  Higher value means higher
+        * *  up in the group list (optional, defaults to -100).
+        * * $groupDefinition['filters'] array Numeric array of filter definitions, each of which
+        * *  is an associative array to be passed to the filter constructor.  However,
+        * *  'priority' is optional for the filters.  Any filter that has priority unset
+        * *  will be put to the bottom, in the order given.
+        * * $groupDefinition['isFullCoverage'] bool Whether the group is full coverage;
+        * *  if true, this means that checking every item in the group means no
+        * *  changes list entries are filtered out.
+        */
+       public function __construct( array $groupDefinition ) {
+               $this->name = $groupDefinition['name'];
+
+               if ( isset( $groupDefinition['title'] ) ) {
+                       $this->title = $groupDefinition['title'];
+               }
+
+               if ( isset ( $groupDefinition['whatsThisHeader'] ) ) {
+                       $this->whatsThisHeader = $groupDefinition['whatsThisHeader'];
+                       $this->whatsThisBody = $groupDefinition['whatsThisBody'];
+                       $this->whatsThisUrl = $groupDefinition['whatsThisUrl'];
+                       $this->whatsThisLinkText = $groupDefinition['whatsThisLinkText'];
+               }
+
+               $this->type = $groupDefinition['type'];
+               if ( isset( $groupDefinition['priority'] ) ) {
+                       $this->priority = $groupDefinition['priority'];
+               } else {
+                       $this->priority = self::DEFAULT_PRIORITY;
+               }
+
+               $this->isFullCoverage = $groupDefinition['isFullCoverage'];
+
+               $this->filters = [];
+               $lowestSpecifiedPriority = -1;
+               foreach ( $groupDefinition['filters'] as $filterDefinition ) {
+                       if ( isset( $filterDefinition['priority'] ) ) {
+                               $lowestSpecifiedPriority = min( $lowestSpecifiedPriority, $filterDefinition['priority'] );
+                       }
+               }
+
+               // Convenience feature: If you specify a group (and its filters) all in
+               // one place, you don't have to specify priority.  You can just put them
+               // in order.  However, if you later add one (e.g. an extension adds a filter
+               // to a core-defined group), you need to specify it.
+               $autoFillPriority = $lowestSpecifiedPriority - 1;
+               foreach ( $groupDefinition['filters'] as $filterDefinition ) {
+                       if ( !isset( $filterDefinition['priority'] ) ) {
+                               $filterDefinition['priority'] = $autoFillPriority;
+                               $autoFillPriority--;
+                       }
+                       $filterDefinition['group'] = $this;
+
+                       $filter = $this->createFilter( $filterDefinition );
+                       $this->registerFilter( $filter );
+               }
+       }
+
+       /**
+        * Creates a filter of the appropriate type for this group, from the definition
+        *
+        * @param array $filterDefinition Filter definition
+        * @return ChangesListFilter Filter
+        */
+       abstract protected function createFilter( array $filterDefinition );
+
+       /**
+        * Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
+        *
+        * WARNING: This means there is a conflict when both things are *shown*
+        * (not filtered out), even for the hide-based filters.  So e.g. conflicting with
+        * 'hideanons' means there is a conflict if only anonymous users are *shown*.
+        *
+        * @param ChangesListFilterGroup|ChangesListFilter $other Other
+        *  ChangesListFilterGroup or ChangesListFilter
+        * @param string $globalKey i18n key for top-level conflict message
+        * @param string $forwardKey i18n key for conflict message in this
+        *  direction (when in UI context of $this object)
+        * @param string $backwardKey i18n key for conflict message in reverse
+        *  direction (when in UI context of $other object)
+        */
+       public function conflictsWith( $other, $globalKey, $forwardKey,
+               $backwardKey ) {
+
+               if ( $globalKey === null || $forwardKey === null ||
+                       $backwardKey === null ) {
+
+                       throw new MWException( 'All messages must be specified' );
+               }
+
+               $this->setUnidirectionalConflict(
+                       $other,
+                       $globalKey,
+                       $forwardKey
+               );
+
+               $other->setUnidirectionalConflict(
+                       $this,
+                       $globalKey,
+                       $backwardKey
+               );
+       }
+
+       /**
+        * Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with
+        * this object.
+        *
+        * Internal use ONLY.
+        *
+        * @param ChangesListFilterGroup|ChangesListFilter $other Other
+        *  ChangesListFilterGroup or ChangesListFilter
+        * @param string $globalDescription i18n key for top-level conflict message
+        * @param string $contextDescription i18n key for conflict message in this
+        *  direction (when in UI context of $this object)
+        */
+       public function setUnidirectionalConflict( $other, $globalDescription,
+               $contextDescription ) {
+
+               if ( $other instanceof ChangesListFilterGroup ) {
+                       $this->conflictingGroups[] = [
+                               'group' => $other->getName(),
+                               'globalDescription' => $globalDescription,
+                               'contextDescription' => $contextDescription,
+                       ];
+               } elseif ( $other instanceof ChangesListFilter ) {
+                       $this->conflictingFilters[] = [
+                               'group' => $other->getGroup()->getName(),
+                               'filter' => $other->getName(),
+                               'globalDescription' => $globalDescription,
+                               'contextDescription' => $contextDescription,
+                       ];
+               } else {
+                       throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
+               }
+       }
+
+       /**
+        * @return string Internal name
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        * @return string i18n key for title
+        */
+       public function getTitle() {
+               return $this->title;
+       }
+
+       /**
+        * @return string Type (TYPE constant from a subclass)
+        */
+       public function getType() {
+               return $this->type;
+       }
+
+       /**
+        * @return int Priority.  Higher means higher in the group list
+        */
+       public function getPriority() {
+               return $this->priority;
+       }
+
+       /**
+        * @return array Associative array of ChangesListFilter objects, with filter name as key
+        */
+       public function getFilters() {
+               return $this->filters;
+       }
+
+       /**
+        * Get filter by name
+        *
+        * @param string $name Filter name
+        * @return ChangesListFilter Specified filter
+        */
+       public function getFilter( $name ) {
+               return $this->filters[$name];
+       }
+
+       /**
+        * Check whether the URL parameter is for the group, or for individual filters.
+        * Defaults can also be defined on the group if and only if this is true.
+        *
+        * @return bool True if and only if the URL parameter is per-group
+        */
+       abstract public function isPerGroupRequestParameter();
+
+       /**
+        * Gets the JS data in the format required by the front-end of the structured UI
+        *
+        * @param ChangesListSpecialPage $specialPage
+        * @return array|null Associative array, or null if there are no filters that
+        *  display in the structured UI.  messageKeys is a special top-level value, with
+        *  the value being an array of the message keys to send to the client.
+        */
+       public function getJsData( ChangesListSpecialPage $specialPage ) {
+               $output = [
+                       'name' => $this->name,
+                       'type' => $this->type,
+                       'fullCoverage' => $this->isFullCoverage,
+                       'filters' => [],
+                       'priority' => $this->priority,
+                       'conflicts' => [],
+                       'messageKeys' => [ $this->title ]
+               ];
+
+               if ( isset ( $this->whatsThisHeader ) ) {
+                       $output['whatsThisHeader'] = $this->whatsThisHeader;
+                       $output['whatsThisBody'] = $this->whatsThisBody;
+                       $output['whatsThisUrl'] = $this->whatsThisUrl;
+                       $output['whatsThisLinkText'] = $this->whatsThisLinkText;
+
+                       array_push(
+                               $output['messageKeys'],
+                               $output['whatsThisHeader'],
+                               $output['whatsThisBody'],
+                               $output['whatsThisLinkText']
+                       );
+               }
+
+               usort( $this->filters, function ( $a, $b ) {
+                       return $b->getPriority() - $a->getPriority();
+               } );
+
+               foreach ( $this->filters as $filterName => $filter ) {
+                       if ( $filter->displaysOnStructuredUi( $specialPage ) ) {
+                               $filterData = $filter->getJsData();
+                               $output['messageKeys'] = array_merge(
+                                       $output['messageKeys'],
+                                       $filterData['messageKeys']
+                               );
+                               unset( $filterData['messageKeys'] );
+                               $output['filters'][] = $filterData;
+                       }
+               }
+
+               if ( count( $output['filters'] ) === 0 ) {
+                       return null;
+               }
+
+               $output['title'] = $this->title;
+
+               $conflicts = array_merge(
+                       $this->conflictingGroups,
+                       $this->conflictingFilters
+               );
+
+               foreach ( $conflicts as $conflictInfo ) {
+                       $output['conflicts'][] = $conflictInfo;
+                       array_push(
+                               $output['messageKeys'],
+                               $conflictInfo['globalDescription'],
+                               $conflictInfo['contextDescription']
+                       );
+               }
+
+               return $output;
+       }
+}
diff --git a/includes/changes/ChangesListStringOptionsFilter.php b/includes/changes/ChangesListStringOptionsFilter.php
new file mode 100644 (file)
index 0000000..b6a8774
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * An individual filter in a ChangesListStringOptionsFilterGroup.
+ *
+ * This filter type will only be displayed on the structured UI currently.
+ *
+ * @since 1.29
+ */
+class ChangesListStringOptionsFilter extends ChangesListFilter {
+       /**
+        * @inheritdoc
+        */
+       public function displaysOnUnstructuredUi( ChangesListSpecialPage $specialPage ) {
+               return false;
+       }
+}
diff --git a/includes/changes/ChangesListStringOptionsFilterGroup.php b/includes/changes/ChangesListStringOptionsFilterGroup.php
new file mode 100644 (file)
index 0000000..befc213
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Represents a filter group (used on ChangesListSpecialPage and descendants)
+ *
+ * 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
+ * @license GPL 2+
+ * @author Matthew Flaschen
+ */
+
+/**
+ * Represents a filter group with multiple string options. They are passed to the server as
+ * a single form parameter separated by a delimiter.  The parameter name is the
+ * group name.  E.g. groupname=opt1;opt2 .
+ *
+ * If all options are selected they are replaced by the term "all".
+ *
+ * There is also a single DB query modification for the whole group.
+ *
+ * @since 1.29
+ */
+
+class ChangesListStringOptionsFilterGroup extends ChangesListFilterGroup {
+       /**
+        * Type marker, used by JavaScript
+        */
+       const TYPE = 'string_options';
+
+       /**
+        * Delimiter
+        */
+       const SEPARATOR = ';';
+
+       /**
+        * Signifies that all options in the group are selected.
+        */
+       const ALL = 'all';
+
+       /**
+        * Signifies that no options in the group are selected, meaning the group has no effect.
+        *
+        * For full-coverage groups, this is the same as ALL if all filters are allowed.
+        * For others, it is not.
+        */
+       const NONE = '';
+
+       /**
+        * Group name; used as form parameter.
+        *
+        * @var string $name
+        */
+
+       /**
+        * Defaul parameter value
+        *
+        * @var string $defaultValue
+        */
+       protected $defaultValue;
+
+       /**
+        * Callable used to do the actual query modification; see constructor
+        *
+        * @var callable $queryCallable
+        */
+       protected $queryCallable;
+
+       /**
+        * Create a new filter group with the specified configuration
+        *
+        * @param array $groupDefinition Configuration of group
+        * * $groupDefinition['name'] string Group name
+        * * $groupDefinition['title'] string i18n key for title (optional, can be omitted
+        * *  only if none of the filters in the group display in the structured UI)
+        * * $groupDefinition['priority'] int Priority integer.  Higher means higher in the
+        * *  group list.
+        * * $groupDefinition['filters'] array Numeric array of filter definitions, each of which
+        * *  is an associative array to be passed to the filter constructor.  However,
+        * *  'priority' is optional for the filters.  Any filter that has priority unset
+        * *  will be put to the bottom, in the order given.
+        * * $groupDefinition['default'] string Default for group.
+        * * $groupDefinition['isFullCoverage'] bool Whether the group is full coverage;
+        * *  if true, this means that checking every item in the group means no
+        * *  changes list entries are filtered out.
+        * * $groupDefinition['queryCallable'] callable Callable accepting parameters:
+        * *  string $specialPageClassName Class name of current special page
+        * *  IContextSource $context Context, for e.g. user
+        * *  IDatabase $dbr Database, for addQuotes, makeList, and similar
+        * *  array &$tables Array of tables; see IDatabase::select $table
+        * *  array &$fields Array of fields; see IDatabase::select $vars
+        * *  array &$conds Array of conditions; see IDatabase::select $conds
+        * *  array &$query_options Array of query options; see IDatabase::select $options
+        * *  array &$join_conds Array of join conditions; see IDatabase::select $join_conds
+        * *  array $selectedValues The allowed and requested values, lower-cased and sorted
+        */
+       public function __construct( array $groupDefinition ) {
+               if ( !isset( $groupDefinition['isFullCoverage'] ) ) {
+                       throw new MWException( 'You must specify isFullCoverage' );
+               }
+
+               $groupDefinition['type'] = self::TYPE;
+
+               parent::__construct( $groupDefinition );
+
+               $this->queryCallable = $groupDefinition['queryCallable'];
+
+               if ( isset( $groupDefinition['default'] ) ) {
+                       $this->setDefault( $groupDefinition['default'] );
+               } else {
+                       throw new MWException( 'You must specify a default' );
+               }
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function isPerGroupRequestParameter() {
+               return true;
+       }
+
+       /**
+        * Sets default of filter group.
+        *
+        * @param string $defaultValue
+        */
+       public function setDefault( $defaultValue ) {
+               $this->defaultValue = $defaultValue;
+       }
+
+       /**
+        * Gets default of filter group
+        *
+        * @return string $defaultValue
+        */
+       public function getDefault() {
+               return $this->defaultValue;
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function createFilter( array $filterDefinition ) {
+               return new ChangesListStringOptionsFilter( $filterDefinition );
+       }
+
+       /**
+        * Registers a filter in this group
+        *
+        * @param ChangesListStringOptionsFilter $filter ChangesListStringOptionsFilter
+        */
+       public function registerFilter( ChangesListStringOptionsFilter $filter ) {
+               $this->filters[$filter->getName()] = $filter;
+       }
+
+       /**
+        * Modifies the query to include the filter group.
+        *
+        * The modification is only done if the filter group is in effect.  This means that
+        * one or more valid and allowed filters were selected.
+        *
+        * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
+        * @param ChangesListSpecialPage $specialPage Current special page
+        * @param array &$tables Array of tables; see IDatabase::select $table
+        * @param array &$fields Array of fields; see IDatabase::select $vars
+        * @param array &$conds Array of conditions; see IDatabase::select $conds
+        * @param array &$query_options Array of query options; see IDatabase::select $options
+        * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
+        * @param string $value URL parameter value
+        */
+       public function modifyQuery( IDatabase $dbr, ChangesListSpecialPage $specialPage,
+               &$tables, &$fields, &$conds, &$query_options, &$join_conds, $value ) {
+
+               $allowedFilterNames = [];
+               foreach ( $this->filters as $filter ) {
+                       if ( $filter->isAllowed( $specialPage ) ) {
+                               $allowedFilterNames[] = $filter->getName();
+                       }
+               }
+
+               if ( $value === self::ALL ) {
+                       $selectedValues = $allowedFilterNames;
+               } else {
+                       $selectedValues = explode( self::SEPARATOR, strtolower( $value ) );
+
+                       // remove values that are not recognized or not currently allowed
+                       $selectedValues = array_intersect(
+                               $selectedValues,
+                               $allowedFilterNames
+                       );
+               }
+
+               // If there are now no values, because all are disallowed or invalid (also,
+               // the user may not have selected any), this is a no-op.
+
+               // If everything is unchecked, the group always has no effect, regardless
+               // of full-coverage.
+               if ( count( $selectedValues ) === 0 ) {
+                       return;
+               }
+
+               sort( $selectedValues );
+
+               call_user_func_array(
+                       $this->queryCallable,
+                       [
+                               get_class( $specialPage ),
+                               $specialPage->getContext(),
+                               $dbr,
+                               &$tables,
+                               &$fields,
+                               &$conds,
+                               &$query_options,
+                               &$join_conds,
+                               $selectedValues
+                       ]
+               );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       public function getJsData( ChangesListSpecialPage $specialPage ) {
+               $output = parent::getJsData( $specialPage );
+
+               $output['separator'] = self::SEPARATOR;
+               $output['default'] = $this->getDefault();
+
+               return $output;
+       }
+}
index 3aad60e..b8a2ac8 100644 (file)
@@ -34,9 +34,10 @@ class EnhancedChangesList extends ChangesList {
 
        /**
         * @param IContextSource|Skin $obj
+        * @param array $filterGroups Array of ChangesListFilterGroup objects (currently optional)
         * @throws MWException
         */
-       public function __construct( $obj ) {
+       public function __construct( $obj, array $filterGroups = [] ) {
                if ( $obj instanceof Skin ) {
                        // @todo: deprecate constructing with Skin
                        $context = $obj->getContext();
@@ -49,7 +50,7 @@ class EnhancedChangesList extends ChangesList {
                        $context = $obj;
                }
 
-               parent::__construct( $context );
+               parent::__construct( $context, $filterGroups );
 
                // message is set by the parent ChangesList class
                $this->cacheEntryFactory = new RCCacheEntryFactory(
index 697188e..dde4daa 100644 (file)
@@ -65,6 +65,7 @@ class LocalSettingsGenerator {
                                '_MemCachedServers', 'wgDBserver', 'wgDBuser',
                                'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
                                'wgMetaNamespace', 'wgLogo', 'wgAuthenticationTokenVersion', 'wgPingback',
+                               '_Caches',
                        ],
                        $db->getGlobalNames()
                );
@@ -293,11 +294,17 @@ class LocalSettingsGenerator {
                        case 'db':
                        case 'memcached':
                        case 'accel':
+                       case 'none':
                                $cacheType = 'CACHE_' . strtoupper( $this->values['_MainCacheType'] );
                                break;
-                       case 'none':
                        default:
-                               $cacheType = 'CACHE_NONE';
+                               // If the user skipped the options page,
+                               // default to CACHE_ACCEL if available
+                               if ( count( $this->values['_Caches'] ) ) {
+                                       $cacheType = 'CACHE_ACCEL';
+                               } else {
+                                       $cacheType = 'CACHE_NONE';
+                               }
                }
 
                $mcservers = $this->buildMemcachedServerList();
index 968ee15..988ec41 100644 (file)
@@ -97,6 +97,8 @@ class MssqlUpdater extends DatabaseUpdater {
 
                        // 1.29
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
+                       [ 'dropIndex', 'oldimage', 'oi_name_archive_name',
+                               'patch-alter-table-oldimage.sql' ],
                ];
        }
 
index 3131c3c..ff13196 100644 (file)
@@ -299,6 +299,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
                        [ 'dropIndex', 'user_groups', 'ug_user_group', 'patch-user_groups-primary-key.sql' ],
                        [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
+                       [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
                ];
        }
 
index cdbbfd0..dcd66dd 100644 (file)
@@ -162,6 +162,7 @@ class SqliteUpdater extends DatabaseUpdater {
                        // 1.29
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
                        [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
+                       [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
                ];
        }
 
index 779ad6d..7a26214 100644 (file)
@@ -62,6 +62,7 @@
        "config-memory-bad": "'''Diwallit :''' Da $1 emañ arventenn <code>memory_limit</code> PHP.\nRe izel eo moarvat.\nMarteze e c'hwito ar staliadenn !",
        "config-xcache": "Staliet eo [http://xcache.lighttpd.net/ XCache]",
        "config-apc": "Staliet eo [http://www.php.net/apc APC]",
+       "config-apcu": "Staliet eo [http://www.php.net/apcu APCu]",
        "config-wincache": "Staliet eo [http://www.iis.net/download/WinCacheForPhp WinCache]",
        "config-diff3-bad": "N'eo ket bet kavet GNU diff3.",
        "config-imagemagick": "ImageMagick kavet : <code>$1</code>.\nGweredekaet e vo ar bihanaat skeudennoù ma vez gweredekaet ganeoc'h ar pellgargañ restroù.",
index c50dfd7..631f6fd 100644 (file)
@@ -348,6 +348,8 @@ class SwiftFileBackend extends FileBackendStore {
                };
 
                $opHandle = new SwiftFileOpHandle( $this, $handler, $reqs );
+               $opHandle->resourcesToClose[] = $handle;
+
                if ( !empty( $params['async'] ) ) { // deferred
                        $status->value = $opHandle;
                } else { // actually write the object in Swift
index 0a4f0ed..cf9033b 100644 (file)
@@ -359,7 +359,7 @@ class ObjectCache {
         *
         * @since 1.26
         * @return WANObjectCache
-        * @deprecated Since 1.28 Use MediaWikiServices::getMainWANCache()
+        * @deprecated Since 1.28 Use MediaWikiServices::getMainWANObjectCache()
         */
        public static function getMainWANInstance() {
                return MediaWikiServices::getInstance()->getMainWANObjectCache();
index f62b302..e92f697 100644 (file)
@@ -39,6 +39,384 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        /** @var array */
        protected $customFilters;
 
+       // Order of both groups and filters is significant; first is top-most priority,
+       // descending from there.
+       // 'showHideSuffix' is a shortcut to and avoid spelling out
+       // details specific to subclasses here.
+       /**
+        * Definition information for the filters and their groups
+        *
+        * The value is $groupDefinition, a parameter to the ChangesListFilterGroup constructor.
+        * However, priority is dynamically added for the core groups, to ease maintenance.
+        *
+        * Groups are displayed to the user in the structured UI.  However, if necessary,
+        * all of the filters in a group can be configured to only display on the
+        * unstuctured UI, in which case you don't need a group title.  This is done in
+        * getFilterGroupDefinitionFromLegacyCustomFilters, for example.
+        *
+        * @var array $filterGroupDefinitions
+        */
+       private $filterGroupDefinitions;
+
+       /**
+        * Filter groups, and their contained filters
+        * This is an associative array (with group name as key) of ChangesListFilterGroup objects.
+        *
+        * @var array $filterGroups
+        */
+       protected $filterGroups = [];
+
+       public function __construct( $name, $restriction ) {
+               parent::__construct( $name, $restriction );
+
+               $this->filterGroupDefinitions = [
+                       [
+                               'name' => 'registration',
+                               'title' => 'rcfilters-filtergroup-registration',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hideliu',
+                                               'label' => 'rcfilters-filter-registered-label',
+                                               'description' => 'rcfilters-filter-registered-description',
+                                               // rcshowhideliu-show, rcshowhideliu-hide,
+                                               // wlshowhideliu
+                                               'showHideSuffix' => 'showhideliu',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_user = 0';
+                                               },
+                                               'cssClassSuffix' => 'liu',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_user' );
+                                               },
+
+                                       ],
+                                       [
+                                               'name' => 'hideanons',
+                                               'label' => 'rcfilters-filter-unregistered-label',
+                                               'description' => 'rcfilters-filter-unregistered-description',
+                                               // rcshowhideanons-show, rcshowhideanons-hide,
+                                               // wlshowhideanons
+                                               'showHideSuffix' => 'showhideanons',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_user != 0';
+                                               },
+                                               'cssClassSuffix' => 'anon',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_user' );
+                                               },
+                                       ]
+                               ],
+                       ],
+
+                       [
+                               'name' => 'userExpLevel',
+                               'title' => 'rcfilters-filtergroup-userExpLevel',
+                               'class' => ChangesListStringOptionsFilterGroup::class,
+                               // Excludes unregistered users
+                               'isFullCoverage' => false,
+                               'filters' => [
+                                       [
+                                               'name' => 'newcomer',
+                                               'label' => 'rcfilters-filter-user-experience-level-newcomer-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-newcomer-description',
+                                               'cssClassSuffix' => 'user-newcomer',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       $performer = $rc->getPerformer();
+                                                       return $performer && $performer->isLoggedIn() &&
+                                                               $performer->getExperienceLevel() === 'newcomer';
+                                               }
+                                       ],
+                                       [
+                                               'name' => 'learner',
+                                               'label' => 'rcfilters-filter-user-experience-level-learner-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-learner-description',
+                                               'cssClassSuffix' => 'user-learner',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       $performer = $rc->getPerformer();
+                                                       return $performer && $performer->isLoggedIn() &&
+                                                               $performer->getExperienceLevel() === 'learner';
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'experienced',
+                                               'label' => 'rcfilters-filter-user-experience-level-experienced-label',
+                                               'description' => 'rcfilters-filter-user-experience-level-experienced-description',
+                                               'cssClassSuffix' => 'user-experienced',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       $performer = $rc->getPerformer();
+                                                       return $performer && $performer->isLoggedIn() &&
+                                                               $performer->getExperienceLevel() === 'experienced';
+                                               },
+                                       ]
+                               ],
+                               'default' => ChangesListStringOptionsFilterGroup::NONE,
+                               'queryCallable' => [ $this, 'filterOnUserExperienceLevel' ],
+                       ],
+
+                       [
+                               'name' => 'authorship',
+                               'title' => 'rcfilters-filtergroup-authorship',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidemyself',
+                                               'label' => 'rcfilters-filter-editsbyself-label',
+                                               'description' => 'rcfilters-filter-editsbyself-description',
+                                               // rcshowhidemine-show, rcshowhidemine-hide,
+                                               // wlshowhidemine
+                                               'showHideSuffix' => 'showhidemine',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $user = $ctx->getUser();
+                                                       if ( $user->getId() ) {
+                                                               $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() );
+                                                       } else {
+                                                               $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
+                                                       }
+                                               },
+                                               'cssClassSuffix' => 'self',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $ctx->getUser()->equals( $rc->getPerformer() );
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hidebyothers',
+                                               'label' => 'rcfilters-filter-editsbyother-label',
+                                               'description' => 'rcfilters-filter-editsbyother-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $user = $ctx->getUser();
+                                                       if ( $user->getId() ) {
+                                                               $conds[] = 'rc_user = ' . $dbr->addQuotes( $user->getId() );
+                                                       } else {
+                                                               $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() );
+                                                       }
+                                               },
+                                               'cssClassSuffix' => 'others',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$ctx->getUser()->equals( $rc->getPerformer() );
+                                               },
+                                       ]
+                               ]
+                       ],
+
+                       [
+                               'name' => 'automated',
+                               'title' => 'rcfilters-filtergroup-automated',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidebots',
+                                               'label' => 'rcfilters-filter-bots-label',
+                                               'description' => 'rcfilters-filter-bots-description',
+                                               // rcshowhidebots-show, rcshowhidebots-hide,
+                                               // wlshowhidebots
+                                               'showHideSuffix' => 'showhidebots',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_bot = 0';
+                                               },
+                                               'cssClassSuffix' => 'bot',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_bot' );
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hidehumans',
+                                               'label' => 'rcfilters-filter-humans-label',
+                                               'description' => 'rcfilters-filter-humans-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_bot = 1';
+                                               },
+                                               'cssClassSuffix' => 'human',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_bot' );
+                                               },
+                                       ]
+                               ]
+                       ],
+
+                       [
+                               'name' => 'reviewStatus',
+                               'title' => 'rcfilters-filtergroup-reviewstatus',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidepatrolled',
+                                               'label' => 'rcfilters-filter-patrolled-label',
+                                               'description' => 'rcfilters-filter-patrolled-description',
+                                               // rcshowhidepatr-show, rcshowhidepatr-hide
+                                               // wlshowhidepatr
+                                               'showHideSuffix' => 'showhidepatr',
+                                               'default' => false,
+                                               'isAllowedCallable' => function ( $pageClassName, $context ) {
+                                                       return $context->getUser()->useRCPatrol();
+                                               },
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_patrolled = 0';
+                                               },
+                                               'cssClassSuffix' => 'patrolled',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_patrolled' );
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hideunpatrolled',
+                                               'label' => 'rcfilters-filter-unpatrolled-label',
+                                               'description' => 'rcfilters-filter-unpatrolled-description',
+                                               'default' => false,
+                                               'isAllowedCallable' => function ( $pageClassName, $context ) {
+                                                       return $context->getUser()->useRCPatrol();
+                                               },
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_patrolled = 1';
+                                               },
+                                               'cssClassSuffix' => 'unpatrolled',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_patrolled' );
+                                               },
+                                       ],
+                               ],
+                       ],
+
+                       [
+                               'name' => 'significance',
+                               'title' => 'rcfilters-filtergroup-significance',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hideminor',
+                                               'label' => 'rcfilters-filter-minor-label',
+                                               'description' => 'rcfilters-filter-minor-description',
+                                               // rcshowhideminor-show, rcshowhideminor-hide,
+                                               // wlshowhideminor
+                                               'showHideSuffix' => 'showhideminor',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_minor = 0';
+                                               },
+                                               'cssClassSuffix' => 'minor',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_minor' );
+                                               }
+                                       ],
+                                       [
+                                               'name' => 'hidemajor',
+                                               'label' => 'rcfilters-filter-major-label',
+                                               'description' => 'rcfilters-filter-major-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_minor = 1';
+                                               },
+                                               'cssClassSuffix' => 'major',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return !$rc->getAttribute( 'rc_minor' );
+                                               }
+                                       ]
+                               ]
+                       ],
+
+                       // With extensions, there can be change types that will not be hidden by any of these.
+                       [
+                               'name' => 'changeType',
+                               'title' => 'rcfilters-filtergroup-changetype',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidepageedits',
+                                               'label' => 'rcfilters-filter-pageedits-label',
+                                               'description' => 'rcfilters-filter-pageedits-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_EDIT );
+                                               },
+                                               'cssClassSuffix' => 'src-mw-edit',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_EDIT;
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hidenewpages',
+                                               'label' => 'rcfilters-filter-newpages-label',
+                                               'description' => 'rcfilters-filter-newpages-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_NEW );
+                                               },
+                                               'cssClassSuffix' => 'src-mw-new',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_NEW;
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hidecategorization',
+                                               'label' => 'rcfilters-filter-categorization-label',
+                                               'description' => 'rcfilters-filter-categorization-description',
+                                               // rcshowhidecategorization-show, rcshowhidecategorization-hide.
+                                               // wlshowhidecategorization
+                                               'showHideSuffix' => 'showhidecategorization',
+                                               'isAllowedCallable' => function ( $pageClassName, $context ) {
+                                                       return $context->getConfig()->get( 'RCWatchCategoryMembership' );
+                                               },
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE );
+                                               },
+                                               'cssClassSuffix' => 'src-mw-categorize',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_CATEGORIZE;
+                                               },
+                                       ],
+                                       [
+                                               'name' => 'hidelog',
+                                               'label' => 'rcfilters-filter-logactions-label',
+                                               'description' => 'rcfilters-filter-logactions-description',
+                                               'default' => false,
+                                               'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
+                                                       &$query_options, &$join_conds ) {
+
+                                                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_LOG );
+                                               },
+                                               'cssClassSuffix' => 'src-mw-log',
+                                               'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                                       return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_LOG;
+                                               }
+                                       ],
+                               ],
+                       ],
+               ];
+       }
+
        /**
         * Main execution point
         *
@@ -96,9 +474,15 @@ abstract class ChangesListSpecialPage extends SpecialPage {
         */
        public function getRows() {
                $opts = $this->getOptions();
-               $conds = $this->buildMainQueryConds( $opts );
 
-               return $this->doMainQuery( $conds, $opts );
+               $tables = [];
+               $fields = [];
+               $conds = [];
+               $query_options = [];
+               $join_conds = [];
+               $this->buildQuery( $tables, $fields, $conds, $query_options, $join_conds, $opts );
+
+               return $this->doMainQuery( $tables, $fields, $conds, $query_options, $join_conds, $opts );
        }
 
        /**
@@ -115,17 +499,99 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Create a FormOptions object with options as specified by the user
+        * Register all filters and their groups, plus conflicts
+        *
+        * You might want to customize these in the same method, in subclasses.  You can
+        * call getFilterGroup to access a group, and (on the group) getFilter to access a
+        * filter, then make necessary modfications to the filter or group (e.g. with
+        * setDefault).
+        */
+       protected function registerFilters() {
+               $this->registerFiltersFromDefinitions( $this->filterGroupDefinitions );
+
+               Hooks::run( 'ChangesListSpecialPageStructuredFilters', [ $this ] );
+
+               $unstructuredGroupDefinition =
+                       $this->getFilterGroupDefinitionFromLegacyCustomFilters(
+                               $this->getCustomFilters()
+                       );
+               $this->registerFiltersFromDefinitions( [ $unstructuredGroupDefinition ] );
+
+               $userExperienceLevel = $this->getFilterGroup( 'userExpLevel' );
+
+               $registration = $this->getFilterGroup( 'registration' );
+               $anons = $registration->getFilter( 'hideanons' );
+
+               // This means there is a conflict between any item in user experience level
+               // being checked and only anons being *shown* (hideliu=1&hideanons=0 in the
+               // URL, or equivalent).
+               $userExperienceLevel->conflictsWith(
+                       $anons,
+                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global',
+                       'rcfilters-filtergroup-user-experience-level-conflicts-unregistered',
+                       'rcfilters-filter-unregistered-conflicts-user-experience-level'
+               );
+       }
+
+       /**
+        * Register filters from a definition object
+        *
+        * Array specifying groups and their filters; see Filter and
+        * ChangesListFilterGroup constructors.
+        *
+        * There is light processing to simplify core maintenance.  See overrides
+        * of this method as well.
+        */
+       protected function registerFiltersFromDefinitions( array $definition ) {
+               $priority = -1;
+               foreach ( $definition as $groupDefinition ) {
+                       $groupDefinition['priority'] = $priority;
+                       $priority--;
+
+                       $className = $groupDefinition['class'];
+                       unset( $groupDefinition['class'] );
+                       $this->registerFilterGroup( new $className( $groupDefinition ) );
+               }
+       }
+
+       /**
+        * Get filter group definition from legacy custom filters
+        *
+        * @param array Custom filters from legacy hooks
+        * @return array Group definition
+        */
+       protected function getFilterGroupDefinitionFromLegacyCustomFilters( $customFilters ) {
+               // Special internal unstructured group
+               $unstructuredGroupDefinition = [
+                       'name' => 'unstructured',
+                       'class' => ChangesListBooleanFilterGroup::class,
+                       'priority' => -1, // Won't display in structured
+                       'filters' => [],
+               ];
+
+               foreach ( $customFilters as $name => $params ) {
+                       $unstructuredGroupDefinition['filters'][] = [
+                               'name' => $name,
+                               'showHide' => $params['msg'],
+                               'default' => $params['default'],
+                       ];
+               }
+
+               return $unstructuredGroupDefinition;
+       }
+
+       /**
+        * Register all the filters, including legacy hook-driven ones.
+        * Then create a FormOptions object with options as specified by the user
         *
         * @param array $parameters
         *
         * @return FormOptions
         */
        public function setup( $parameters ) {
+               $this->registerFilters();
+
                $opts = $this->getDefaultOptions();
-               foreach ( $this->getCustomFilters() as $key => $params ) {
-                       $opts->add( $key, $params['default'] );
-               }
 
                $opts = $this->fetchOptionsFromRequest( $opts );
 
@@ -140,8 +606,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Get a FormOptions object containing the default options. By default returns some basic options,
-        * you might want to not call parent method and discard them, or to override default values.
+        * Get a FormOptions object containing the default options. By default, returns
+        * some basic options.  The filters listed explicitly here are overriden in this
+        * method, in subclasses, but most filters (e.g. hideminor, userExpLevel filters,
+        * and more) are structured.  Structured filters are overriden in registerFilters.
+        * not here.
         *
         * @return FormOptions
         */
@@ -149,23 +618,18 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                $config = $this->getConfig();
                $opts = new FormOptions();
 
-               $opts->add( 'hideminor', false );
-               $opts->add( 'hidemajor', false );
-               $opts->add( 'hidebots', false );
-               $opts->add( 'hidehumans', false );
-               $opts->add( 'hideanons', false );
-               $opts->add( 'hideliu', false );
-               $opts->add( 'hidepatrolled', false );
-               $opts->add( 'hideunpatrolled', false );
-               $opts->add( 'hidemyself', false );
-               $opts->add( 'hidebyothers', false );
-
-               if ( $config->get( 'RCWatchCategoryMembership' ) ) {
-                       $opts->add( 'hidecategorization', false );
+               // Add all filters
+               foreach ( $this->filterGroups as $filterGroup ) {
+                       // URL parameters can be per-group, like 'userExpLevel',
+                       // or per-filter, like 'hideminor'.
+                       if ( $filterGroup->isPerGroupRequestParameter() ) {
+                               $opts->add( $filterGroup->getName(), $filterGroup->getDefault() );
+                       } else {
+                               foreach ( $filterGroup->getFilters() as $filter ) {
+                                       $opts->add( $filter->getName(), $filter->getDefault() );
+                               }
+                       }
                }
-               $opts->add( 'hidepageedits', false );
-               $opts->add( 'hidenewpages', false );
-               $opts->add( 'hidelog', false );
 
                $opts->add( 'namespace', '', FormOptions::INTNULL );
                $opts->add( 'invert', false );
@@ -175,14 +639,86 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Get custom show/hide filters
+        * Register a structured changes list filter group
+        *
+        * @param ChangesListFilterGroup $group
+        */
+       public function registerFilterGroup( ChangesListFilterGroup $group ) {
+               $groupName = $group->getName();
+
+               $this->filterGroups[$groupName] = $group;
+       }
+
+       /**
+        * Gets the currently registered filters groups
+        *
+        * @return array Associative array of ChangesListFilterGroup objects, with group name as key
+        */
+       protected function getFilterGroups() {
+               return $this->filterGroups;
+       }
+
+       /**
+        * Gets a specified ChangesListFilterGroup by name
+        *
+        * @param string $groupName Name of group
+        *
+        * @return ChangesListFilterGroup
+        */
+       public function getFilterGroup( $groupName ) {
+               return $this->filterGroups[$groupName];
+       }
+
+       // Currently, this intentionally only includes filters that display
+       // in the structured UI.  This can be changed easily, though, if we want
+       // to include data on filters that use the unstructured UI.  messageKeys is a
+       // special top-level value, with the value being an array of the message keys to
+       // send to the client.
+       /**
+        * Gets structured filter information needed by JS
+        *
+        * @return array Associative array
+        * * array $return['groups'] Group data
+        * * array $return['messageKeys'] Array of message keys
+        */
+       public function getStructuredFilterJsData() {
+               $output = [
+                       'groups' => [],
+                       'messageKeys' => [],
+               ];
+
+               $context = $this->getContext();
+
+               usort( $this->filterGroups, function ( $a, $b ) {
+                       return $b->getPriority() - $a->getPriority();
+               } );
+
+               foreach ( $this->filterGroups as $groupName => $group ) {
+                       $groupOutput = $group->getJsData( $this );
+                       if ( $groupOutput !== null ) {
+                               $output['messageKeys'] = array_merge(
+                                       $output['messageKeys'],
+                                       $groupOutput['messageKeys']
+                               );
+
+                               unset( $groupOutput['messageKeys'] );
+                               $output['groups'][] = $groupOutput;
+                       }
+               }
+
+               return $output;
+       }
+
+       /**
+        * Get custom show/hide filters using deprecated ChangesListSpecialPageFilters
+        * hook.
         *
         * @return array Map of filter URL param names to properties (msg/default)
         */
        protected function getCustomFilters() {
                if ( $this->customFilters === null ) {
                        $this->customFilters = [];
-                       Hooks::run( 'ChangesListSpecialPageFilters', [ $this, &$this->customFilters ] );
+                       Hooks::run( 'ChangesListSpecialPageFilters', [ $this, &$this->customFilters ], '1.29' );
                }
 
                return $this->customFilters;
@@ -209,7 +745,37 @@ abstract class ChangesListSpecialPage extends SpecialPage {
         * @param FormOptions $opts
         */
        public function parseParameters( $par, FormOptions $opts ) {
-               // nothing by default
+               $stringParameterNameSet = [];
+               $hideParameterNameSet = [];
+
+               // URL parameters can be per-group, like 'userExpLevel',
+               // or per-filter, like 'hideminor'.
+
+               foreach ( $this->filterGroups as $filterGroup ) {
+                       if ( $filterGroup->isPerGroupRequestParameter() ) {
+                               $stringParameterNameSet[$filterGroup->getName()] = true;
+                       } elseif ( $filterGroup->getType() === ChangesListBooleanFilterGroup::TYPE ) {
+                               foreach ( $filterGroup->getFilters() as $filter ) {
+                                       $hideParameterNameSet[$filter->getName()] = true;
+                               }
+                       }
+               }
+
+               $bits = preg_split( '/\s*,\s*/', trim( $par ) );
+               foreach ( $bits as $bit ) {
+                       $m = [];
+                       if ( isset( $hideParameterNameSet[$bit] ) ) {
+                               // hidefoo => hidefoo=true
+                               $opts[$bit] = true;
+                       } elseif ( isset( $hideParameterNameSet["hide$bit"] ) ) {
+                               // foo => hidefoo=false
+                               $opts["hide$bit"] = false;
+                       } elseif ( preg_match( '/^(.*)=(.*)$/', $bit, $m ) ) {
+                               if ( isset( $stringParameterNameSet[$m[1]] ) ) {
+                                       $opts[$m[1]] = $m[2];
+                               }
+                       }
+               }
        }
 
        /**
@@ -222,90 +788,39 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Return an array of conditions depending of options set in $opts
+        * Sets appropriate tables, fields, conditions, etc. depending on which filters
+        * the user requested.
         *
+        * @param array &$tables Array of tables; see IDatabase::select $table
+        * @param array &$fields Array of fields; see IDatabase::select $vars
+        * @param array &$conds Array of conditions; see IDatabase::select $conds
+        * @param array &$query_options Array of query options; see IDatabase::select $options
+        * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
         * @param FormOptions $opts
-        * @return array
         */
-       public function buildMainQueryConds( FormOptions $opts ) {
+       protected function buildQuery( &$tables, &$fields, &$conds, &$query_options,
+               &$join_conds, FormOptions $opts ) {
+
                $dbr = $this->getDB();
                $user = $this->getUser();
-               $conds = [];
-
-               // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on
-               // what the user meant and either show only bots or force anons to be shown.
-               $botsonly = false;
-               $hideanons = $opts['hideanons'];
-               if ( $opts['hideanons'] && $opts['hideliu'] ) {
-                       if ( $opts['hidebots'] ) {
-                               $hideanons = false;
-                       } else {
-                               $botsonly = true;
-                       }
-               }
 
-               // Toggles
-               if ( $opts['hideminor'] ) {
-                       $conds[] = 'rc_minor = 0';
-               }
-               if ( $opts['hidemajor'] ) {
-                       $conds[] = 'rc_minor = 1';
-               }
-               if ( $opts['hidebots'] ) {
-                       $conds['rc_bot'] = 0;
-               }
-               if ( $opts['hidehumans'] ) {
-                       $conds[] = 'rc_bot = 1';
-               }
-               if ( $user->useRCPatrol() ) {
-                       if ( $opts['hidepatrolled'] ) {
-                               $conds[] = 'rc_patrolled = 0';
-                       }
-                       if ( $opts['hideunpatrolled'] ) {
-                               $conds[] = 'rc_patrolled = 1';
-                       }
-               }
-               if ( $botsonly ) {
-                       $conds['rc_bot'] = 1;
-               } else {
-                       if ( $opts['hideliu'] ) {
-                               $conds[] = 'rc_user = 0';
-                       }
-                       if ( $hideanons ) {
-                               $conds[] = 'rc_user != 0';
-                       }
-               }
-
-               if ( $opts['hidemyself'] ) {
-                       if ( $user->getId() ) {
-                               $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() );
-                       } else {
-                               $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
-                       }
-               }
-               if ( $opts['hidebyothers'] ) {
-                       if ( $user->getId() ) {
-                               $conds[] = 'rc_user = ' . $dbr->addQuotes( $user->getId() );
+               $context = $this->getContext();
+               foreach ( $this->filterGroups as $filterGroup ) {
+                       // URL parameters can be per-group, like 'userExpLevel',
+                       // or per-filter, like 'hideminor'.
+                       if ( $filterGroup->isPerGroupRequestParameter() ) {
+                               $filterGroup->modifyQuery( $dbr, $this, $tables, $fields, $conds,
+                                       $query_options, $join_conds, $opts[$filterGroup->getName()] );
                        } else {
-                               $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() );
+                               foreach ( $filterGroup->getFilters() as $filter ) {
+                                       if ( $opts[$filter->getName()] && $filter->isAllowed( $this ) ) {
+                                               $filter->modifyQuery( $dbr, $this, $tables, $fields, $conds,
+                                                       $query_options, $join_conds );
+                                       }
+                               }
                        }
                }
 
-               if ( $this->getConfig()->get( 'RCWatchCategoryMembership' )
-                       && $opts['hidecategorization'] === true
-               ) {
-                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE );
-               }
-               if ( $opts['hidepageedits'] ) {
-                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_EDIT );
-               }
-               if ( $opts['hidenewpages'] ) {
-                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_NEW );
-               }
-               if ( $opts['hidelog'] ) {
-                       $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_LOG );
-               }
-
                // Namespace filtering
                if ( $opts['namespace'] !== '' ) {
                        $selectedNS = $dbr->addQuotes( $opts['namespace'] );
@@ -327,22 +842,24 @@ abstract class ChangesListSpecialPage extends SpecialPage {
 
                        $conds[] = $condition;
                }
-
-               return $conds;
        }
 
        /**
         * Process the query
         *
-        * @param array $conds
+        * @param array $tables Array of tables; see IDatabase::select $table
+        * @param array $fields Array of fields; see IDatabase::select $vars
+        * @param array $conds Array of conditions; see IDatabase::select $conds
+        * @param array $query_options Array of query options; see IDatabase::select $options
+        * @param array $join_conds Array of join conditions; see IDatabase::select $join_conds
         * @param FormOptions $opts
         * @return bool|ResultWrapper Result or false
         */
-       public function doMainQuery( $conds, $opts ) {
-               $tables = [ 'recentchanges' ];
-               $fields = RecentChange::selectFields();
-               $query_options = [];
-               $join_conds = [];
+       protected function doMainQuery( $tables, $fields, $conds,
+               $query_options, $join_conds, FormOptions $opts ) {
+
+               $tables[] = 'recentchanges';
+               $fields = array_merge( RecentChange::selectFields(), $fields );
 
                ChangeTags::modifyDisplayQuery(
                        $tables,
@@ -353,6 +870,15 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        ''
                );
 
+               // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on
+               // what the user meant and either show only bots or force anons to be shown.
+
+               // -------
+
+               // XXX: We're no longer doing this handling.  To preserve back-compat, we need to complete
+               // T151873 (particularly the hideanons/hideliu/hidebots/hidehumans part) in conjunction
+               // with merging this.
+
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
                        $opts )
                ) {
@@ -456,7 +982,8 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        /**
         * Get options to be displayed in a form
         * @todo This should handle options returned by getDefaultOptions().
-        * @todo Not called by anything, should be called by something… doHeader() maybe?
+        * @todo Not called by anything in this class (but is in subclasses), should be
+        * called by something… doHeader() maybe?
         *
         * @param FormOptions $opts
         * @return array
@@ -533,21 +1060,78 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        }
 
        /**
-        * Get filters that can be rendered.
-        *
-        * Filters with 'msg' => false can be used to filter data but won't
-        * be presented as show/hide toggles in the UI. They are not returned
-        * by this function.
+        * Filter on users' experience levels; this will not be called if nothing is
+        * selected.
         *
-        * @param array $allFilters Map of filter URL param names to properties (msg/default)
-        * @return array Map of filter URL param names to properties (msg/default)
+        * @param string $specialPageClassName Class name of current special page
+        * @param IContextSource $context Context, for e.g. user
+        * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
+        * @param array &$tables Array of tables; see IDatabase::select $table
+        * @param array &$fields Array of fields; see IDatabase::select $vars
+        * @param array &$conds Array of conditions; see IDatabase::select $conds
+        * @param array &$query_options Array of query options; see IDatabase::select $options
+        * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
+        * @param array $selectedExpLevels The allowed active values, sorted
         */
-       protected function getRenderableCustomFilters( $allFilters ) {
-               return array_filter(
-                       $allFilters,
-                       function( $filter ) {
-                               return isset( $filter['msg'] ) && ( $filter['msg'] !== false );
-                       }
+       public function filterOnUserExperienceLevel( $specialPageClassName, $context, $dbr,
+               &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedExpLevels ) {
+
+               global $wgLearnerEdits,
+                          $wgExperiencedUserEdits,
+                          $wgLearnerMemberSince,
+                          $wgExperiencedUserMemberSince;
+
+               $LEVEL_COUNT = 3;
+
+               // If all levels are selected, all logged-in users are included (but no
+               // anons), so we can short-circuit.
+               if ( count( $selectedExpLevels ) === $LEVEL_COUNT ) {
+                       $conds[] = 'rc_user != 0';
+                       return;
+               }
+
+               $tables[] = 'user';
+               $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
+
+               $now = time();
+               $secondsPerDay = 86400;
+               $learnerCutoff = $now - $wgLearnerMemberSince * $secondsPerDay;
+               $experiencedUserCutoff = $now - $wgExperiencedUserMemberSince * $secondsPerDay;
+
+               $aboveNewcomer = $dbr->makeList(
+                       [
+                               'user_editcount >= ' . intval( $wgLearnerEdits ),
+                               'user_registration <= ' . $dbr->timestamp( $learnerCutoff ),
+                       ],
+                       IDatabase::LIST_AND
                );
+
+               $aboveLearner = $dbr->makeList(
+                       [
+                               'user_editcount >= ' . intval( $wgExperiencedUserEdits ),
+                               'user_registration <= ' . $dbr->timestamp( $experiencedUserCutoff ),
+                       ],
+                       IDatabase::LIST_AND
+               );
+
+               if ( $selectedExpLevels === [ 'newcomer' ] ) {
+                       $conds[] =  "NOT ( $aboveNewcomer )";
+               } elseif ( $selectedExpLevels === [ 'learner' ] ) {
+                       $conds[] = $dbr->makeList(
+                               [ $aboveNewcomer, "NOT ( $aboveLearner )" ],
+                               IDatabase::LIST_AND
+                       );
+               } elseif ( $selectedExpLevels === [ 'experienced' ] ) {
+                       $conds[] = $aboveLearner;
+               } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) {
+                       $conds[] = "NOT ( $aboveLearner )";
+               } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) {
+                       $conds[] = $dbr->makeList(
+                               [ "NOT ( $aboveNewcomer )", $aboveLearner ],
+                               IDatabase::LIST_OR
+                       );
+               } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
+                       $conds[] = $aboveNewcomer;
+               }
        }
 }
index eb29907..29e8900 100644 (file)
@@ -53,7 +53,8 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
 
                // 10 seconds server-side caching max
-               $this->getOutput()->setCdnMaxage( 10 );
+               $out = $this->getOutput();
+               $out->setCdnMaxage( 10 );
                // Check if the client has a cached version
                $lastmod = $this->checkLastModified();
                if ( $lastmod === false ) {
@@ -65,6 +66,65 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        true
                );
                parent::execute( $subpage );
+
+               if ( $this->isStructuredFilterUiEnabled() ) {
+                       $jsData = $this->getStructuredFilterJsData();
+
+                       $messages = [];
+                       foreach ( $jsData['messageKeys'] as $key ){
+                               $messages[$key] = $this->msg( $key )->plain();
+                       }
+
+                       $out->addHTML(
+                               ResourceLoader::makeInlineScript(
+                                       Xml::encodeJsCall( 'mw.messages.set', [
+                                               $messages
+                                       ] )
+                               )
+                       );
+
+                       $out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] );
+               }
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function registerFiltersFromDefinitions( array $definition ) {
+               foreach ( $definition as $groupName => &$groupDefinition ) {
+                       foreach ( $groupDefinition['filters'] as &$filterDefinition ) {
+                               if ( isset( $filterDefinition['showHideSuffix'] ) ) {
+                                       $filterDefinition['showHide'] = 'rc' . $filterDefinition['showHideSuffix'];
+                               }
+                       }
+               }
+
+               parent::registerFiltersFromDefinitions( $definition );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function registerFilters() {
+               parent::registerFilters();
+
+               $user = $this->getUser();
+
+               $significance = $this->getFilterGroup( 'significance' );
+               $hideMinor = $significance->getFilter( 'hideminor' );
+               $hideMinor->setDefault( $user->getBoolOption( 'hideminor' ) );
+
+               $automated = $this->getFilterGroup( 'automated' );
+               $hideBots = $automated->getFilter( 'hidebots' );
+               $hideBots->setDefault( true );
+
+               $reviewStatus = $this->getFilterGroup( 'reviewStatus' );
+               $hidePatrolled = $reviewStatus->getFilter( 'hidepatrolled' );
+               $hidePatrolled->setDefault( $user->getBoolOption( 'hidepatrolled' ) );
+
+               $changeType = $this->getFilterGroup( 'changeType' );
+               $hideCategorization = $changeType->getFilter( 'hidecategorization' );
+               $hideCategorization->setDefault( $user->getBoolOption( 'hidecategorization' ) );
        }
 
        /**
@@ -80,20 +140,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
-               $opts->add( 'hideminor', $user->getBoolOption( 'hideminor' ) );
-               $opts->add( 'hidebots', true );
-               $opts->add( 'hideanons', false );
-               $opts->add( 'hideliu', false );
-               $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
-               $opts->add( 'hidemyself', false );
-               $opts->add( 'hidecategorization', $user->getBoolOption( 'hidecategorization' ) );
-
                $opts->add( 'categories', '' );
                $opts->add( 'categories_any', false );
                $opts->add( 'tagfilter', '' );
 
-               $opts->add( 'userExpLevel', 'all' );
-
                return $opts;
        }
 
@@ -118,36 +168,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         * @param FormOptions $opts
         */
        public function parseParameters( $par, FormOptions $opts ) {
+               parent::parseParameters( $par, $opts );
+
                $bits = preg_split( '/\s*,\s*/', trim( $par ) );
                foreach ( $bits as $bit ) {
-                       if ( 'hidebots' === $bit ) {
-                               $opts['hidebots'] = true;
-                       }
-                       if ( 'bots' === $bit ) {
-                               $opts['hidebots'] = false;
-                       }
-                       if ( 'hideminor' === $bit ) {
-                               $opts['hideminor'] = true;
-                       }
-                       if ( 'minor' === $bit ) {
-                               $opts['hideminor'] = false;
-                       }
-                       if ( 'hideliu' === $bit ) {
-                               $opts['hideliu'] = true;
-                       }
-                       if ( 'hidepatrolled' === $bit ) {
-                               $opts['hidepatrolled'] = true;
-                       }
-                       if ( 'hideanons' === $bit ) {
-                               $opts['hideanons'] = true;
-                       }
-                       if ( 'hidemyself' === $bit ) {
-                               $opts['hidemyself'] = true;
-                       }
-                       if ( 'hidecategorization' === $bit ) {
-                               $opts['hidecategorization'] = true;
-                       }
-
                        if ( is_numeric( $bit ) ) {
                                $opts['limit'] = $bit;
                        }
@@ -174,14 +198,14 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        /**
-        * Return an array of conditions depending of options set in $opts
-        *
-        * @param FormOptions $opts
-        * @return array
+        * @inheritdoc
         */
-       public function buildMainQueryConds( FormOptions $opts ) {
+       protected function buildQuery( &$tables, &$fields, &$conds,
+               &$query_options, &$join_conds, FormOptions $opts ) {
+
                $dbr = $this->getDB();
-               $conds = parent::buildMainQueryConds( $opts );
+               parent::buildQuery( $tables, $fields, $conds,
+                       $query_options, $join_conds, $opts );
 
                // Calculate cutoff
                $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
@@ -196,25 +220,19 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
 
                $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff );
-
-               return $conds;
        }
 
        /**
-        * Process the query
-        *
-        * @param array $conds
-        * @param FormOptions $opts
-        * @return bool|ResultWrapper Result or false (for Recentchangeslinked only)
+        * @inheritdoc
         */
-       public function doMainQuery( $conds, $opts ) {
+       protected function doMainQuery( $tables, $fields, $conds, $query_options,
+               $join_conds, FormOptions $opts ) {
+
                $dbr = $this->getDB();
                $user = $this->getUser();
 
-               $tables = [ 'recentchanges' ];
-               $fields = RecentChange::selectFields();
-               $query_options = [];
-               $join_conds = [];
+               $tables[] = 'recentchanges';
+               $fields = array_merge( RecentChange::selectFields(), $fields );
 
                // JOIN on watchlist for users
                if ( $user->getId() && $user->isAllowed( 'viewmywatchlist' ) ) {
@@ -243,8 +261,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $opts['tagfilter']
                );
 
-               $this->filterOnUserExperienceLevel( $tables, $conds, $join_conds, $opts );
-
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
                        $opts )
                ) {
@@ -331,7 +347,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $dbr = $this->getDB();
 
                $counter = 1;
-               $list = ChangesList::newFromContext( $this->getContext() );
+               $list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
                $list->initChangesListRows( $rows );
 
                $userShowHiddenCats = $this->getUser()->getBoolOption( 'showhiddencats' );
@@ -537,6 +553,17 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                return $extraOpts;
        }
 
+       /**
+        * Check whether the structured filter UI is enabled
+        *
+        * @return bool
+        */
+       protected function isStructuredFilterUiEnabled() {
+               return $this->getUser()->getOption(
+                       'rcenhancedfilters'
+               );
+       }
+
        /**
         * Add page-specific modules.
         */
@@ -544,7 +571,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                parent::addModules();
                $out = $this->getOutput();
                $out->addModules( 'mediawiki.special.recentchanges' );
-               if ( $this->getUser()->getOption( 'rcenhancedfilters' ) ) {
+               if ( $this->isStructuredFilterUiEnabled() ) {
                        $out->addModules( 'mediawiki.rcfilters.filters.ui' );
                        $out->addModuleStyles( 'mediawiki.rcfilters.filters.base.styles' );
                }
@@ -760,49 +787,45 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
                $dl = $lang->pipeList( $dl );
 
-               // show/hide links
-               $filters = [
-                       'hideminor' => 'rcshowhideminor',
-                       'hidebots' => 'rcshowhidebots',
-                       'hideanons' => 'rcshowhideanons',
-                       'hideliu' => 'rcshowhideliu',
-                       'hidepatrolled' => 'rcshowhidepatr',
-                       'hidemyself' => 'rcshowhidemine'
-               ];
-
-               if ( $config->get( 'RCWatchCategoryMembership' ) ) {
-                       $filters['hidecategorization'] = 'rcshowhidecategorization';
-               }
-
                $showhide = [ 'show', 'hide' ];
 
-               foreach ( $this->getRenderableCustomFilters( $this->getCustomFilters() ) as $key => $params ) {
-                       $filters[$key] = $params['msg'];
-               }
-
-               // Disable some if needed
-               if ( !$user->useRCPatrol() ) {
-                       unset( $filters['hidepatrolled'] );
-               }
-
                $links = [];
-               foreach ( $filters as $key => $msg ) {
-                       // The following messages are used here:
-                       // rcshowhideminor-show, rcshowhideminor-hide, rcshowhidebots-show, rcshowhidebots-hide,
-                       // rcshowhideanons-show, rcshowhideanons-hide, rcshowhideliu-show, rcshowhideliu-hide,
-                       // rcshowhidepatr-show, rcshowhidepatr-hide, rcshowhidemine-show, rcshowhidemine-hide,
-                       // rcshowhidecategorization-show, rcshowhidecategorization-hide.
-                       $linkMessage = $this->msg( $msg . '-' . $showhide[1 - $options[$key]] );
-                       // Extensions can define additional filters, but don't need to define the corresponding
-                       // messages. If they don't exist, just fall back to 'show' and 'hide'.
-                       if ( !$linkMessage->exists() ) {
-                               $linkMessage = $this->msg( $showhide[1 - $options[$key]] );
-                       }
 
-                       $link = $this->makeOptionsLink( $linkMessage->text(),
-                               [ $key => 1 - $options[$key] ], $nondefaults );
-                       $links[] = "<span class=\"$msg rcshowhideoption\">"
-                               . $this->msg( $msg )->rawParams( $link )->escaped() . '</span>';
+               $filterGroups = $this->getFilterGroups();
+
+               $context = $this->getContext();
+               foreach ( $filterGroups as $groupName => $group ) {
+                       if ( !$group->isPerGroupRequestParameter() ) {
+                               foreach ( $group->getFilters() as $key => $filter ) {
+                                       if ( $filter->displaysOnUnstructuredUi( $this ) ) {
+                                               $msg = $filter->getShowHide();
+                                               $linkMessage = $this->msg( $msg . '-' . $showhide[1 - $options[$key]] );
+                                               // Extensions can define additional filters, but don't need to define the corresponding
+                                               // messages. If they don't exist, just fall back to 'show' and 'hide'.
+                                               if ( !$linkMessage->exists() ) {
+                                                       $linkMessage = $this->msg( $showhide[1 - $options[$key]] );
+                                               }
+
+                                               $link = $this->makeOptionsLink( $linkMessage->text(),
+                                                       [ $key => 1 - $options[$key] ], $nondefaults );
+
+                                               $attribs = [
+                                                       'class' => "$msg rcshowhideoption",
+                                                       'data-filter-name' => $filter->getName(),
+                                               ];
+
+                                               if ( $filter->isFeatureAvailableOnStructuredUi( $this ) ) {
+                                                       $attribs['data-feature-in-structured-ui'] = true;
+                                               }
+
+                                               $links[] = Html::rawElement(
+                                                       'span',
+                                                       $attribs,
+                                                       $this->msg( $msg )->rawParams( $link )->escaped()
+                                               );
+                                       }
+                               }
+                       }
                }
 
                // show from this onward link
@@ -831,66 +854,4 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        protected function getCacheTTL() {
                return 60 * 5;
        }
-
-       function filterOnUserExperienceLevel( &$tables, &$conds, &$join_conds, $opts ) {
-               global $wgLearnerEdits,
-                       $wgExperiencedUserEdits,
-                       $wgLearnerMemberSince,
-                       $wgExperiencedUserMemberSince;
-
-               $selectedExpLevels = explode( ',', strtolower( $opts['userExpLevel'] ) );
-               // remove values that are not recognized
-               $selectedExpLevels = array_intersect(
-                       $selectedExpLevels,
-                       [ 'newcomer', 'learner', 'experienced' ]
-               );
-               sort( $selectedExpLevels );
-
-               if ( $selectedExpLevels ) {
-                       $tables[] = 'user';
-                       $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
-
-                       $now = time();
-                       $secondsPerDay = 86400;
-                       $learnerCutoff = $now - $wgLearnerMemberSince * $secondsPerDay;
-                       $experiencedUserCutoff = $now - $wgExperiencedUserMemberSince * $secondsPerDay;
-
-                       $aboveNewcomer = $this->getDB()->makeList(
-                               [
-                                       'user_editcount >= ' . intval( $wgLearnerEdits ),
-                                       'user_registration <= ' . $this->getDB()->timestamp( $learnerCutoff ),
-                               ],
-                               IDatabase::LIST_AND
-                       );
-
-                       $aboveLearner = $this->getDB()->makeList(
-                               [
-                                       'user_editcount >= ' . intval( $wgExperiencedUserEdits ),
-                                       'user_registration <= ' . $this->getDB()->timestamp( $experiencedUserCutoff ),
-                               ],
-                               IDatabase::LIST_AND
-                       );
-
-                       if ( $selectedExpLevels === [ 'newcomer' ] ) {
-                               $conds[] =  "NOT ( $aboveNewcomer )";
-                       } elseif ( $selectedExpLevels === [ 'learner' ] ) {
-                               $conds[] = $this->getDB()->makeList(
-                                       [ $aboveNewcomer, "NOT ( $aboveLearner )" ],
-                                       IDatabase::LIST_AND
-                               );
-                       } elseif ( $selectedExpLevels === [ 'experienced' ] ) {
-                               $conds[] = $aboveLearner;
-                       } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) {
-                               $conds[] = "NOT ( $aboveLearner )";
-                       } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) {
-                               $conds[] = $this->getDB()->makeList(
-                                       [ "NOT ( $aboveNewcomer )", $aboveLearner ],
-                                       IDatabase::LIST_OR
-                               );
-                       } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
-                               $conds[] = $aboveNewcomer;
-                       }
-               }
-       }
-
 }
index aab0f6d..873285b 100644 (file)
@@ -46,7 +46,12 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                $opts['target'] = $par;
        }
 
-       public function doMainQuery( $conds, $opts ) {
+       /**
+        * @inheritdoc
+        */
+       protected function doMainQuery( $tables, $select, $conds, $query_options,
+               $join_conds, FormOptions $opts ) {
+
                $target = $opts['target'];
                $showlinkedto = $opts['showlinkedto'];
                $limit = $opts['limit'];
@@ -79,10 +84,8 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                $ns = $title->getNamespace();
                $dbkey = $title->getDBkey();
 
-               $tables = [ 'recentchanges' ];
-               $select = RecentChange::selectFields();
-               $join_conds = [];
-               $query_options = [];
+               $tables[] = 'recentchanges';
+               $select = array_merge( RecentChange::selectFields(), $select );
 
                // left join with watchlist table to highlight watched rows
                $uid = $this->getUser()->getId();
index 822648b..5d7fa5d 100644 (file)
@@ -104,6 +104,56 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                ];
        }
 
+       /**
+        * @inheritdoc
+        */
+       protected function registerFiltersFromDefinitions( array $definition ) {
+               foreach ( $definition as $groupName => &$groupDefinition ) {
+                       foreach ( $groupDefinition['filters'] as &$filterDefinition ) {
+                               if ( isset( $filterDefinition['showHideSuffix'] ) ) {
+                                       $filterDefinition['showHide'] = 'wl' . $filterDefinition['showHideSuffix'];
+                               }
+                       }
+               }
+
+               parent::registerFiltersFromDefinitions( $definition );
+       }
+
+       /**
+        * @inheritdoc
+        */
+       protected function registerFilters() {
+               parent::registerFilters();
+
+               $user = $this->getUser();
+
+               $significance = $this->getFilterGroup( 'significance' );
+               $hideMinor = $significance->getFilter( 'hideminor' );
+               $hideMinor->setDefault( $user->getBoolOption( 'watchlisthideminor' ) );
+
+               $automated = $this->getFilterGroup( 'automated' );
+               $hideBots = $automated->getFilter( 'hidebots' );
+               $hideBots->setDefault( $user->getBoolOption( 'watchlisthidebots' ) );
+
+               $registration = $this->getFilterGroup( 'registration' );
+               $hideAnons = $registration->getFilter( 'hideanons' );
+               $hideAnons->setDefault( $user->getBoolOption( 'watchlisthideanons' ) );
+               $hideLiu = $registration->getFilter( 'hideliu' );
+               $hideLiu->setDefault( $user->getBoolOption( 'watchlisthideliu' ) );
+
+               $reviewStatus = $this->getFilterGroup( 'reviewStatus' );
+               $hidePatrolled = $reviewStatus->getFilter( 'hidepatrolled' );
+               $hidePatrolled->setDefault( $user->getBoolOption( 'watchlisthidepatrolled' ) );
+
+               $authorship = $this->getFilterGroup( 'authorship' );
+               $hideMyself = $authorship->getFilter( 'hidemyself' );
+               $hideMyself->setDefault( $user->getBoolOption( 'watchlisthideown' ) );
+
+               $changeType = $this->getFilterGroup( 'changeType' );
+               $hideCategorization = $changeType->getFilter( 'hidecategorization' );
+               $hideCategorization->setDefault( $user->getBoolOption( 'watchlisthidecategorization' ) );
+       }
+
        /**
         * Get a FormOptions object containing the default options
         *
@@ -120,14 +170,6 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        return $opts;
                }
 
-               $opts->add( 'hideminor', $user->getBoolOption( 'watchlisthideminor' ) );
-               $opts->add( 'hidebots', $user->getBoolOption( 'watchlisthidebots' ) );
-               $opts->add( 'hideanons', $user->getBoolOption( 'watchlisthideanons' ) );
-               $opts->add( 'hideliu', $user->getBoolOption( 'watchlisthideliu' ) );
-               $opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
-               $opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
-               $opts->add( 'hidecategorization', $user->getBoolOption( 'watchlisthidecategorization' ) );
-
                return $opts;
        }
 
@@ -181,32 +223,28 @@ class SpecialWatchlist extends ChangesListSpecialPage {
        }
 
        /**
-        * Return an array of conditions depending of options set in $opts
-        *
-        * @param FormOptions $opts
-        * @return array
+        * @inheritdoc
         */
-       public function buildMainQueryConds( FormOptions $opts ) {
+       protected function buildQuery( &$tables, &$fields, &$conds, &$query_options,
+               &$join_conds, FormOptions $opts ) {
+
                $dbr = $this->getDB();
-               $conds = parent::buildMainQueryConds( $opts );
+               parent::buildQuery( $tables, $fields, $conds, $query_options, $join_conds,
+                       $opts );
 
                // Calculate cutoff
                if ( $opts['days'] > 0 ) {
                        $conds[] = 'rc_timestamp > ' .
                                $dbr->addQuotes( $dbr->timestamp( time() - intval( $opts['days'] * 86400 ) ) );
                }
-
-               return $conds;
        }
 
        /**
-        * Process the query
-        *
-        * @param array $conds
-        * @param FormOptions $opts
-        * @return bool|ResultWrapper Result or false (for Recentchangeslinked only)
+        * @inheritdoc
         */
-       public function doMainQuery( $conds, $opts ) {
+       protected function doMainQuery( $tables, $fields, $conds, $query_options,
+               $join_conds, FormOptions $opts ) {
+
                $dbr = $this->getDB();
                $user = $this->getUser();
 
@@ -231,19 +269,23 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        $usePage = true;
                }
 
-               $tables = [ 'recentchanges', 'watchlist' ];
-               $fields = RecentChange::selectFields();
-               $query_options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-               $join_conds = [
-                       'watchlist' => [
-                               'INNER JOIN',
-                               [
-                                       'wl_user' => $user->getId(),
-                                       'wl_namespace=rc_namespace',
-                                       'wl_title=rc_title'
+               $tables = array_merge( [ 'recentchanges', 'watchlist' ], $tables );
+               $fields = array_merge( RecentChange::selectFields(), $fields );
+
+               $query_options = array_merge( [ 'ORDER BY' => 'rc_timestamp DESC' ], $query_options );
+               $join_conds = array_merge(
+                       [
+                               'watchlist' => [
+                                       'INNER JOIN',
+                                       [
+                                               'wl_user' => $user->getId(),
+                                               'wl_namespace=rc_namespace',
+                                               'wl_title=rc_title'
+                                       ],
                                ],
                        ],
-               ];
+                       $join_conds
+               );
 
                if ( $this->getConfig()->get( 'ShowUpdatedMarker' ) ) {
                        $fields[] = 'wl_notificationtimestamp';
@@ -361,7 +403,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
 
                $dbr->dataSeek( $rows, 0 );
 
-               $list = ChangesList::newFromContext( $this->getContext() );
+               $list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
                $list->setWatchlistDivs();
                $list->initChangesListRows( $rows );
                $dbr->dataSeek( $rows, 0 );
@@ -448,31 +490,23 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $cutofflinks = $this->msg( 'wlshowtime' ) . ' ' . $this->cutoffselector( $opts );
 
                # Spit out some control panel links
-               $filters = [
-                       'hideminor' => 'wlshowhideminor',
-                       'hidebots' => 'wlshowhidebots',
-                       'hideanons' => 'wlshowhideanons',
-                       'hideliu' => 'wlshowhideliu',
-                       'hidemyself' => 'wlshowhidemine',
-                       'hidepatrolled' => 'wlshowhidepatr'
-               ];
-
-               if ( $this->getConfig()->get( 'RCWatchCategoryMembership' ) ) {
-                       $filters['hidecategorization'] = 'wlshowhidecategorization';
-               }
-
-               foreach ( $this->getRenderableCustomFilters( $this->getCustomFilters() ) as $key => $params ) {
-                       $filters[$key] = $params['msg'];
-               }
-
-               // Disable some if needed
-               if ( !$user->useRCPatrol() ) {
-                       unset( $filters['hidepatrolled'] );
-               }
-
                $links = [];
-               foreach ( $filters as $name => $msg ) {
-                       $links[] = $this->showHideCheck( $nondefaults, $msg, $name, $opts[$name] );
+               $context = $this->getContext();
+               $namesOfDisplayedFilters = [];
+               foreach ( $this->getFilterGroups() as $groupName => $group ) {
+                       if ( !$group->isPerGroupRequestParameter() ) {
+                               foreach ( $group->getFilters() as $filterName => $filter ) {
+                                       if ( $filter->displaysOnUnstructuredUi( $this ) ) {
+                                               $namesOfDisplayedFilters[] = $filterName;
+                                               $links[] = $this->showHideCheck(
+                                                       $nondefaults,
+                                                       $filter->getShowHide(),
+                                                       $filterName,
+                                                       $opts[$filterName]
+                                               );
+                                       }
+                               }
+                       }
                }
 
                $hiddenFields = $nondefaults;
@@ -481,8 +515,8 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                unset( $hiddenFields['invert'] );
                unset( $hiddenFields['associated'] );
                unset( $hiddenFields['days'] );
-               foreach ( $filters as $key => $value ) {
-                       unset( $hiddenFields[$key] );
+               foreach ( $namesOfDisplayedFilters as $filterName ) {
+                       unset( $hiddenFields[$filterName] );
                }
 
                # Create output
index 0d71629..49510da 100644 (file)
@@ -31,7 +31,6 @@ class SearchInputWidget extends TitleInputWidget {
        public function __construct( array $config = [] ) {
                $config = array_merge( [
                        'maxLength' => null,
-                       'type' => 'search',
                        'icon' => 'search',
                ], $config );
 
@@ -56,6 +55,10 @@ class SearchInputWidget extends TitleInputWidget {
                $this->addClasses( [ 'mw-widget-searchInputWidget' ] );
        }
 
+       protected function getInputElement( $config ) {
+               return ( new \OOUI\Tag( 'input' ) )->setAttributes( [ 'type' => 'search' ] );
+       }
+
        protected function getJavaScriptClassName() {
                return 'mw.widgets.SearchInputWidget';
        }
index 76b9b01..b1b8c73 100644 (file)
@@ -161,7 +161,7 @@ class InterwikiSearchResultSetWidget implements SearchResultSetWidget {
                $searchLink = Html::rawElement(
                        'a',
                        [ 'href' => $href ],
-                       $this->specialSearch->msg( 'search-interwiki-more' )->escaped()
+                       $this->specialSearch->msg( 'search-interwiki-more-results' )->escaped()
                );
 
                return Html::rawElement( 'div', [ 'class' => 'iw-result__footer' ], $searchLink );
index 2c132c5..524d511 100644 (file)
@@ -38,6 +38,7 @@
        "editfont-sansserif": "خط بلا زوائد",
        "editfont-serif": "خط بزوائد",
        "sunday": "الأحد",
+       "monday": "الإثنين",
        "tuesday": "الثلاث",
        "wednesday": "الأربعا",
        "thursday": "الخميس",
        "cancel": "بطّل",
        "mypage": "صفحتي",
        "mytalk": "نقاشي",
+       "qbedit": "بدّل",
        "returnto": "إرجع لـ$1",
        "tagline": "من {{SITENAME}}",
        "help": "معاونة",
        "youhavenewmessages": "توجد لديك $1 ($2).",
        "youhavenewmessagesmulti": "لديك رسائل جديدة على $1",
        "editsection": "بدّل",
+       "editold": "بدّل",
        "editlink": "بدّل",
        "viewsourcelink": "شوف المصدر",
        "editsectionhint": "بدّل الطرف: $1",
        "createaccountmail": "بواسطة البريد الإلكتروني",
        "createacct-submit": "أعمل الحساب",
        "createacct-benefit-heading": "{{SITENAME}} إتّعمل بعباد كيفك.",
+       "createacct-benefit-body1": "{{PLURAL:$1|تبديل|تبديلات|تبديلات|تبديلات|تبديلات|تبديلات|تبديلات|تبديلات|تبديلات|تبديلات|تبديل}}",
        "createacct-benefit-body3": "آخر {{PLURAL:$1|مساهم|مساهمين}}",
        "badretype": "كلمات السر التي أدخلتها لا تتطابق.",
        "userexists": "اسم المستخدم الذي تم إدخاله مستعمل بالفعل.\nالرجاء اختيار اسم مختلف.",
        "preview": "معاينة",
        "showpreview": "أظهر معاينة",
        "showdiff": "ورّي التبديلات",
-       "anoneditwarning": "'''تحذÙ\8aر:''' Ù\84Ù\85 ØªÙ\82Ù\85 Ø¨Ø§Ù\84دخÙ\88Ù\84.\nسÙ\8aسجÙ\84 Ø¹Ù\86Ù\88اÙ\86 Ø§Ù\84Ø¢Ù\8aبÙ\8a Ø®Ø§ØµØªÙ\83 Ù\81Ù\8a ØªØ§Ø±Ù\8aØ® Ù\87Ø°Ù\87 Ø§Ù\84صÙ\81حة.",
+       "anoneditwarning": "'''ردÙ\91 Ø¨Ø§Ù\84Ù\83:''' Ø±Ø§Ù\83 Ù\85ا Ø¯Ø®Ù\84تش Ù\84Ù\84حساب Ù\85تاعÙ\83.\nإذا Ø¨Ø¯Ù\91Ù\84ت Ø­Ø§Ø¬Ø©Ø\8c Ù\8aÙ\86جÙ\91Ù\85Ù\88ا Ø§Ù\84عباد Ø§Ù\84Ù\83Ù\84Ù\91 Ù\8aعرÙ\81Ù\88ا Ø§Ù\84Ø¥Ù\8a Ù¾Ø§Ù\8a Ù\85تاعÙ\83. Ø£Ù\85ا Ø¥Ø°Ø§ <strong>[$1 Ø¯Ø®Ù\84ت Ù\84حسابÙ\83]</strong> Ù\88Ù\84Ù\91ا <strong>[$2 Ø¹Ù\85Ù\84ت Ø­Ø³Ø§Ø¨ Ø¬Ø¯Ù\8aد]</strong>Ø\8c Ø§Ù\84تبدÙ\8aÙ\84ات Ù\85تاعÙ\83 Ø§Ù\84Ù\83Ù\84 Ø¨Ø´ Ù\8aتÙ\86سبÙ\88ا Ù\84إسÙ\85 Ø§Ù\84Ù\85ستعÙ\85Ù\84 Ù\85تاعÙ\83Ø\8c Ù\88 Ø¨Ø®Ù\84اÙ\81 Ù\87ذا ØªÙ\86جÙ\91Ù\85 ØªØ¹Ù\85Ù\84 Ø¨Ø±Ø´Ø§ Ø­Ù\88اÙ\8aج Ù\85ا ØªÙ\86جÙ\91Ù\85Ø´ ØªØ¹Ù\85Ù\84Ù\87Ù\85 Ù\88Ù\82ت Ø¥Ù\84Ù\91Ù\8a ØªØ¨Ø¯Ù\91Ù\84 Ø­Ø§Ø¬Ø© Ø¨Ø§Ù\84Ø¥Ù\8a Ù¾Ø§Ù\8a Ù\85تاعÙ\83.",
        "anonpreviewwarning": "''أنت غير مسجل الدخول. الحفظ سيسجل عنوان الأيبي الخاص بك في تاريخ هذه الصفحة.''",
        "missingsummary": "'''تنبيه:''' لم تقم بكتابة ملخص للتعديل.\nإذا قمت بضغط حفظ الصفحة مرة أخرى، فيتم حفظ تعديلك بدون ملخص.",
        "missingcommenttext": "من فضلك أدخل تعليقا في الأسفل.",
        "rcshowhidemine": "$1 تعديلاتي",
        "rcshowhidemine-hide": "خبّي",
        "rclinks": "أظهر آخر $1 تعديل في آخر $2 يوم<br />$3",
+       "diff": "فرق",
        "hist": "تاريخ",
        "hide": "أخف",
        "show": "ورّي",
        "nolinkstoimage": "لا توجد صفحات تصل لهذا الملف.",
        "sharedupload-desc-here": "هذا الملف من $1 ويمكن استخدامه بواسطة المشاريع الأخرى.\nالوصف على [$2 صفحة وصف الملف] هناك معروض بالأسفل.",
        "upload-disallowed-here": "ما تنجّمش تبدّل هذي التصويرة.",
+       "randompage": "پاج بالزهر",
        "statistics": "إحصاءات",
+       "brokenredirects-edit": "بدّل",
        "nmembers": "{{PLURAL:$1|حتى عضو|عضو واحد||زوز أعضاء|$1 عضو}}",
        "prefixindex": "كل الصفحات بالبادئة",
        "usercreated": "{{GENDER:$3|أنشأه|أنشأته}} في $1 الساعة $2",
        "rollbacklink": "رجّع",
        "protectlogpage": "سجل الحماية",
        "protectedarticle": "حمى \"[[$1]]\"",
+       "restriction-edit": "تبديل",
        "undeletelink": "اعرض/استعد",
        "undeleteviewlink": "اعرض",
        "namespace": "النطاق",
        "tooltip-n-mainpage-description": "شوف الپاج الأولانيّة",
        "tooltip-n-randompage": "خرّج پاج بالزهر",
        "tooltip-feed-atom": "تلقيم أتوم لهذه الصفحة",
-       "tooltip-t-contributions": "رؤية قائمة مساهمات هذا المستخدم",
+       "tooltip-t-contributions": "ليستة مساهمات ها {{GENDER:$1|المستعمل|المستعملة}}",
        "tooltip-t-emailuser": "أرسل رسالة لهذا المستخدم",
        "tooltip-t-upload": "صبّ فيشيايات",
        "tooltip-ca-nstab-user": "اعرض صفحة المستخدم",
        "duplicate-defaultsort": "'''تحذير:''' مفتاح الترتيب الافتراضي \"$2\" يتجاوز مفتاح الترتيب الافتراضي السابق \"$1\".",
        "external_image_whitelist": " #<pre>اترك هذا السطر تماما كما هو\n#ضع منثورات التعبيرات المنتظمة (فقط الجزء الذي يذهب بين //) بالأسفل\n#هذه ستتم مطابقتها مع مسارات الصور الخرجية (الموصولة بشكل مباشر)\n#هذه التي تطابق سيتم عرضها كصور، غير ذلك فقط وصلة إلى الصورة سيتم عرضها\n#السطور التي تبدأ ب# تتم معاملتها كتعليقات\n#هذا لا يتأثر بحالة الحروف\n\n#ضع كل منثورات التعبيرات المنتظمة فوق هذا السطر. اترك هذا السطر تماما كما هو</pre>",
        "tag-filter": "مرشح [[Special:Tags|الوسوم]]:",
+       "tags-edit": "بدّل",
        "logentry-newusers-create": "إتحلّ حساب {{GENDER:$2|المستعمل|المستعملة}} $1",
        "searchsuggest-search": "لوّج في {{SITENAME}}"
 }
index d435dbb..3a9db73 100644 (file)
@@ -27,7 +27,7 @@
                ]
        },
        "tog-underline": "Onderstreep skakels.",
-       "tog-hideminor": "Moenie klein wysigings in die onlangse wysigingslys wys nie.",
+       "tog-hideminor": "Versteek klein wysigings in die onlangse wysigingslys.",
        "tog-hidepatrolled": "Versteek gepatrolleerde wysigings in onlangse wysigingslys",
        "tog-newpageshidepatrolled": "Versteek gepatrolleerde wysigings van nuwe bladsy lys",
        "tog-extendwatchlist": "Brei dophoulys uit om alle wysigings te wys, nie slegs die nuutste nie",
        "externaldberror": "'n Databasisfout het tydens aanmelding voorgekom of u het nie toestemming om u eksterne rekening op te dateer nie.",
        "login": "Meld aan",
        "nav-login-createaccount": "Meld aan / registreer",
-       "userlogin": "Meld aan / registreer",
-       "userloginnocreate": "Meld aan",
        "logout": "Teken uit",
        "userlogout": "Teken uit",
        "notloggedin": "Nie ingeteken nie",
        "userlogin-noaccount": "Nog nie geregistreer nie?",
        "userlogin-joinproject": "Word lid van {{SITENAME}}",
-       "nologin": "Nog nie geregistreer nie? $1.",
-       "nologinlink": "Skep gerus 'n gebruiker",
        "createaccount": "Skep nuwe rekening",
-       "gotaccount": "Het u reeds 'n rekening? $1.",
-       "gotaccountlink": "Meld aan",
-       "userlogin-resetlink": "U besonderhede vergeet?",
        "userlogin-resetpassword-link": "Wagwoord vergeet?",
        "userlogin-helplink2": "Hulp met aanmelding",
        "userlogin-loggedin": "U is reeds aangemeld as {{GENDER:$1|$1}}.\nGebruik die onderstaande vorm om as 'n ander gebruiker aan te meld.",
        "createacct-another-email-ph": "Verskaf e-posadres",
        "createaccountmail": "Gebruik 'n tydelike lukrake wagwoord en stuur dit na die e-posadres hier onder",
        "createacct-realname": "Regte naam (opsioneel)",
-       "createaccountreason": "Rede:",
        "createacct-reason": "Rede",
        "createacct-reason-ph": "Hoekom u nog 'n rekening skep",
        "createacct-submit": "Skep u rekening",
        "saveprefs": "Stoor voorkeure",
        "restoreprefs": "Herstel voorkeure",
        "prefs-editing": "Wysigings",
-       "rows": "Rye",
-       "columns": "Kolomme",
        "searchresultshead": "Soekresultate",
        "stub-threshold": "Drempel vir merk as <a href=\"#\" class=\"stub\">saadjie</a> (grepe):",
        "stub-threshold-sample-link": "voorbeeld",
        "sp-contributions-blocked-notice-anon": "Hierdie IP-adres is tans geblokkeer.\nDie laaste inskrywing uit die blokkeerlogboek word hier ter inligting weergegee:",
        "sp-contributions-search": "Soek na bydraes",
        "sp-contributions-username": "IP-adres of gebruikersnaam:",
-       "sp-contributions-toponly": "Wys slegs die nuutste weergawes",
+       "sp-contributions-toponly": "Slegs die nuutste weergawes",
+       "sp-contributions-newonly": "Slegs nuwe bladsye",
+       "sp-contributions-hideminor": "Versteek klein wysigings",
        "sp-contributions-submit": "Soek",
        "whatlinkshere": "Skakels hierheen",
        "whatlinkshere-title": "Bladsye wat na \"$1\" skakel",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|het}} 'n nuwe weergawe van $3 opgelaai",
        "logentry-upload-revert": "$1 het $3 {{GENDER:$2|opgelaai}}",
        "rightsnone": "(geen)",
-       "revdelete-summary": "redigeringsopsomming",
        "feedback-adding": "U terugvoer word op die bladsy geplaas...",
        "feedback-back": "Terug",
        "feedback-bugcheck": "Gaaf! Kontroleer of dit nie dalk een van die [$1 bekende foute] is nie.",
        "feedback-thanks-title": "Baie dankie!",
        "searchsuggest-search": "Soek",
        "searchsuggest-containing": "bevat...",
-       "api-error-badaccess-groups": "U word nie toegelaat om lêers te laai op hierdie wiki.",
        "api-error-badtoken": "Interne fout: slegte teken.",
-       "api-error-copyuploaddisabled": "Oplaai via URL is gedeaktiveer op hierdie bediener.",
-       "api-error-duplicate": "Daar {{PLURAL:$1|is al 'n lêer|is al lêers}} met dieselfde inhoud op die wiki.",
-       "api-error-duplicate-archive": "Daar {{PLURAL:$1|was 'n ander lêer|was ander lêers}} op hierdie webtuiste met dieselfde inhoud, maar {{PLURAL:$1|dit is|dit is}} geskrap.",
-       "api-error-empty-file": "Die lêer wat u probeer oplaai is leeg.",
        "api-error-emptypage": "Die skep van leë nuwe bladsye word nie toegelaat nie.",
-       "api-error-fetchfileerror": "Interne fout: Iets het verkeerd gegaan met die haal van die lêer.",
-       "api-error-fileexists-forbidden": "Daar is reeds 'n lêer met die naam \"$1\" wat nie oorskryf kan word nie.",
-       "api-error-fileexists-shared-forbidden": "Daar is reeds 'n lêer met die naam \"$1\" in die gedeelde lêerstoor, en kan nie oorskryf word nie.",
-       "api-error-file-too-large": "Die lêer wat u probeer oplaai is te groot.",
-       "api-error-filename-tooshort": "Die lêernaam is te kort.",
-       "api-error-filetype-banned": "Hierdie tipe lêer is verban en word nie toegelaat nie.",
-       "api-error-filetype-banned-type": "Die {{PLURAL:$4|lêertipe|lêertipes}} $1 word nie toegelaat nie. Toelaatbare {{PLURAL:$3|lêertipes|lêertipes}} is $2.",
-       "api-error-filetype-missing": "Die lêer het nie 'n uitbreiding nie.",
-       "api-error-hookaborted": "Die wysiging wat jy probeer maak, is deur 'n uitbreiding haak geaborteer.",
-       "api-error-http": "Interne fout: Kan nie 'n verbinding met die bediener maak nie.",
-       "api-error-illegal-filename": "Die lêernaam word nie toegelaat nie.",
-       "api-error-internal-error": "Interne fout: daar is iets verkeerd gegaan het met die verwerking van die oplaai van die lêer op die wiki.",
-       "api-error-invalid-file-key": "Interne fout: die lêer is nie in tydelike berging gevind nie.",
-       "api-error-missingparam": "Interne fout: ontbrekende parameters op aanvraag.",
-       "api-error-missingresult": "Interne fout: Kon nie bepaal of die kopie daarin geslaag.",
-       "api-error-mustbeloggedin": "U moet ingeteken wees om lêers te kan laai.",
-       "api-error-mustbeposted": "Interne fout: Die versoek vereis 'n HTTP POST-metode.",
-       "api-error-noimageinfo": "Die oplaai daarin geslaag, maar die bediener het ons nie enige inligting oor die lêer.",
-       "api-error-nomodule": "Interne fout: daar is nie 'n uploadmodule ingestel nie.",
-       "api-error-ok-but-empty": "Interne fout: geen reaksie van die bediener.",
-       "api-error-overwrite": "'N bestaande lêer vervang word nie toegelaat nie.",
-       "api-error-stashfailed": "Interne fout: Server nie tydelike lêer te stoor.",
        "api-error-publishfailed": "Interne fout: bediener kon nie die tydelike lêer publiseer nie.",
-       "api-error-timeout": "Die bediener het nie reageer binne die verwagte tyd.",
-       "api-error-unclassified": "'n Onbekende fout het voorgekom.",
-       "api-error-unknown-code": "Onbekende fout: \"$1\"",
-       "api-error-unknown-error": "Interne fout: daar is iets verkeerd geloop het toe probeer om jou lêer te laai.",
+       "api-error-stashfailed": "Interne fout: Server nie tydelike lêer te stoor.",
        "api-error-unknown-warning": "Onbekende waarskuwing: $1",
        "api-error-unknownerror": "Onbekende fout: \"$1\"",
-       "api-error-uploaddisabled": "Oplaai is afgeskakel op hierdie wiki.",
-       "api-error-verification-error": "Hierdie lêer kan beskadig of het die verkeerde uitbreiding.",
        "duration-seconds": "$1 {{PLURAL:$1|sekonde|sekondes}}",
        "duration-minutes": "$1 {{PLURAL:$1|minuut|minute}}",
        "duration-hours": "$1 {{PLURAL:$1|uur|ure}}",
index e880470..d851261 100644 (file)
        "rcfilters-filter-registered-description": "المحررون مسجلو الدخول.",
        "rcfilters-filter-unregistered-label": "غير مسجل",
        "rcfilters-filter-unregistered-description": "المحررون غير مسجلي الدخول.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "مرشح \"غير المجلين\" غير نشط لأن تأثيره ملغى بواسطة  {{PLURAL:$2|مرشح|مرشحات}} الخبرة، و {{PLURAL:$2|الذي يعطي|التي تعطي}} فقط المستخدمين المسجلين: $1",
        "rcfilters-filtergroup-authorship": "ملكية التعديلات",
        "rcfilters-filter-editsbyself-label": "تعديلاتك الشخصية",
        "rcfilters-filter-editsbyself-description": "التعديلات بواسطتك.",
        "rcfilters-filter-editsbyother-label": "التعديلات بواسطة الآخرين",
        "rcfilters-filter-editsbyother-description": "التعديلات المنشأة بواسطة المستخدمين الآخرين (ليس أنت).",
        "rcfilters-filtergroup-userExpLevel": "مستوى الخبرة (للمستخدمين المسجلين فقط)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "القادمون الجدد",
-       "rcfilters-filter-userExpLevel-newcomer-description": "أقل من 10 تعديلات و4 أيام من النشاط.",
-       "rcfilters-filter-userExpLevel-learner-label": "المتعلمون",
-       "rcfilters-filter-userExpLevel-learner-description": "المزيد من أيام النشاط والتعديلات أكثر من \"القادمين الجدد\" ولكن أقل من \"المستخدمين ذوي الخبرة\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "المستخدمون ذوو الخبرة",
-       "rcfilters-filter-userExpLevel-experienced-description": "أكثر من 30 يوما من النشاط و500 تعديل.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "هذا المرشح غير فعال لأنه يعطي فقط المستخدمين المسجلين، لذا فمرشح \"غير المسجلين\" يلغي تأثيره.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "مرشح \"غير المسجلين\" في حالة تعارض مع واحد أو أكثر من مرشحات الخبرة. مرشحات الخبرة تعطي المستخدمين المسجلين فقط. المرشحات المتعارضة معلم عليها كغير نشطة بالأعلى.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "القادمون الجدد",
+       "rcfilters-filter-user-experience-level-newcomer-description": "أقل من 10 تعديلات و4 أيام من النشاط.",
+       "rcfilters-filter-user-experience-level-learner-label": "المتعلمون",
+       "rcfilters-filter-user-experience-level-learner-description": "المزيد من أيام النشاط والتعديلات أكثر من \"القادمين الجدد\" ولكن أقل من \"المستخدمين ذوي الخبرة\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "المستخدمون ذوو الخبرة",
+       "rcfilters-filter-user-experience-level-experienced-description": "أكثر من 30 يوما من النشاط و500 تعديل.",
        "rcfilters-filtergroup-automated": "المساهمات الأوتوماتيكية",
        "rcfilters-filter-bots-label": "بوت",
        "rcfilters-filter-bots-description": "التعديلات بواسطة الأدوات الأوتوماتيكية.",
        "rcfilters-filter-humans-label": "بشري (ليس بوت)",
        "rcfilters-filter-humans-description": "التعديلات بواسطة المحررين البشريين.",
+       "rcfilters-filtergroup-reviewstatus": "حالة المراجعة",
+       "rcfilters-filter-patrolled-label": "مراجعة",
+       "rcfilters-filter-patrolled-description": "التعديلات المعلم عليها كمراجعة.",
+       "rcfilters-filter-unpatrolled-label": "غير مراجعة",
+       "rcfilters-filter-unpatrolled-description": "التعديلات غير المعلم عليها كمراجعة.",
        "rcfilters-filtergroup-significance": "الأهمية",
        "rcfilters-filter-minor-label": "تعديلات طفيفة",
        "rcfilters-filter-minor-description": "التعديلات التي علم عليها المستخدم كطفيفة.",
index 79fc096..232427d 100644 (file)
        "viewhelppage": "شوف الباجة تاع المعاونة",
        "categorypage": "شوف الباجة تاع الصنيف",
        "viewtalkpage": "شوف التقرعيج",
-       "otherlanguages": "ب لوغات اخرين",
+       "otherlanguages": "بلوغات اخرين",
        "redirectedfrom": "(محول من $1)",
        "redirectpagesub": "باجة تاع التحوال",
        "redirectto": "حوّل لـ:",
        "externaldberror": "بالاك كاشما صرات غلطة فل توتاق تاع داتاباز ولا ما عندكش السراح باش تبدّل الحساب تاعك الخرجاني.",
        "login": "تسجال الدخول",
        "nav-login-createaccount": "تسجل/ اصنع حساب",
-       "userlogin": "تسجل/ اصنع حساب",
-       "userloginnocreate": "مسجّل الدخول",
        "logout": "مسجّل الخروج",
        "userlogout": "سجل خروج",
        "notloggedin": "ماشي مسجّل داخل.",
        "userlogin-noaccount": "ما عندك حساب؟",
        "userlogin-joinproject": "انضم لـ {{SITENAME}}",
-       "nologin": "ما عندكش حساب مسجل؟ '''$1'''.",
-       "nologinlink": "اصنع حساب",
        "createaccount": "اصنع حساب",
-       "gotaccount": "عندك حساب مسجل؟ '''$1'''.",
-       "gotaccountlink": "كونكسيون",
-       "userlogin-resetlink": "نسيت تفاصيل الدخول؟",
        "userlogin-resetpassword-link": "راك ناسي كلمت` السرّ؟",
        "userlogin-helplink2": "معاونة ف تسجال الدخول",
        "userlogin-loggedin": "راك مازلت مسجّل داخل ب`السميّة {{GENDER:$1|$1}}.\nاستعمل الجدوال تاع التجواب الّي هنا لتحت باش تتسجّل داخل ب سميّت` مستعملي وحداخُر.",
        "createacct-another-email-ph": "دخّل علوان تاع إيمال",
        "createaccountmail": "استعمل كلمت` سرّ على الزهَر و ابعتها للإيمال المنعوت هنايا.",
        "createacct-realname": "الأسم الحقّاني (ماشي محتّم)",
-       "createaccountreason": "سبّة:",
        "createacct-reason": "سبّة",
        "createacct-reason-ph": "علاش راك تخلق حساب وحداخُر",
        "createacct-submit": "اصنع حسابك",
        "passwordreset-emaildisabled": "الفعاليّات تاع الإيمال راهي محبّسة ف هاد الويكي.",
        "passwordreset-username": "سميّت` المستعملي:",
        "passwordreset-domain": "الدومان:",
-       "passwordreset-capture": "شوف الإيمال الناتج؟",
-       "passwordreset-capture-help": "يلا تقبش (cocher) هاد القويبسة، الإيمال (ب كلمت` السرّ المأقّتة) غادي يتورّا لك كلّي راه مبعوت لل مستعملي.",
        "passwordreset-email": "آدريسة تاع إيمال:",
        "passwordreset-emailtitle": "وصافات تاع الحساب ف {{SITENAME}}",
        "passwordreset-emailtext-ip": "شي واحد (يكون بالاك نتا، لادريسة إيپي $1) راه طلَب المصاوبة تاع كلمت` السرّ تاعك ف {{SITENAME}} ($4). {{PLURAL:$3|هاد الحساب |هاد الحسابات}} تاع المستعملي {{PLURAL:$3|راه مربوط|راهم مربوطين}} ب لادريسة تاع الإيمال:\n\n$2\n\n{{PLURAL:$3|هاد كلمت` السرّ المأقّتة|هادي كلمات` السرّ المأقّتة}} غادي يكمل صلوحها منّا على {{PLURAL:$5|نهار واحد|$5 إيّام}}.\nمليح لوكان تدخُل ل`السيت من ضركا و تبدّل كلمت` السرّ.\nيلا كاش ما وحداخُر دار هاد المطلب ولا راك تفكّرت كلمت` السرّ تاعك و ما بقيتش باغي تبدّلها، تنجم برك تنسا هاد الميساج و تستعمل كلمت` السرّ تاعك تاع مضاري.",
        "tooltip-search": " فتّش في {{SITENAME}}",
        "tooltip-search-go": "روح ل صفحة عندها نفس هاذ الاسم ايذا توجْدت",
        "tooltip-search-fulltext": "فتّش ع الپاجات الّي فيها هاذ النصّ",
-       "tooltip-p-logo": "زÙ\88ر ØµÙ\81حة Ø§Ù\84استÙ\82باÙ\84",
+       "tooltip-p-logo": "زÙ\88ر Ø§Ù\84باجة Ø§Ù\84Ù\84Ù\88Ù\84Ø©",
        "tooltip-n-mainpage": "زور الپاجة اللولانيّة",
        "tooltip-n-mainpage-description": "زور صفحة الاستقبال",
-       "tooltip-n-portal": "ع Ø§Ù\84Ù\85شرÙ\88عØ\8c Ø´Ù\86Ù\88Ù\91 ØªÙ\82در ØªØ¯Ù\8aرØ\8c Ù\81Ù\8aÙ\86 ØªÙ\84Ù\82Ù\89 Ø§Ù\84حاجات Ø§Ù\84Ù\91ي حاجتك بيها",
+       "tooltip-n-portal": "بخÙ\88صÙ\88ص Ø§Ù\84Ù\85شرÙ\88عØ\8c Ù\88اش ØªÙ\82در ØªØ¯Ù\8aرØ\8c Ù\88Ù\8aÙ\86 ØªÙ\84Ù\82Ù\89 Ø§Ù\84Ù\85عÙ\84Ù\88Ù\85Ø© Ø§Ù\84Ù\84ي حاجتك بيها",
        "tooltip-n-currentevents": "شوف اش قاعد يصير",
        "tooltip-n-recentchanges": "ليستة تاع التبدالات الاخّرين ف الويكي",
        "tooltip-n-randompage": "شرجي صفحة ع الزهر",
        "logentry-move-move": "{{GENDER:$2|نقّل|نقّلت}} $1 الصفحة $3 لـ $4",
        "logentry-newusers-create": "راه تفتح حساب {{GENDER:$2|المستخدم|المستخدمه}} $1",
        "logentry-upload-upload": " {{GENDER:$2|نزّل|نزّلت}} $1 $3",
-       "searchsuggest-search": "فتّش"
+       "searchsuggest-search": "فتّش في"
 }
index 005cc36..49ac8bd 100644 (file)
        "rcfilters-filter-editsbyother-label": "Ediciones d'otros",
        "rcfilters-filter-editsbyother-description": "Ediciones creaes por otros usuarios (non por ti).",
        "rcfilters-filtergroup-userExpLevel": "Nivel d'esperiencia (solo pa usuarios rexistraos)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Recién llegaos",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Menos de 10 ediciones y 4 díes d'actividá.",
-       "rcfilters-filter-userExpLevel-learner-label": "Aprendices",
-       "rcfilters-filter-userExpLevel-learner-description": "Más díes d'actividá y ediciones que los «Recién llegaos», pero menos que los «Usuarios espertos».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Usuarios espertos",
-       "rcfilters-filter-userExpLevel-experienced-description": "Más de 30 díes d'actividá y 500 ediciones.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Recién llegaos",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 ediciones y 4 díes d'actividá.",
+       "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
+       "rcfilters-filter-user-experience-level-learner-description": "Más díes d'actividá y ediciones que los «Recién llegaos», pero menos que los «Usuarios espertos».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Usuarios espertos",
+       "rcfilters-filter-user-experience-level-experienced-description": "Más de 30 díes d'actividá y 500 ediciones.",
        "rcfilters-filtergroup-automated": "Contribuciones automátiques",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Ediciones feches con ferramientes automátiques.",
index 4aa597b..8bbbfd3 100644 (file)
        "externaldberror": "Verilənlər bazasının doğruluğunu yoxlamada xəta baş verib və yaxud sizin xarici istifadəçi qeydiyyatını yeniləmək hüququnuz yoxdur.",
        "login": "Daxil ol",
        "nav-login-createaccount": "Daxil ol / hesab yarat",
-       "userlogin": "Daxil ol və ya qeydiyyatdan keç",
-       "userloginnocreate": "Daxil ol",
        "logout": "Çıxış",
        "userlogout": "Çıxış",
        "notloggedin": "Daxil olmamısınız",
        "userlogin-noaccount": "İstifadəçi hesabınız yoxdur?",
        "userlogin-joinproject": "{{SITENAME}} layihəsinə qoşulun",
-       "nologin": "İstifadəçi hesabınız yoxdur? $1.",
-       "nologinlink": "Hesab yarat",
        "createaccount": "Hesab yarat",
-       "gotaccount": "İstifadəçi hesabınız varmı? '''$1'''.",
-       "gotaccountlink": "Daxil olun",
-       "userlogin-resetlink": "Daxilolma məlumatlarınızı unutmusunuz?",
        "userlogin-resetpassword-link": "Parolunuzu unutmusunuz?",
        "userlogin-helplink2": "Qeydiyyat barədə köməklik",
        "userlogin-loggedin": "Siz artıq {{GENDER:$1|$1}} kimi daxil olmusunuz.\nAşağıdakı formadan istifadə edərək, başqa bir istifadəçi kimi daxil ola bilərsiniz.",
        "createacct-another-email-ph": "E-poçt ünvanını daxil edin",
        "createaccountmail": "Müvəqqəti təsadüfi bir paroldan istifadə edə bilər və bu parolu göstərilən e-poçt ünvanına göndərə bilərsiniz",
        "createacct-realname": "Əsl adınız (məcburi deyil)",
-       "createaccountreason": "Səbəb:",
        "createacct-reason": "Səbəb",
        "createacct-reason-ph": "Niyə başqa bir hesab yaradırsınız",
        "createacct-submit": "İstifadəçi hesabı yarat",
        "subject": "Mövzu:",
        "minoredit": "Kiçik redaktə",
        "watchthis": "Bu səhifəni izlə",
-       "savearticle": "Səhifəni qeyd et",
+       "savearticle": "Səhifəni dərc et",
        "publishpage": "Səhifəni yayımla",
        "publishchanges": "Dəyişiklikləri yayımla",
        "preview": "Sınaq görüntüsü",
        "prefs-timeoffset": "Saat qurşağının fərqi",
        "prefs-advancedediting": "Ümumi parametrlər",
        "prefs-editor": "Redaktor",
+       "prefs-preview": "İlkin baxış",
        "prefs-advancedrc": "Ətraflı variantlar",
        "prefs-advancedrendering": "Ətraflı variantlar",
        "prefs-advancedsearchoptions": "Ətraflı variantlar",
        "logentry-newusers-autocreate": "$1 istifadəçi hesabı avtomatik yaradıldı",
        "logentry-upload-upload": "$1 $3 faylını {{GENDER:$2|yüklədi}}.",
        "rightsnone": "(yoxdur)",
-       "revdelete-summary": "redaktə xülasəsi",
        "feedback-bugnew": "Mən yoxladım. Yeni xəta barədə xəbər ver",
        "feedback-cancel": "İmtina",
        "feedback-close": "Oldu",
        "duration-centuries": "$1 {{PLURAL:$1|əsr|əsr}}",
        "duration-millennia": "$1 {{PLURAL:$1|minillik|minillik}}",
        "limitreport-cputime": "CPU vaxt istifadəsi",
+       "limitreport-cputime-value": "$1 {{PLURAL:$1|saniyə|saniyə}}",
        "limitreport-walltime": "Real vaxt istifadəsi",
+       "limitreport-walltime-value": "$1 {{PLURAL:$1|saniyə|saniyə}}",
        "limitreport-postexpandincludesize-value": "$1/$2 bayt",
        "expand_templates_output": "Nəticə",
        "expand_templates_ok": "OK",
index 0d21be3..bce66e9 100644 (file)
        "revdelete-suppress-text": "Скрываньне можа выкарыстоўвацца '''толькі''' ў наступных выпадках:\n* патэнцыйна паклёпніцкая інфармацыя\n* раскрыцьцё асабістых зьвестак\n*: ''хатнія адрасы, тэлефонныя нумары, нумары пашпартоў і г. д.''",
        "revdelete-legend": "Усталяваць абмежаваньні бачнасьці",
        "revdelete-hide-text": "Тэкст вэрсіі",
-       "revdelete-hide-image": "Схаваць зьмест файла",
+       "revdelete-hide-image": "Схаваць зьмест файлу",
        "revdelete-hide-name": "Схаваць мэту і парамэтры",
        "revdelete-hide-comment": "Апісаньне зьменаў",
        "revdelete-hide-user": "Імя ўдзельніка/IP-адрас",
        "rcfilters-filter-registered-description": "Рэдактары, якія ўвайшлі ў сыстэму.",
        "rcfilters-filter-unregistered-label": "Незарэгістраваныя",
        "rcfilters-filter-unregistered-description": "Рэдактары, якія не ўвайшлі ў сыстэму",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Фільтар «Незарэгістраваны» неактыўны, бо яго эфэкт адменены {{PLURAL:$2|1=наступным фільтрам|наступнымі фільтрамі}} дасьведчаных удзельнікаў, {{PLURAL:$2|1=які знаходзіць|якія знаходзяць}} толькі зарэгістраваных удзельнікаў: $1",
        "rcfilters-filtergroup-authorship": "Рэдагаваць аўтарства",
        "rcfilters-filter-editsbyself-label": "Вашыя праўкі",
        "rcfilters-filter-editsbyself-description": "Праўкі, зробленыя вамі",
        "rcfilters-filter-editsbyother-label": "Праўкі іншых удзельнікаў",
        "rcfilters-filter-editsbyother-description": "Праўкі, зробленыя іншымі ўдзельнікамі (ня вамі).",
        "rcfilters-filtergroup-userExpLevel": "Узровень досьведу (толькі для зарэгістраваных удзельнікаў)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Навічкі",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Менш за 10 правак і 4 дні актыўнасьці.",
-       "rcfilters-filter-userExpLevel-learner-label": "Вучні",
-       "rcfilters-filter-userExpLevel-learner-description": "Болей дзён актыўнасьці і правак, чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Дасьведчаныя ўдзельнікі",
-       "rcfilters-filter-userExpLevel-experienced-description": "Больш за 30 дзён актыўнасьці і 500 правак.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Гэты фільтар не актыўны, бо ён знаходзіць толькі зарэгістраваных удзельнікаў, такім чынам фільтар «Незарэгістраваныя» касуе яго эфэкт.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтар «Незарэгістраваныя» канфліктуе з адным ці некалькімі фільтрамі ўзроўню досьведу. Фільтры ўзроўню досьведу шукаюць толькі зарэгістраваных удзельнікаў. Канфліктныя фільтры пазначаныя вышэй як неактыўныя.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Навічкі",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Менш за 10 правак і 4 дні актыўнасьці.",
+       "rcfilters-filter-user-experience-level-learner-label": "Вучні",
+       "rcfilters-filter-user-experience-level-learner-description": "Болей дзён актыўнасьці і правак, чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Дасьведчаныя ўдзельнікі",
+       "rcfilters-filter-user-experience-level-experienced-description": "Больш за 30 дзён актыўнасьці і 500 правак.",
        "rcfilters-filtergroup-automated": "Аўтаматычны ўнёсак",
        "rcfilters-filter-bots-label": "Робат",
        "rcfilters-filter-bots-description": "Праўкі, зробленыя з дапамогай аўтаматызаваных інструмэнтаў.",
        "rcfilters-filter-humans-label": "Чалавек (ня робат)",
        "rcfilters-filter-humans-description": "Праўкі, зробленыя людзьмі.",
+       "rcfilters-filtergroup-reviewstatus": "Статус праверкі",
+       "rcfilters-filter-patrolled-label": "Правераныя",
+       "rcfilters-filter-patrolled-description": "Праўкі, пазначаныя як правераныя.",
        "rcfilters-filtergroup-significance": "Значэньне",
        "rcfilters-filter-minor-label": "Дробныя праўкі",
        "rcfilters-filter-minor-description": "Праўкі, якія аўтар пазначыў як дробныя.",
        "authmanager-authplugin-setpass-failed-message": "Дадатак аўтэнтыфікацыі адмовіў зьмену паролю.",
        "authmanager-authplugin-create-fail": "Дадатак аўтэнтыфікацыі адмовіў у стварэньні рахунку.",
        "authmanager-authplugin-setpass-denied": "Дадатак аўтэнтыфікацыі не дазваляе зьмяняць паролі.",
+       "authmanager-authplugin-setpass-bad-domain": "Няслушны дамэн.",
        "authmanager-realname-label": "Сапраўднае імя",
        "authmanager-provider-temporarypassword": "Часовы пароль",
        "changecredentials": "Зьмена ўліковых зьвестак",
index c44e942..de9737f 100644 (file)
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (гл. асобна [[Special:NewPages|новыя старонкі]])",
        "recentchanges-submit": "Паказаць",
-       "rcfilters-filter-userExpLevel-learner-description": "Болей дзён актыўнасці і правак, чым у «навічкоў», але меней чым у «дасведчаных удзельнікаў».",
-       "rcfilters-filter-userExpLevel-experienced-description": "Больш за 30 дзён актыўнасці і 500 правак.",
+       "rcfilters-filter-user-experience-level-learner-description": "Болей дзён актыўнасці і правак, чым у «навічкоў», але меней чым у «дасведчаных удзельнікаў».",
+       "rcfilters-filter-user-experience-level-experienced-description": "Больш за 30 дзён актыўнасці і 500 правак.",
        "rcfilters-filter-bots-label": "Бот",
        "rcnotefrom": "Ніжэй {{PLURAL:$5|паказана змяненне|паказаны змены}} з <strong>$3, $4</strong> (не больш за <strong>$1</strong>).",
        "rclistfrom": "Паказаць змены з $3 $2",
index c6ebbd8..21dd334 100644 (file)
        "rcfilters-filter-editsbyself-label": "Ваши собствени редакции",
        "rcfilters-filter-editsbyself-description": "Ваши редакции.",
        "rcfilters-filter-editsbyother-label": "Чужди редакции",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Новодошли",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Новодошли",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-humans-label": "Човек (не бот)",
        "rcfilters-filter-minor-label": "Малки промени",
index 0252484..30c5ca1 100644 (file)
        "rcfilters-invalid-filter": "অকার্যকর ফিল্টার",
        "rcfilters-empty-filter": "কোনো সক্রিয় ফিল্টার নেই। সমস্ত অবদান দেখানো হয়েছে।",
        "rcfilters-filterlist-title": "ছাঁকনি",
+       "rcfilters-filterlist-feedbacklink": "নতুন (বিটা) ছাঁকনির উপর মতামত প্রদান করুন",
        "rcfilters-highlightbutton-title": "ফলাফল আলোকপাত করুন",
        "rcfilters-highlightmenu-title": "একটি রং নির্বাচন করুন",
+       "rcfilters-highlightmenu-help": "এই বৈশিষ্ট্য আলোকপাত করতে একটি রঙ নির্বাচন করুন",
        "rcfilters-filterlist-noresults": "কোনও ফিল্টার পাওয়া যায়নি",
        "rcfilters-filtergroup-registration": "ব্যবহারকারী নিবন্ধন",
        "rcfilters-filter-registered-label": "নিবন্ধিত",
        "rcfilters-filter-editsbyother-label": "অন্যদের দ্বারা সম্পাদনা",
        "rcfilters-filter-editsbyother-description": "অন্য ব্যবহারকারীদের দ্বারা করা সম্পাদনা (আপনার না)।",
        "rcfilters-filtergroup-userExpLevel": "অভিজ্ঞতার স্তর (শুধু মাত্র নিবন্ধিত ব্যবহারকারীর জন্য)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "নতুন আগত",
-       "rcfilters-filter-userExpLevel-newcomer-description": "১০টি সম্পাদনার কম ও ৪ দিনের কার্যকলাপ।",
-       "rcfilters-filter-userExpLevel-learner-label": "শিক্ষার্থী",
-       "rcfilters-filter-userExpLevel-experienced-label": "অভিজ্ঞ ব্যবহারকারী",
-       "rcfilters-filter-userExpLevel-experienced-description": "৩০ দিনের বেশী কার্যকলাপ ও ৫০০টি সম্পাদনা।",
+       "rcfilters-filter-user-experience-level-newcomer-label": "নতুন আগত",
+       "rcfilters-filter-user-experience-level-newcomer-description": "১০টি সম্পাদনার কম ও ৪ দিনের কার্যকলাপ।",
+       "rcfilters-filter-user-experience-level-learner-label": "শিক্ষার্থী",
+       "rcfilters-filter-user-experience-level-experienced-label": "অভিজ্ঞ ব্যবহারকারী",
+       "rcfilters-filter-user-experience-level-experienced-description": "৩০ দিনের বেশী কার্যকলাপ ও ৫০০টি সম্পাদনা।",
        "rcfilters-filtergroup-automated": "স্বয়ংক্রিয় অবদান",
        "rcfilters-filter-bots-label": "বট",
        "rcfilters-filter-bots-description": "স্বয়ংক্রিয় সরঞ্জাম দিয়ে করা সম্পাদনা।",
        "rcfilters-filter-humans-label": "মানুষ (বট নয়)",
        "rcfilters-filter-humans-description": "মানব সম্পাদক দ্বারা করা সম্পাদনা।",
+       "rcfilters-filtergroup-reviewstatus": "পর্যালোচনার অবস্থা",
+       "rcfilters-filter-patrolled-label": "পরীক্ষিত",
+       "rcfilters-filter-patrolled-description": "সম্পাদনা পরীক্ষিত হিসেবে চিহ্নিত করা হয়েছে।",
+       "rcfilters-filter-unpatrolled-label": "অপরীক্ষিত",
+       "rcfilters-filter-unpatrolled-description": "সম্পাদনা পরীক্ষিত হিসেবে চিহ্নিত করা হয় নি।",
        "rcfilters-filtergroup-significance": "তাৎপর্য",
        "rcfilters-filter-minor-label": "অনুল্লেখ্য সম্পাদনা",
        "rcfilters-filter-major-label": "অ-অনুল্লেখ্য সম্পাদনা",
        "authmanager-authplugin-setpass-failed-title": "পাসওয়ার্ড পরিবর্তন ব্যর্থ হয়েছে",
        "authmanager-authplugin-setpass-failed-message": "প্রমাণীকরণ প্লাগইন পাসওয়ার্ড পরিবর্তন করতে অস্বীকৃতি জানিয়েছে।",
        "authmanager-authplugin-create-fail": "প্রমাণীকরণ প্লাগইন অ্যাকাউন্ট তৈরি করতে অস্বীকৃতি জানিয়েছে।",
+       "authmanager-authplugin-setpass-denied": "প্রমাণীকরণ প্লাগইন পাসওয়ার্ড পরিবর্তন করার অনুমতি দেয় না।",
        "authmanager-authplugin-setpass-bad-domain": "অবৈধ ডোমেইন।",
        "authmanager-autocreate-noperm": "স্বয়ংক্রিয় অ্যাকাউন্ট সৃষ্টি মঞ্জুরিপ্রাপ্ত নয়।",
+       "authmanager-autocreate-exception": "স্বয়ংক্রিয় অ্যাকাউন্ট সৃষ্টিকরণ পূর্ববর্তী ত্রুটির কারণে সাময়িকভাবে অক্ষম করা হয়েছে।",
        "authmanager-userdoesnotexist": "ব্যবহারকারী অ্যাকাউন্ট \"$1\" অনিবন্ধিত।",
        "authmanager-username-help": "প্রমাণীকরণের জন্য ব্যবহারকারী নাম।",
        "authmanager-password-help": "প্রমাণীকরণের জন্য পাসওয়ার্ড।",
index 5d5287d..e001670 100644 (file)
        "rcfilters-filter-registered-description": "Aozerien gevreet.",
        "rcfilters-filter-unregistered-label": "Divarilh",
        "rcfilters-filter-unregistered-description": "Aozerien n'int ket kevreet.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Dizoberiant eo ar sil \"dienroll\" peogwir eo nullet e wered gant ar {{PLURAL:$2|sil|siloù}} Arroutet, na {{PLURAL:$2|gav|gavont}} nemet an implijerien enrollet : $1",
        "rcfilters-filtergroup-authorship": "Kemmañ anv an aozer",
        "rcfilters-filter-editsbyself-label": "Kemmet ganeoc'h",
        "rcfilters-filter-editsbyself-description": "Kemmet ganeoc'h",
        "rcfilters-filter-editsbyother-label": "Kemmet gant tud all",
        "rcfilters-filter-editsbyother-description": "Kemmoù graet gant implijerien all (ket ganeoc'h).",
        "rcfilters-filtergroup-userExpLevel": "Live skiant-prenañ (evit an implijer enrollet hepken)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Tud nevez-deuet",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Nebeutoc'h eget 10 kemm ha 4 devezh obererezh.",
-       "rcfilters-filter-userExpLevel-learner-label": "Deskarded",
-       "rcfilters-filter-userExpLevel-learner-description": "Muioc'h a zeizioù obererezh hag a gemmoù eget \"deraouidi\" hogen nebeutoc'h eget an \"implijerien arroutet\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Implijerien arroutet",
-       "rcfilters-filter-userExpLevel-experienced-description": "Ouzhpenn 30 devezh oberiantiz ha 500 kemm.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Dizoberiant eo ar sil-mañ peogwir ne gav nemet implijerien enrollet, dre se emañ nullet e wered gant ar sil \"Dienroll\".",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Emañ ar sil \"Dienroll\" oc'h en em sikanañ gant ur sil Arroutet pe meur a hini. Ar siloù Arroutet a gav implijerien enrollet hepken. Ar siloù o tabutal zo merket evel dizoberiant a-us.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Tud nevez-deuet",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Nebeutoc'h eget 10 kemm ha 4 devezh obererezh.",
+       "rcfilters-filter-user-experience-level-learner-label": "Deskarded",
+       "rcfilters-filter-user-experience-level-learner-description": "Muioc'h a zeizioù obererezh hag a gemmoù eget \"deraouidi\" hogen nebeutoc'h eget an \"implijerien arroutet\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Implijerien arroutet",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ouzhpenn 30 devezh oberiantiz ha 500 kemm.",
        "rcfilters-filtergroup-automated": "Degasadennoù emgefre",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Kemmoù graet gant ostilhoù emgefre.",
        "rcfilters-filter-humans-label": "Den (ket ur robot)",
        "rcfilters-filter-humans-description": "Kemmoù graet gant tud kig-hag-eskern.",
+       "rcfilters-filtergroup-reviewstatus": "Adwelet ar statud",
+       "rcfilters-filter-patrolled-label": "Gwiriet",
+       "rcfilters-filter-patrolled-description": "Merkañ ar c'hemmoù evel gwiriet.",
+       "rcfilters-filter-unpatrolled-label": "Anwiriet",
+       "rcfilters-filter-unpatrolled-description": "Kemmoù n'int ket merket evit gwiriet.",
        "rcfilters-filtergroup-significance": "Talvoudegezh",
        "rcfilters-filter-minor-label": "Kemmoù dister",
        "rcfilters-filter-minor-description": "Kemmoù merket evel dister gant an aozer.",
        "restricted-displaytitle-ignored-desc": "Ar bajenn-mañ zo dezhi un <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> zo bet laosket a-gostez peogwir n'eo ket kevatal d'an titl zo d'ar bajenn bremañ.",
        "noindex-category-desc": "Ar bajenn-mañ n'eo ket menegeret gant ar robotoù rak ar ger hud <code><nowiki>__NOINDEX__</nowiki></code> zo enni hag emañ en un esaouenn anv m'eo aotreet ar merkañ.",
        "index-category-desc": "Un <code><nowiki>__INDEX__</nowiki></code> zo er bajenn (hag emañ en un esaouenn anv m'eo aotreet ober gant ar merk-se). Abalamour da se e vo menegeret gant robotoù pa ne vije ket bet a-hend-all.",
+       "post-expand-template-inclusion-category-desc": "Brasoc'h eo ment ar bajenn eget <code>$wgMaxArticleSize</code> ur wech dispaket he holl batromoù. Gant se n'eus ket bet gallet dispakañ patromoù zo.",
+       "post-expand-template-argument-category-desc": "Brasoc'h eo ar bajenn eget <code>$wgMaxArticleSize</code> ur wech dispaket arguzenn ur patrom (un dra bennak etre un triad briataennoù, evel <code>{{{Foo}}}</code>).",
+       "expensive-parserfunction-category-desc": "Ur re implij eus arc'hwelioù koustus an dielfenner a vez graet gant ar bajenn (evel <code>#ifexist</code>). Gwelet [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
        "broken-file-category-desc": "Er bajenn-mañ ez eus ul liamm restr torr (ul liamm da enframmañ ur restr pa n'eus ket eus ar restr-se).",
+       "hidden-category-category-desc": "E pajenn ar rummad emañ <code><nowiki>__HIDDENCAT__</nowiki></code>, ar pezh a vir outi a zont war wel e takad al liammoù rummad dre ziouer.",
        "trackingcategories-nodesc": "N'eus deskrivadur ebet.",
        "trackingcategories-disabled": "Diweredekaet eo ar rummad",
        "mailnologin": "Chomlec'h ebet",
        "emailccsubject": "Eilenn eus ho kemennadenn da $1: $2",
        "emailsent": "Postel kaset",
        "emailsenttext": "Kaset eo bet ho postel.",
-       "emailuserfooter": "Kaset eo bet ar postel-mañ gant $1 da $2 dre an arc'hwel \"Kas ur postel d'an implijer\" war {{SITENAME}}.",
+       "emailuserfooter": "{{GENDER:$1|Kaset}} eo bet ar postel-mañ gant $1 da {{GENDER:$2|$2}} dre an arc'hwel \"{{int:emailuser}}\" war {{SITENAME}}. Ma {{GENDER:$2|respontit}} d'ar postel-mañ e vo kaset {{GENDER:$2|ho}} postel war-eeun d'ar {{GENDER:$1|c'haser|gaserez}} orin a {{GENDER:$1|c'hallo}} gwelet {{GENDER:$2|ho}} chomlec'h postel .",
        "usermessage-summary": "En deus laosket ur gemennadenn sistem.",
        "usermessage-editor": "Kemennerezh ar reizhiad",
        "watchlist": "Roll evezhiañ",
        "watchlistanontext": "Ret eo deoc'h kevreañ evit gwelet pe aozañ an elfennoù a zo en ho roll evezhiañ.",
        "watchnologin": "Digevreet",
        "addwatch": "Ouzhpennañ d'ar roll evezhiañ",
-       "addedwatchtext": "Ouzh ho [[Special:Watchlist|rollad evezhiañ]] eo bet ouzhpennet ar bajenn \"[[:$1]]\".\nAmañ e vo rollet ar c'hemmoù da zont evit ar bajenn-mañ hag ar bajenn gaozeal stag outi.",
+       "addedwatchtext": "Ouzh ho [[Special:Watchlist|rollad evezhiañ]] eo bet ouzhpennet ar bajenn \"[[:$1]]\" hag ar bajenn gaozeal stag outi.",
+       "addedwatchtext-talk": "Ouzhpennet eo bet \"[[:$1]]\" hag ar bajenn stag outi ouzh ho [[Special:Watchlist|roll evezhiañ]].",
        "addedwatchtext-short": "Ouzhpennet eo bet ar bajenn \"$1\" d'ho roll-evezhiañ.",
        "removewatch": "Lemel a-ziwar ar roll evezhiañ",
-       "removedwatchtext": "Lamet eo bet ar bajenn \"[[:$1]]\" a-ziwar ho [[Special:Watchlist|roll evezhiañ]].",
+       "removedwatchtext": "Lamet eo bet ar bajenn \"[[:$1]]\" hag ar bajenn gaozeal stag outi a-ziwar ho [[Special:Watchlist|roll evezhiañ]].",
+       "removedwatchtext-talk": "Lamet eo bet \"[[:$1]]\" hag ar bajenn stag outi a-ziwar ho [[Special:Watchlist|roll evezhiañ]].",
        "removedwatchtext-short": "Lamet eo bet ar bajenn \"$1\" eus ho roll-evezhiañ.",
        "watch": "Evezhiañ",
        "watchthispage": "Evezhiañ ar bajenn-mañ",
        "enotif_lastvisited": "Sellet ouzh $1 evit gwelet an holl gemmoù abaoe ho selladenn ziwezhañ.",
        "enotif_lastdiff": "Gwelet $1 evit sellet ouzh ar c'hemm-mañ.",
        "enotif_anon_editor": "implijer dizanv $1",
-       "enotif_body": "$WATCHINGUSERNAME ker,\n\n$CHANGEDORCREATED eo bet pajenn $PAGETITLE {{SITENAME}} gant $PAGEEDITOR d'an $PAGEEDITDATE gwelet $PAGETITLE_URL evit gwelet ar stumm red.\n\n$NEWPAGE\n\nDiverrañ an aozer : $PAGESUMMARY $PAGEMINOREDIT\n\nMont e darempred gant an aozer :\npostel: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nNemet ez afec'h da welet ar bajenn end-eeun, ne vo kemenn all ebet ma vez kemmet pelloc'h.\nGallout a rit nevesaat doare ar pennadoù evezhiet ganeoc'h en ho rollad evezhiañ ivez.\n\n            Ho reizhiad kemenn {{SITENAME}} muiañ karet\n\n--\nEvit kemmañ arventennoù ho kemennoù dre bostel, sellit ouzh\n{{canonicalurl:{{#special:Preferences}}}}\n\nEvit kemmañ doare ho rollad evezhiañ, sellit ouzh\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nEvit dilemel ar bajenn eus ho rollad evezhiañ, sellit ouzh\n$UNWATCHURL\n\nEvezhiadennoù ha skoazell pelloc'h :\n$HELPPAGE",
+       "enotif_body": "$WATCHINGUSERNAME ker,\n\n$PAGEINTRO $NEWPAGE\n\nDiverrañ an aozer : $PAGESUMMARY $PAGEMINOREDIT\n\nMont e darempred gant an aozer :\npostel: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nNemet ez afec'h da welet ar bajenn end-eeun en ur vezañ kevreet, ne vo kemenn all ebet ma vez un tamm obererezh pelloc'h.\n\n\n            Ho reizhiad kemenn {{SITENAME}} muiañ karet\n\n--\nEvit kemmañ arventennoù ho kemennoù dre bostel, sellit ouzh\n{{canonicalurl:{{#special:Preferences}}}}\n\nEvit kemmañ doare ho rollad evezhiañ, sellit ouzh\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nEvit dilemel ar bajenn eus ho rollad evezhiañ, sellit ouzh\n$UNWATCHURL\n\nEvezhiadennoù ha skoazell pelloc'h :\n$HELPPAGE",
        "created": "Krouet",
        "changed": "kemmet",
        "deletepage": "Diverkañ ur bajenn",
        "confirm": "Kadarnaat",
        "excontent": "endalc'had '$1'",
-       "excontentauthor": "an danvez a oa : '$1' (ha '[[Special:Contributions/$2|$2]]' a oa bet an implijer nemetañ)",
+       "excontentauthor": "an danvez a oa : ''$1'' ha ''[[Special:Contributions/$2|$2]]'' ([[User talk:$2|kaozeal]]) a oa bet an implijer nemetañ",
        "exbeforeblank": "A-raok diverkañ e oa an endalc'had : '$1'",
        "delete-confirm": "Diverkañ \"$1\"",
        "delete-legend": "Diverkañ",
        "delete-toobig": "Bras eo istor ar bajenn-mañ, ouzhpenn $1 {{PLURAL:$1|stumm|stumm}} zo. Bevennet eo bet an diverkañ pajennoù a-seurt-se kuit da zegas reuz war {{SITENAME}} dre fazi .",
        "delete-warning-toobig": "Bras eo istor ar bajenn-mañ, ouzhpenn {{PLURAL:$1|stumm|stumm}} zo.\nDiverkañ anezhi a c'hallo degas reuz war mont en-dro diaz titouroù {{SITENAME}};\ntaolit evezh bras.",
        "deleteprotected": "Ne c'hallit ket dilemel ar bajenn-mañ rak gwarezet eo bet.",
+       "deleting-backlinks-warning": "<strong>Taolit pled :</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Pajennoù all]] zo enno liammoù war-zu ar bajenn emaoc'h o vont da zilemel pe a zo treuzkludet ar bajenn enno.",
        "rollback": "disteuler ar c'hemmoù",
        "rollbacklink": "disteuler",
        "rollbacklinkcount": "disteurel $1 {{PLURAL:$1|kemm}}",
        "editcomment": "Diverradenn ar c'hemm a oa : <em>$1</em>.",
        "revertpage": "Kemmoù distaolet gant [[Special:Contributions/$2|$2]] ([[User talk:$2|Kaozeal]]); adlakaet d'ar stumm diwezhañ a-gent gant [[User:$1|$1]]",
        "revertpage-nouser": "Disteuler kemmoù un implijer kuzhet ha distreiñ d'ar stumm diwezhañ gant an {{GENDER:$1|[[implijer :$1|$1]]}}",
-       "rollback-success": "Disteuler kemmoù $1; distreiñ da stumm diwezhañ $2.",
+       "rollback-success": "Nullet ar c'hemmoù gant {{GENDER:$3|$1}};\nadlakaet diouzh ar stumm diwezhañ gant {{GENDER:$4|$2}}.",
+       "rollback-success-notify": "Nullet ar c'hemmoù gant $1;\nadlakaet diouzh ar stumm diwezhañ gant $2. [$3 Diskouez ar c'hemmoù]",
        "sessionfailure-title": "Fazi dalc'h",
        "sessionfailure": "Evit doare ez eus ur gudenn gant ho talc'h;\nNullet eo bet an ober-mañ a-benn en em wareziñ diouzh an tagadennoù preizhañ.\nKlikit war \"kent\" hag adkargit ar bajenn oc'h deuet drezi; goude klaskit en-dro.",
        "changecontentmodel": "Cheñch patrom danvez ur bajenn",
        "changecontentmodel-success-title": "Cheñchet eo bet ar patrom danvez",
        "changecontentmodel-success-text": "Kemmet eo bet patrom danvez [[:$1]].",
        "changecontentmodel-cannot-convert": "N'hall ket danvez [[:$1]] bezañ troet en ur seurt $2.",
+       "changecontentmodel-nodirectediting": "N'haller ket ober kemmoù ar-eeun gant ar patrom danvez $1",
        "changecontentmodel-emptymodels-title": "N'eus patrom danvez hegerz ebet",
+       "changecontentmodel-emptymodels-text": "N'haller ket amdreiñ danvez [[:$1]] e doare ebet.",
+       "log-name-contentmodel": "Marilh kemmañ patrom danvez",
+       "log-description-contentmodel": "Rollañ a ra ar bajenn-mañ ar c'hemmoù graet e patrom danvez ar pajennoù hag ar pajennoù a oa bet krouet gant ur patrom danvez all estreget an hini a zeu dre ziouer.",
+       "logentry-contentmodel-new": "{{GENDER:$2|Krouet eo bet}} ar bajenn $3 gant $1 en ur ober gant ur patrom danvez \"$5\" disheñvel diouzh an hini dre ziouer",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|Kemmet eo bet}} patrom danvez ar bajenn $3 gant $1, eus \"$4\" da \"$5\"",
        "logentry-contentmodel-change-revertlink": "disteuler",
        "logentry-contentmodel-change-revert": "disteuler",
        "protectlogpage": "Log_gwareziñ",
        "modifiedarticleprotection": "Kemmañ live gwareziñ \"[[$1]]\"",
        "unprotectedarticle": "diwarezet eo bet ''[[$1]]''",
        "movedarticleprotection": "en deus adkaset an arventennoù gwareziñ eus \"[[$2]]\" da \"[[$1]]\"",
+       "protectedarticle-comment": "{{GENDER:$2|En deus gwarezet|He deus gwarezet}} \"[[$1]]\"",
+       "modifiedarticleprotection-comment": "{{GENDER:$2|En deus kemmet live gwareziñ|He deus kemmet live gwareziñ}} \"[[$1]]\"",
+       "unprotectedarticle-comment": "{{GENDER:$2|En deus lamet gwarez|He deus lamet gwarez}} \"[[$1]]\"",
        "protect-title": "Kemmañ al live gwareziñ evit \"$1\"",
        "protect-title-notallowed": "Gwelet al live gwareziñ evit \"$1\"",
        "prot_1movedto2": "[[$1]] adkaset war-du [[$2]]",
        "protect-locked-blocked": "E-keit ha ma viot stanket ne viot ket evit cheñch al live gwareziñ. Setu aze arventennoù a-vremañ ar bajenn '''$1''':",
        "protect-locked-dblock": "N'haller ket cheñch al liveoù gwareziñ rak prennet eo an diaz titouroù.\nSetu doare a-vremañ ar bajenn '''$1''' :",
        "protect-locked-access": "N'eo ket aotreet ho kont da zegas kemmoù e live gwareziñ ur bajenn.\nSetu an doare a-vremañ evit ar bajenn-mañ '''$1''':",
-       "protect-cascadeon": "Gwarezet eo ar bajenn-mañ peogwir he c'haver er {{PLURAL:$1|bajenn|pajennoù}} da-heul zo gweredekaet enno ar gwareziñ dre skalierad. Gallout a rit kemmañ al live gwareziñ met ne cheñcho ket ar gwareziñ dre skalierad.",
+       "protect-cascadeon": "Gwarezet eo ar bajenn-mañ peogwir emañ treuzkludet er {{PLURAL:$1|bajenn|pajennoù}} da-heul zo gweredekaet enno ar gwareziñ dre skalierad. Ma kemmit live gwareziñ ar bajenn-mañ ne cheñcho ket ar gwareziñ dre skalierad.",
        "protect-default": "Aotren an holl implijerien",
        "protect-fallback": "Degemer hepken an implijerien gant an aotre \"$1\"",
        "protect-level-autoconfirmed": "Degemer hepken an implijerien emgadarnaet",
        "protect-othertime": "Termen all :",
        "protect-othertime-op": "termen all",
        "protect-existing-expiry": "Termen echuiñ merket : $3, $2",
+       "protect-existing-expiry-infinity": "Termen echuiñ merket : difin",
        "protect-otherreason": "Abeg all/ouzhpenn :",
        "protect-otherreason-op": "Abeg all",
        "protect-dropdown": "*Abegoù gwareziñ boutin\n** Vandalerezh betek re\n** Stroberezh betek re\n** Tabutoù toull war kemmoù zo\n** Pajenn kemmet alies-tre",
        "undeletepagetext": "Diverket eo bet {{PLURAL:$1|ar bajenn da-heul; emañ|ar pajennoù da-heul; emaint}} e bank roadennoù an dielloù, ma c'hallont bezañ assavet.\nIngal e c'hall an diell bezañ goullonderet.",
        "undelete-fieldset-title": "Assevel ar stummoù",
        "undeleteextrahelp": "Evit assevel istor klok ar bajenn, laoskit goullo an holl logoù ha klikit war '''''{{int:undeletebtn}}'''''.\nEvit assevel stummoù zo hepken, askit ar logoù a glot gant ar stummoù da vezañ assavet, ha klikit war '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|stumm|stumm}} diellaouet",
+       "undeleterevisions": "$1 {{PLURAL:$1|adweladenn}} diverket",
        "undeletehistory": "Ma tiziverkit ar bajenn e vo erlec'hiet an holl gemmoù bet degaset enni er roll istor.\n\nMa'z eus bet krouet ur bajenn nevez dezhi an hevelep anv abaoe an diverkadenn, e teuio war wel ar c'hemmoù assavet er roll istor kent ha ne vo ket erlec'hiet ar stumm red en un doare emgefre ken.",
        "undeleterevdel": "Ne vo ket adsavet ar stumm-se eus ar bajenn ma talvez kement ha diverkañ evit darn an doare diwezhañ anezhi. En degouezh-mañ e rankit diaskañ pe diguzhat ar stummoù diverket da ziwezhañ.",
        "undeletehistorynoadmin": "Diverket eo bet ar pennad-mañ. Displeget eo perak en diverradenn a-is, war un dro gant munudoù an implijerien o deus kemmet ar bajenn a-raok na vije diverket. N'eus nemet ar verourien a c'hall tapout krog en destenn bet diverket.",
        "undelete-revision": "Stumm diverket eus $1, (gwiriadenn eus $4 da $5) gant $3 :",
        "undeleterevision-missing": "Stumm fall pe diank. Pe hoc'h eus heuliet ul liamm fall, pe eo bet diziverket ar stumm pe c'hoazh eo bet lamet diouzh an dielloù.",
+       "undeleterevision-duplicate-revid": "N'eus ket bet gallet assevel {{PLURAL:$1|ur stumm|$1 stumm}} peogwir e oa en implij {{PLURAL:$1|e|o}} <code>rev_id</code> c'hoazh.",
        "undelete-nodiff": "N'eus bet kavet stumm kent ebet.",
        "undeletebtn": "Assevel",
        "undeletelink": "gwelet/assevel",
        "undeletedrevisions": "Adsavet {{PLURAL:$1|1 stumm|$1 stumm}}",
        "undeletedrevisions-files": "Adsavet ez ez eus bet {{PLURAL:$1|1 stumm|$1 stumm}} ha {{PLURAL:$2|1 restr|$2 restr}}",
        "undeletedfiles": "{{PLURAL:$1|1 restr|$1 restr}} adsavet",
-       "cannotundelete": "Dibosupl eo diziverkañ:\n$1",
+       "cannotundelete": "Dibosupl eo bet nullañ diziverkadennoù zo pe an holl anezho :\n$1",
        "undeletedpage": "'''Diziverket eo bet $1'''\n\nSellit ouzh [[Special:Log/delete|marilh an diverkadennoù]] evit teuler ur sell ouzh an diverkadennoù diwezhañ.",
        "undelete-header": "Gwelet [[Special:Log/delete|al log diverkañ]] evit ar pajennoù diverket nevez zo.",
        "undelete-search-title": "Klask pajennoù bet diverket",
        "namespace": "Esaouenn anv :",
        "invert": "Eilpennañ an dibab",
        "tooltip-invert": "Askañ ar voest-mañ da guzhat ar c'hemmoù er pajennoù stag ouzh an esaouenn anv diuzet (hag an esaouenn anv stag, m'emañ asket)",
+       "tooltip-whatlinkshere-invert": "Askañ ar log-mañ evit kuzhat liammoù ar pajennoù en esaouenn anv diuzet.",
        "namespace_association": "Esaouennoù anv liammet",
        "tooltip-namespace_association": "Askañ ar voest-mañ da lakaat ivez e-barzh ar gaozeadenn pe danvez an esaouenn anv liammet ouzh an esaouenn anv diuzet",
        "blanknamespace": "(Pennañ)",
        "sp-contributions-newbies-sub": "Evit an implijerien nevez",
        "sp-contributions-newbies-title": "Degasadennoù implijer evit ar c'hontoù nevez",
        "sp-contributions-blocklog": "Roll ar stankadennoù",
-       "sp-contributions-suppresslog": "degasadennoù diverket un implijer",
-       "sp-contributions-deleted": "Degasadennoù diverket",
+       "sp-contributions-suppresslog": "degasadennoù diverket {{GENDER:$1|an implijer|an implijerez}}",
+       "sp-contributions-deleted": "degasadennoù diverket {{GENDER:$1|an implijer|an implijerez}}",
        "sp-contributions-uploads": "Enporzhiadennoù",
        "sp-contributions-logs": "marilhoù",
        "sp-contributions-talk": "kaozeal",
        "unblock": "Distankañ an implijer",
        "blockip": "Stankañ an {{GENDER:$1|implijer|implijerez}}",
        "blockip-legend": "Stankañ an implijer",
-       "blockiptext": "Grit gant ar furmskrid a-is evit stankañ ar moned skrivañ ouzh ur chomlec'h IP pe un implijer bennak.\nSeurt diarbennoù n'hallont bezañ kemeret nemet evit mirout ouzh ar vandalerezh hag a-du gant an [[{{MediaWiki:Policy-url}}|erbedadennoù ha reolennoù da heuliañ]].\nRoit a-is an abeg resis (o verkañ, da skouer, roll ar pajennoù bet graet gaou outo).",
+       "blockiptext": "Grit gant ar furmskrid a-is evit stankañ ar moned skrivañ ouzh ur chomlec'h IP pe un implijer bennak.\nSeurt diarbennoù n'hallont bezañ kemeret nemet evit mirout ouzh ar vandalerezh hag a-du gant ar [[{{MediaWiki:Policy-url}}|reolennoù da vezañ heuliet]].\nRoit a-is an abeg resis (o verkañ, da skouer, roll ar pajennoù bet graet gaou outo).\nGallout a rit stankañ lijorennoù chomlec'hioù IP en ur ober gant an ereadur [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; /$1 eo al lijorenn hirañ aotreet evit IPv4 ha /$2 evit IPv6.",
        "ipaddressorusername": "Chomlec'h IP pe anv implijer",
        "ipbexpiry": "Pad ar stankadenn",
        "ipbreason": "Abeg :",
        "proxyblockreason": "Stanket eo bet hoc'h IP rak ur proksi digor eo. Trugarez da gelaouiñ ho pourvezer moned ouzh ar Genrouedad pe ho skoazell deknikel eus ar gudenn surentez-mañ.",
        "sorbsreason": "Rollet eo ho chomlec'h IP evel ur proksi digor en DNSBL implijet gant {{SITENAME}}.",
        "sorbs_create_account_reason": "Rollet eo ho chomlec'h IP evel ur proksi digor war an DNSBL implijet gant {{SITENAME}}. N'hallit ket krouiñ ur gont",
+       "softblockrangesreason": "N'eo ket aotreet kemmañ netra ebet gan tho chomlec'h IP ($1). Kevreit mar plij.",
+       "xffblockreason": "Stanket ez eus bet ur chomlec'h IP en talbenn X-Forwarded-For, ho hini pe hini ur servijer proksi implijet ganeoc'h. Setu aze abeg ar stankadenn : $1",
        "cant-see-hidden-user": "Stanket ha kuzhet eo bet dija an implijer emaoc'h o klask stankañ. Dre ma n'emañ ket ganeoc'h ar gwir hideuser, n'hallit ket gwelet pe kemmañ stankadenn an implijer.",
        "ipbblocked": "Ne c'hellit ket stankañ pe distankañ implijerien all, dre ma 'z oc'h stanket",
        "ipbnounblockself": "N'oc'h ket aotreet d'en em zistankañ ho unan",
        "lockedbyandtime": "(gant $1 d'an $2 da $3)",
        "move-page": "Dilec'hiañ $1",
        "move-page-legend": "Adenvel ur pennad",
-       "movepagetext": "Grit gant ar furmskrid a-is evit adenvel ur pennad hag adkas an holl stummoù kent anezhañ war-zu an anv nevez.\nDont a raio an titl kozh da vezañ ur bajenn adkas war-zu an titl nevez.\nGallout a rit nevesaat ent emgefre an adkasoù a-vremañ a gas d'an titl orin.\nNe vo ket kemmet liammoù an titl kozh ha ne vo ket dilec'hiet ar bajenn gaozeal, ma'z eus anezhi.\n\nMa rit ho soñj chom hep en ober, gwiriit mat an holl [[Special:DoubleRedirects|adkasoù doubl]] pe [[Special:BrokenRedirects|adkasoù torret]].\nC'hwi eo zo karget da wiriañ e kas mat al liammoù d'al lec'h int sañset kas.\n\nNotit mat '''ne vo ket''' dilec'hiet ma'z eus dija ur bajenn ganti an anv nevez, nemet e vefe un adkas ha ne vefe tamm istor ebet stag outi.\nDre se e c'haller adkas ur bajenn war-zu he lec'h orin ma oa kamm an adkas.\n\n'''DIWALLIT!'''\nGallout a ra kement-se bezañ ur c'hemm bras ha dic'hortoz evit ur pennad a vez sellet outi alies;\nbezit sur e komprenit mat an heuliadoù a-raok kenderc'hel ganti.",
-       "movepagetext-noredirectfixer": "Grit gant ar furmskrid a-is evit adenvel ur bajenn hag adkas an istor anezhi war-zu an anv nevez.\nDont a raio an titl kozh da vezañ ur bajenn adkas war-zu an titl nevez.\nGwiriit mat an [[Special:DoubleRedirects|adkasoù doubl]] hag an [[Special:BrokenRedirects|adkasoù torr]].\nEnnoc'h emañ fiziet gwiriañ e kendalc'h al liammoù da gas war-zu ar bajenn a rankont kas daveti.\n\nNotit mat ne vo '''ket''' dilec'hiet ar bajenn ma'z eus dija unan gant an titl nevez nemet e vefe goullo istor ar c'hemmoù degaset enni hag e vefe pe goullo ar bajenn pe e vefe un adkas anezhi. Gant se e c'haller adenvel ur bajenn war-zu he lec'h orin mard eo faziek an dilec'hiañ ha dibosupl eo frikañ ur bajenn zo anezhi c'hoazh. \n\n'''Diwallit !'''\nUr c'hemm bras ha dic'hortoz e c'hall bezañ evit ur bajenn a vez sellet outi alies ; bezit sur hoc'h eus komprenet mat an heuliadoù a-raok kenderc'hel ganti.",
-       "movepagetalktext": "Gant se e vo adanvet ent emgefre ar bajenn gaozeal stag, ma'z eus anezhi '''nemet ma:'''\n*ec'h adanvit ur bajenn war-du ul lec'h all,\n*ez eus ur bajenn gaozeal c'hoazh gant an anv nevez, pe\n*diweredekaet hoc'h eus ar bouton a-is.\n\nEn degouezh-se e rankot adenvel pe gendeuziñ ar bajenn c'hwi hoc'h-unan ma karit.",
+       "movepagetext": "Grit gant ar furmskrid a-is evit adenvel ur pennad hag adkas an holl stummoù kent anezhañ war-zu an anv nevez.\nDont a raio an titl kozh da vezañ ur bajenn adkas war-zu an titl nevez.\nGallout a rit nevesaat ent emgefre an adkasoù a-vremañ a gas d'an titl orin.\nMa rit ho soñj chom hep en ober, gwiriit mat an holl [[Special:DoubleRedirects|adkasoù doubl]] pe [[Special:BrokenRedirects|adkasoù torr]].\nC'hwi eo zo karget da wiriañ e kas mat al liammoù d'al lec'h int sañset kas.\n\nNotit mat <strong>ne vo ket</strong> dilec'hiet ar bjenn ma'z eus dija ur unan ganti an anv nevez, nemet e vefe un adkas ha ne vefe tamm istor ebet stag outi.\nDre se e c'haller adkas ur bajenn war-zu he lec'h orin ma oa kamm an adkas ha ne c'hallit ket frikañ ur bajenn zo anezhi c'hoazh.\n\n<strong>NOTENN :</strong>\nGallout a ra kement-se bezañ ur c'hemm bras ha dic'hortoz evit ur pennad a vez sellet outi alies;\nbezit sur e komprenit mat an heuliadoù a-raok kenderc'hel ganti.",
+       "movepagetext-noredirectfixer": "Grit gant ar furmskrid a-is evit adenvel ur bajenn hag adkas an istor anezhi war-zu an anv nevez.\nDont a raio an titl kozh da vezañ ur bajenn adkas war-zu an titl nevez.\nGwiriit mat an [[Special:DoubleRedirects|adkasoù doubl]] hag an [[Special:BrokenRedirects|adkasoù torr]].\nEnnoc'h emañ fiziet gwiriañ e kendalc'h al liammoù da gas war-zu ar bajenn a rankont kas daveti.\n\nNotit mat <strong>ne vo ket</strong> dilec'hiet ar bajenn ma'z eus dija unan gant an titl nevez nemet e vefe goullo istor ar c'hemmoù degaset enni hag e vefe pe goullo ar bajenn pe e vefe un adkas anezhi. Gant se e c'haller adenvel ur bajenn war-zu he lec'h orin mard eo faziek an dilec'hiañ ha dibosupl eo frikañ ur bajenn zo anezhi c'hoazh. \n\n<strong>'''Diwallit !</strong>\nUr c'hemm bras ha dic'hortoz e c'hall bezañ evit ur bajenn a vez sellet outi alies ; bezit sur hoc'h eus komprenet mat an heuliadoù a-raok kenderc'hel ganti.",
+       "movepagetalktext": "Ma askit al log-mañ e vo adkaset ent emgefre ar bajenn gaozeal stag outi d'an titl nevez nemet e vefe ur bajenn gaozeal na vefe ket goullo. \n\nEn degouezh-se e rankot adenvel pe gendeuziñ ar bajenn c'hwi hoc'h-unan ma karit.",
        "moveuserpage-warning": "'''Diwallit : ''' War-nes dilec'hiañ ur bajenn implijer emaoc'h. Notit mat n'eus nemet ar bajenn a vo dilec'hiet ha ne vo ''ket'' adanvet an implijer.",
+       "movecategorypage-warning": "<strong>Diwallit :</strong> Emaoc'h o vont da zilec'hiañ ur bajenn rummad. Taolit pled ne vo dilec'hiet nemet ar rummad ha ne vo ket treuzkaset <em>pajenn ebet</em> eus ar rummad kozh er rummad nevez.",
        "movenologintext": "A-benn gellout adenvel ur pennad e rankit bezañ un implijer enrollet ha bezañ [[Special:UserLogin|kevreet]].",
        "movenotallowed": "N'oc'h ket aotreet da zilec'hiañ pajennoù.",
        "movenotallowedfile": "N'oc'h ket aoteret da adenvel restroù.",
        "cant-move-to-user-page": "Noc'h ket aotreet da adenvel ur bajenn gant anv hini un implijer all (nemet un ispajenn e vefe).",
        "cant-move-category-page": "N'oc'h ket aotreet da zilec'hiañ pajennoù rummad.",
        "cant-move-to-category-page": "N'oc'h ket aotreet da zilec'hiañ ur bajenn d'ur bajenn rummad.",
+       "cant-move-subpages": "N'oc'h ket aotreet da zilec'hiañ ispajennoù.",
+       "namespace-nosubpages": "N'eo ket aotreet ober gant ispajennoù gant an esaouenn anv \"$1\".",
        "newtitle": "Titl nevez :",
        "move-watch": "Evezhiañ ar bajenn-mañ",
        "movepagebtn": "Adenvel ar pennad",
        "movelogpagetext": "Setu roll ar pajennoù bet savet un adkas evito.",
        "movesubpage": "{{PLURAL:$1|Ispajenn}}",
        "movesubpagetext": "Bez' ez eus $1 {{PLURAL:$1|ispajenn|ispajenn}} diskouezet a-is d'ar bajenn-mañ.",
+       "movesubpagetalktext": "Ar bajenn gaozeal stag he deus $1 {{PLURAL:$1|ispajenn}} diskouezet a-is.",
        "movenosubpage": "Ispajenn ebet d'ar bajenn-mañ.",
        "movereason": "Abeg :",
        "revertmove": "nullañ",
-       "delete_and_move_text": "==Ezhomm diverkañ==\n\nSavet eo ar pennad tal \"[[:$1]]\" c'hoazh.\nDiverkañ anezhañ a fell deoc'h ober evit reiñ lec'h d'an adkas ?",
+       "delete_and_move_text": "Savet eo ar bajenn dal \"[[:$1]]\" c'hoazh.\nDiverkañ anezhi a fell deoc'h ober evit reiñ lec'h d'an adkas ?",
        "delete_and_move_confirm": "Ya, diverkañ ar bajenn",
        "delete_and_move_reason": "Diverket evit ober lec'h d'an adkas \"[[$1]]\"",
        "selfmove": "Heñvel eo titl ar poent loc'hañ ha hini ar pal; n'haller ket adkas ur bajenn war-du he lec'h orin.",
        "immobile-target-namespace-iw": "N'eo ket ur pal mat al liammoù Interwiki evit adenvel pajennoù.",
        "immobile-source-page": "N'haller ket adenvel ar bajenn-mañ.",
        "immobile-target-page": "N'haller ket kas ar bajenn-mañ war-du an titl-se.",
+       "bad-target-model": "Ar pal c'hoantaet a ra gant ur patrom danvez disheñvel. N'haller ket treiñ e $2 adalek $1.",
        "imagenocrossnamespace": "N'haller ket dilec'hiañ ur skeudenn war-du un esaouenn anv n'eo ket hini ur skeudenn.",
        "nonfile-cannot-move-to-file": "N'haller ket dilec'hiañ un dra ha n'eo ket ur restr war-du an esaouenn anv restr",
        "imagetypemismatch": "Ne glot ket astenn nevez ar restr gant ar furmad-mañ.",
        "move-leave-redirect": "Lezel un adkas war-du an titl nevez",
        "protectedpagemovewarning": "'''DIWALLIT :''' Prennet eo bet ar bajenn-mañ, setu n'eus nemet an implijerien ganto gwrioù merañ a c'hall adenvel anezhi. Kasadenn ziwezhañ ar marilh a zo diskouezet amañ a-is evel dave :",
        "semiprotectedpagemovewarning": "'''NOTENN :''' Prennet eo bet ar bajenn-mañ, setu n'hall bezañ adanvet nemet gant an implijerien enskrivet. Kasadenn ziwezhañ ar marilh a zo diskouezet amañ a-is evel dave :",
-       "move-over-sharedrepo": "== Bez' ez eus eus ar restr-se dija ==\nBez' ez eus eus [[:$1]] war ur sanailh kenrannet dija. Ma cheñchit anv ar restr ne viot ket mui evit tizhout ar restr zo er sanailh kenrannet.",
+       "move-over-sharedrepo": "Bez' ez eus eus [[:$1]] war ur sanailh kenrannet dija. Ma cheñchit anv ar restr e vo friket ar restr kenrannet.",
        "file-exists-sharedrepo": "Implijet c'hoazh eo an anv dibabet gant ur restr zo war ur sanailh kenrannet.\nGrit gant un anv all.",
        "export": "Ezporzhiañ pajennoù",
        "exporttext": "Gallout a rit ezporzhiañ en XML an destenn ha pennad istor ur bajenn pe ur strollad pajennoù;\na-benn neuze e c'hall an disoc'h bezañ enporzhiet en ur wiki all a ya en-dro gant ar meziant MediaWiki dre [[Special:Import|ar bajenn enporzhiañ]].\n\nA-benn ezporzhiañ pajennoù, merkit an titloù anezho er voest skrid a-is, un titl dre linenn. Diuzit mar fell deoc'h kaout, pe get, ar stumm a-vremañ gant an holl stummoù kozh, gant linennoù itor ar bajenn, pe just ar bajenn red gant titouroù diwar-benn ar c'hemm diwezhañ.\n\nMard eo se e c'hallit ivez implijout ul liamm a seurt gant [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] evit ar bajenn [[{{MediaWiki:Mainpage}}]].",
        "thumbnail_image-type": "N'eo ket skoret ar seurt skeudennoù",
        "thumbnail_gd-library": "Kefluniadur diglok al levraoueg GD : dibosupl kavout an arc'hwel $1",
        "thumbnail_image-missing": "Evit doare n'eus ket eus ar restr : $1",
+       "thumbnail_image-failure-limit": "Re a daolioù-esae ($1 pe muioc'h) zo bet evit rentañ ar munud-mañ. Klaskit en-dro diwezhatoc'hik.",
        "import": "Enporzhiañ pajennoù",
-       "importinterwiki": "enporzhiadenn etrewiki",
-       "import-interwiki-text": "Diuzit ur wiki hag ur bajenn da enporzhiañ.\nMiret e vo deiziadoù ar stummmoù hag anvioù an aozerien.\nMiret eo an holl enporzhiadennoù etrewiki e-barzh [[Special:Log/import|log an enporzhiadennoù]].",
+       "importinterwiki": "Enporzhiadenn diwar ur wiki all",
+       "import-interwiki-text": "Diuzit ur wiki hag ur bajenn da enporzhiañ.\nMiret e vo deiziadoù ar stummoù hag anvioù an aozerien.\nMiret eo an holl enporzhiadennoù diwar wikioù all e-barzh [[Special:Log/import|istor an enporzhiadennoù]].",
        "import-interwiki-sourcewiki": "Wiki mammenn :",
        "import-interwiki-sourcepage": "Pajenn orin :",
        "import-interwiki-history": "Eilañ holl stummoù istor ar bajenn-mañ",
        "import-interwiki-submit": "Enporzhiañ",
        "import-mapping-default": "Enporzhiañ d'al lec'hiadurioù dre ziouer",
        "import-mapping-namespace": "Enporzhiañ d'un esaouenn anv :",
+       "import-mapping-subpage": "Enporzhiañ evel ispajennoù eus ar bajenn da-heul :",
        "import-upload-filename": "Anv ar restr :",
        "import-comment": "Notenn :",
        "importtext": "Ezporzhiit ar restr adal ar wiki orin en ur ober gant an arc'hwel [[Special:Export|ezporzhiañ]].\nEnrollit ar bajenn war hoc'h urzhiataer ha kargit anezhi amañ.",
        "importcantopen": "N'eus ket bet gallet digeriñ ar restr enporzhiet",
        "importbadinterwiki": "Liamm etrewiki fall",
        "importsuccess": "Deuet eo an enporzhiadenn da benn vat!",
-       "importnosources": "N'eus bet spisaet tamm mammenn etrewiki ebet ha diweredekaet eo enporzhiañ an Istor war-eeun.",
+       "importnosources": "N'eus bet spisaet wiki ebet da enporzhiañ diwarnañ ha diweredekaet eo enporzhiañ an istorioù war-eeun.",
        "importnofile": "N'eus bet enporzhiet restr ebet.",
        "importuploaderrorsize": "C'hwitet eo bet enporzhiañ ar restr. Brasoc'h eo ar restr eget ar vent aotreet.",
        "importuploaderrorpartial": "C'hwitet eo vet enporzhiañ ar restr. Enporzhiet evit darn eo bet hepken.",
        "import-nonewrevisions": "N'eus bet enporzhiet degasadenn ebet (aze e oant dija, pe distaolet e oant bet abalamour da fazioù).",
        "xml-error-string": "$1 war al linenn $2, bann $3 (okted $4) : $5",
        "import-upload": "Enporzhiañ roadennoù XML",
-       "import-token-mismatch": "Kollet eo bet roadennoù an dalc'h. Klaskit en-dro.",
+       "import-token-mismatch": "Kollet eo bet roadennoù an dalc'h.\n\nMarteze oc'h bet digevreet. <strong>Gwiriit emaoc'h mat kevreet ha klaskit en-dro</strong>.\nMa ne'z a ket en-dro c'hoazh, klaskit [[Special:UserLogout|digevreañ]] hag adkevreañ en-dro, ha gwiriit mat ec'h asant ho merdeer degemer toupinoù digant al lec'hienn-mañ.",
        "import-invalid-interwiki": "Dibosupl enporzhiañ adal ar wiki spisaet.",
        "import-error-edit": "N'eo ket bet enporzhiet ar bajenn \"$1\" peogwir n'oc'h ket aotreet da zegas kemmoù enni.",
        "import-error-create": "N'eo ket bet enporzhiet ar bajenn \"$1\" peogwir n'oc'h ket aotreet da grouiñ anezhi.",
        "import-error-interwiki": "Ne vez ket enporzhiet ar bajenn \"$1\" rak miret eo an anv evit liammoù diavaez (etrewiki).",
-       "import-error-special": "Ne vez ket enporzhiet ar bajenn \"$1\" rak stag eo ouzh un esaouenn anv dibar na aotre ket pajennoù.",
+       "import-error-special": "N'eo ket bet enporzhiet ar bajenn \"$1\" rak stag eo ouzh un esaouenn anv dibar na aotre ket pajennoù.",
        "import-error-invalid": "N'eo ket bet enporzhiet ar bajenn \"$1\" rak direizh e vefe hec'h anv er wiki-mañ.",
+       "import-error-unserialize": "N'eus ket bet gallet distiradekaat stumm $2 ar bajenn \"$1\". Merket eo ar stumm evel unan a ra gant ar patrom danvez $3 stiradekaat evel $4.",
+       "import-error-bad-location": "N'eus ket bet gallet stokañ war \"$1\" ar wiki-mañ ar stumm $2 a ra gant ar patrom danvez $3. N'eo ket skoret ar patrom-se er bajenn-se.",
        "import-options-wrong": "{{PLURAL:$2|Dibab fall|Dibaboù fall}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Pourchas a ra ar bennbajenn un titl n'eo ket reizh.",
        "import-rootpage-nosubpage": "Esaouenn anvioù \"$1\" eus ar bennpajenn ne aotre ket an ispajennoù.",
        "pageinfo-watchers": "Niver a dud o heuliañ",
        "pageinfo-visiting-watchers": "Niver a dud oc'h evezhiañ ar bajenn-mañ o deus sellet ouzh ar c'hemmoù diwezhañ bet graet enni",
        "pageinfo-few-watchers": "Nebeutoc'h eget $1 {{PLURAL:$1|lenner}}",
+       "pageinfo-few-visiting-watchers": "Gallout a ra bezañ, pe get, un implijer o teurel ur sell ouzh ar c'hemmoù diwezhañ",
        "pageinfo-redirects-name": "Niver a adkasoù war-zu ar bajenn-mañ",
        "pageinfo-subpages-name": "Ispajennoù eus ar bajenn-mañ",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|kasadur|kasadurioù}}; $3 {{PLURAL:$3|nann kasaduri|nann kasadurioù}})",
        "pageinfo-toolboxlink": "Titouroù ar bajenn",
        "pageinfo-redirectsto": "Adkas a ra da",
        "pageinfo-redirectsto-info": "Titouroù",
+       "pageinfo-contentpage": "Kontet evel ur bajenn danvez enni",
        "pageinfo-contentpage-yes": "Ya",
+       "pageinfo-protect-cascading": "Gwarezioù a-skalieradoù adalek amañ",
        "pageinfo-protect-cascading-yes": "Ya",
+       "pageinfo-protect-cascading-from": "Emañ ar gwarezioù o skalieradiñ adalek",
        "pageinfo-category-info": "Titouroù ar rummad",
        "pageinfo-category-total": "Niver hollek a izili",
        "pageinfo-category-pages": "Niver a bajennoù",
        "pageinfo-user-id": "ID an implijer",
        "markaspatrolleddiff": "Merkañ evel gwiriet",
        "markaspatrolledtext": "Merkañ ar pennad-mañ evel gwiriet",
+       "markaspatrolledtext-file": "Merkañ ar stumm-mañ eus ar restr evel gwiriet",
        "markedaspatrolled": "Merkañ evel gwiriet",
        "markedaspatrolledtext": "Merket eo bet ar stumm diuzet eus [[:$1]] evel gwiriet.",
        "rcpatroldisabled": "Diweredekaet ar gwiriañ ar C'hemmoù diwezhañ",
        "markedaspatrollederrortext": "Ret eo deoc'h spisaat ur stumm a-benn e verkañ evel gwiriet.",
        "markedaspatrollederror-noautopatrol": "N'oc'h ket aotreet da verkañ evel gwiriet ar c'hemmoù degaset ganeoc'h.",
        "markedaspatrollednotify": "Ar c'hemm-mañ da $1 zo bet merket evel gwiriet.",
+       "markedaspatrollederrornotify": "N'eus ket bet gallet merkañ evel gwiriet.",
        "patrol-log-page": "Log gwiriañ",
        "patrol-log-header": "Setu ur marilh eus ar stummoù patrouilhet.",
        "log-show-hide-patrol": "$1 istor ar stummoù gwiriet",
        "log-show-hide-tag": "$1 marilh an dikedenn",
        "confirm-markpatrolled-button": "Mat eo",
+       "confirm-markpatrolled-top": "Merkañ adweladenn $3 eus $2 evel gwiriet ?",
        "deletedrevision": "Diverket stumm kozh $1.",
        "filedeleteerror-short": "Fazi e-ser diverkañ ar restr : $1",
        "filedeleteerror-long": "Fazioù zo bet kavet e-ser diverkañ ar restr :\n\n$1",
        "svg-long-error": "Restr SVG direizh : $1",
        "show-big-image": "Restr orin",
        "show-big-image-preview": "Ment ar rakweled-mañ : $1.",
+       "show-big-image-preview-differ": "Ment rakwel $3 ar restr $2 : $1.",
        "show-big-image-other": "{{PLURAL:$2|pizhder all|pizhderioù all}} : $1.",
        "show-big-image-size": "$1 × $2 piksel",
        "file-info-gif-looped": "e kelc'h",
        "newimages-legend": "Sil",
        "newimages-label": "Anv ar restr (pe darn anezhi) :",
        "newimages-showbots": "Diskouez an ezporzhiadennoù graet gant robotoù",
+       "newimages-hidepatrolled": "Kuzhat ar enporzhiadennoù gwiriet",
        "noimages": "Netra da welet.",
+       "gallery-slideshow-toggle": "Gwintañ ar munudoù",
        "ilsubmit": "Klask",
        "bydate": "dre an deiziad anezho",
        "sp-newimages-showfrom": "Diskouez ar restroù nevez adal $1, $2",
        "invalidateemail": "Nullañ kadarnaat ar postel",
        "notificationemail_subject_changed": "Cheñchet eo bet ar chomlec'h postel enrollet e {{SITENAME}}",
        "notificationemail_subject_removed": "Lamet eo bet ar chomlec'h postel enrollet e {{SITENAME}}",
+       "notificationemail_body_changed": "Unan bennak, c'hwi moarvat, gant ar chomlec'h IP $1,\nen deus cheñchet chomlec'h postel ar gont \"$2\" da \"$3\" war {{SITENAME}}.\n\nMa n'eo ket bet graet ganeoc'h, kit e darempred gant unan eus merourien al lec'hienn raktal.",
+       "notificationemail_body_removed": "Unan bennak, c'hwi moarvat, gant ar chomlec'h IP $1,\nen deus dilamet chomlec'h postel ar gont \"$2\" war {{SITENAME}}.\n\nMa n'eo ket bet graet ganeoc'h, kit e darempred gant unan eus merourien al lec'hienn raktal.",
        "scarytranscludedisabled": "[Diweredekaet eo an treuzkludañ etrewiki]",
        "scarytranscludefailed": "[N'eus ket bet gallet tapout ar patrom evit $1]",
        "scarytranscludefailed-httpstatus": "[c'hwitet adtapout ar patrom evit $1: HTTP $2]",
        "scarytranscludetoolong": "[URL re hir]",
        "deletedwhileediting": "'''Diwallit''' : Diverket eo bet ar bajenn-mañ bremañ ha krog e oac'h da zegas kemmoù enni!",
-       "confirmrecreate": "Diverket eo bet ar pennad-mañ gant [[User:$1|$1]] ([[User talk:$1|kaozeal]]) goude ma vije bet kroget ganeoc'h kemmañ anezhañ :\n: ''$2''\nKadarnait mar plij e fell deoc'h krouiñ ar pennad-mañ da vat.",
-       "confirmrecreate-noreason": "Diverket eo bet ar pennad-mañ gant [[User:$1|$1]] ([[User talk:$1|kaozeal]]) goude ma vije bet kroget ganeoc'h kemmañ anezhañ. Kadarnait e fell deoc'h adkrouiñ ar pennad-mañ e gwirionez.",
+       "confirmrecreate": "{{GENDER:$1|Diverket}} eo bet ar bajenn-mañ gant [[User:$1|$1]] ([[User talk:$1|kaozeal]]) goude ma vije bet kroget ganeoc'h kemmañ anezhi :\n: ''$2''\nKadarnait mar plij e fell deoc'h krouiñ ar pennad-mañ en-dro.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Diverket}} eo bet ar bajenn-mañ gant [[User:$1|$1]] ([[User talk:$1|kaozeal]]) goude ma vije bet kroget ganeoc'h kemmañ anezhi. Kadarnait e fell deoc'h adkrouiñ ar pennad-mañ e gwirionez.",
        "recreate": "Adkrouiñ",
        "confirm_purge_button": "Mat eo",
        "confirm-purge-top": "Spurjañ krubuilh ar bajenn-mañ?",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|kaozeal]])",
        "timezone-local": "Lec'hel",
        "duplicate-defaultsort": "Diwallit : Frikañ a ra an alc'hwez dre ziouer \"$2\" an hini a oa a-raok \"$1\".",
+       "duplicate-displaytitle": "<strong>Diwallit :</strong> An titl da ziskwel \"$2\" a friko an hini a oa a oa betek-henn \"$1\".",
+       "restricted-displaytitle": "<strong>Diwallit :</strong> Lezet eo bet an titl da ziskwel \"$1\" a-gostez peogwir ne glot ket gant titl ar bajenn evel m'emañ bremañ.",
+       "invalid-indicator-name": "<strong>Fazi :</strong> Ne c'hall ket perzh <code>name</code> merkerioù statud ar bajenn bezañ goullo.",
        "version": "Stumm",
        "version-extensions": "Astennoù staliet",
        "version-skins": "Gwiskadurioù staliet",
        "version-ext-colheader-description": "Deskrivadur",
        "version-ext-colheader-credits": "Aozerien",
        "version-license-title": "Aotre-implijout evit $1",
+       "version-license-not-found": "N'eus bet kavet tamm titour pizh ebet diwar-benn an astenn-mañ.",
        "version-credits-title": "Kredoù evit $1",
+       "version-credits-not-found": "N'eus bet kavet tamm titour trukarekaat pizh ebet evit an astenn-mañ",
        "version-poweredby-credits": "Mont a ra ar wiki-mañ en-dro a-drugarez da '''[https://www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
        "version-poweredby-others": "re all",
        "version-poweredby-translators": "troerien translatewiki.net",
        "version-libraries-license": "Aotre-implijout",
        "version-libraries-description": "Deskrivadur",
        "version-libraries-authors": "Aozerien",
+       "redirect": "Adkas dre restroù, implijerien, pajennoù, stummoù pe ID marilhañ",
+       "redirect-summary": "Adkas a ra ar bajenn dibar-mañ war-zu ur restr (anv ar restr roet), ur bajenn (ID ar stumm pe ar bajenn roet), ur bajenn implijer (ID niverel an implijer roet), pe ur moned en ur marilh (ID ar marilh roet). Implij : [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], or [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Mont",
        "redirect-lookup": "Klask :",
        "redirect-value": "Talvoud :",
        "tag-filter-submit": "Silañ",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Dikedenn|Tikedenn}}]] : $2)",
        "tag-mw-contentmodelchange": "cheñch ar patrom danvez",
+       "tag-mw-contentmodelchange-description": "KEmmoù a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cheñch patrom danvez] ur bajenn",
        "tags-title": "Tikedennoù",
        "tags-intro": "Rollañ a ra ar bajenn-mañ an tikedennoù a c'hall ar meziant implijout da verkañ kemmoù hag o zalvoudegezh.",
        "tags-tag": "Anv an dikedenn",
        "tags-active-yes": "Ya",
        "tags-active-no": "Ket",
        "tags-source-extension": "Termenet gant ar meziant",
+       "tags-source-manual": "Lakaet e pleustr gant an dorn gant an implijerien hag ar robotoù",
        "tags-source-none": "N'emañ ket en implij ken",
        "tags-edit": "aozañ",
        "tags-delete": "diverkañ",
        "tags-manage-no-permission": "N'oc'h ket aotreet da verañ ar c'hemmañ tikedennoù.",
        "tags-manage-blocked": "N'hallit ket merañ ar c'hemmañ tikedennoù e-keit hag {{GENDER:$1|emaoc'h}} stanket.",
        "tags-create-heading": "Krouiñ un dikedenn nevez",
+       "tags-create-explanation": "Dre ziouer e c'hallo an implijerien hag ar robotoù ober gant an tikedennoù nevez krouet.",
        "tags-create-tag-name": "Anv an dikedenn :",
        "tags-create-reason": "Abeg :",
        "tags-create-submit": "Krouiñ",
        "tags-create-no-name": "Rekis eo merkañ anv un dikedenn.",
+       "tags-create-invalid-chars": "Ne c'hall ket bezañ skejoù (<code>,</code>) pe beskelloù (<code>/</code>) en anvioù an tikedennoù.",
+       "tags-create-invalid-title-chars": "Ne c'hall ket anvioù an tikedennoù ober gant arouezennoù na c'hallont ket bezañ implijet e titloù ar pajennoù .",
        "tags-create-already-exists": "Bez' ez eus eus an tikedenn \"$1\" c'hoazh.",
+       "tags-create-warnings-above": "Setu aze ar {{PLURAL:$2|c'hemenn-diwall zo|c'hemennoù-diwall zo}} deuet war wel p'eo bet klasket krouiñ an dikedenn \"$1\" :",
        "tags-create-warnings-below": "Kenderc'hel da grouiñ an dikedenn a fell deoc'h ?",
        "tags-delete-title": "Diverkañ an dikedenn",
+       "tags-delete-explanation-initial": "Emaoc'h o vont da ziverkañ an dikedenn \"$1\" a-ziwar an diaz roadennoù.",
        "tags-delete-reason": "Abeg :",
        "tags-delete-not-found": "N'eus ket eus an dikedenn \"$1\".",
        "tags-activate-title": "Gweredekaat an dikedenn",
        "logentry-suppress-event-legacy": "{{GENDER:$2|Kemmet}} eo bet dre guzh gwelusted darvoudoù ar marilh d'an $3 gant $1",
        "logentry-suppress-revision-legacy": "{{GENDER:$2|Kemmet}} eo bet dre guzh gwelusted ar reizhadennoù war ar bajenn $3 gant $1",
        "revdelete-content-hid": "danvez kuzet",
-       "revdelete-summary-hid": "kemmañ an diverrañ kuzhet",
+       "revdelete-summary-hid": "kuzhet diverradenn ar c'hemmoù",
        "revdelete-uname-hid": "anv implijer kuzhet",
        "revdelete-content-unhid": "danvez war wel",
        "revdelete-summary-unhid": "kemmañ an diverrañ zo war wel",
index 907822a..16910ae 100644 (file)
        "category_header": "Stranice u kategoriji \"$1\"",
        "subcategories": "Potkategorije",
        "category-media-header": "Datoteke u kategoriji \"$1\"",
-       "category-empty": "''Ova kategorija trenutno ne sadrži članke ni medije.''",
+       "category-empty": "<em>Ova kategorija trenutno ne sadrži članke ni medije.</em>",
        "hidden-categories": "{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}}",
        "hidden-category-category": "Skrivene kategorije",
        "category-subcat-count": "{{PLURAL:$2|Ova kategorija samo ima sljedeću potkategoriju.|Ova kategorija ima {{PLURAL:$1|sljedeću potkategoriju|sljedeće $1 potkategorije|sljedećih $1 potkategorija}}, od $2 ukupno.}}",
        "rcfilters-filter-editsbyother-label": "Tuđe izmjene",
        "rcfilters-filter-editsbyother-description": "Izmjene koje su napravili drugi korisnici (ne Vi).",
        "rcfilters-filtergroup-userExpLevel": "Korisničko iskustvo (samo za registrirane)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Novajlije",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Manje od 10 izmjena i 4 dana aktivnosti.",
-       "rcfilters-filter-userExpLevel-learner-label": "Učenici",
-       "rcfilters-filter-userExpLevel-learner-description": "Više dana aktivnosti i izmjena od \"novajlija\", ali manje od \"iskusnih korisnika\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Iskusni korisnici",
-       "rcfilters-filter-userExpLevel-experienced-description": "Preko 30 dana aktivnosti i 500 izmjena.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Novajlije",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 izmjena i 4 dana aktivnosti.",
+       "rcfilters-filter-user-experience-level-learner-label": "Učenici",
+       "rcfilters-filter-user-experience-level-learner-description": "Više dana aktivnosti i izmjena od \"novajlija\", ali manje od \"iskusnih korisnika\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Iskusni korisnici",
+       "rcfilters-filter-user-experience-level-experienced-description": "Preko 30 dana aktivnosti i 500 izmjena.",
        "rcfilters-filtergroup-automated": "Automatski doprinosi",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Izmjene napravljene automatiziranim alatima.",
index d187fbf..e8161bc 100644 (file)
        "rcfilters-filter-editsbyother-label": "Modificacions d'altres",
        "rcfilters-filter-editsbyother-description": "Modificacions creades per altres usuaris (no vostres).",
        "rcfilters-filtergroup-userExpLevel": "Nivell d'experiència (només per a usuaris registrats)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Novells",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Menys de 10 edicions i 4 dies d'activitat.",
-       "rcfilters-filter-userExpLevel-learner-label": "Aprenents",
-       "rcfilters-filter-userExpLevel-learner-description": "Més dies d'activitat i més edicions que els 'novells' però menys que els 'usuaris experimentats'.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Usuaris experimentats",
-       "rcfilters-filter-userExpLevel-experienced-description": "Més de 30 dies d'activitat i més de 500 edicions.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Novells",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Menys de 10 edicions i 4 dies d'activitat.",
+       "rcfilters-filter-user-experience-level-learner-label": "Aprenents",
+       "rcfilters-filter-user-experience-level-learner-description": "Més dies d'activitat i més edicions que els 'novells' però menys que els 'usuaris experimentats'.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Usuaris experimentats",
+       "rcfilters-filter-user-experience-level-experienced-description": "Més de 30 dies d'activitat i més de 500 edicions.",
        "rcfilters-filtergroup-automated": "Contribucions automatitzades",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Modificacions fetes per eines automatitzades.",
index ec38e90..bc2b03a 100644 (file)
        "login": "Системин довзийтар",
        "login-security": "Хьой хилар бакъде",
        "nav-login-createaccount": "Довзийтар / дӀаяздар кхоллар",
-       "userlogin": "Довзийтар я декъашхочун дӀаяздар кхоллар",
-       "userloginnocreate": "Довзийта",
        "logout": "Болх дӀаберзор",
        "userlogout": "Болх дӀаберзор",
        "notloggedin": "ЦӀарца доцуш",
        "userlogin-noaccount": "Декъашхочун дӀаяздар дац хьа?",
        "userlogin-joinproject": "Проектехь дӀаяздар кхоллар",
-       "nologin": "Декъашхочун дӀаяздар дац хьа? '''$1'''.",
-       "nologinlink": "Кхолла декъашхочун дӀаяздар",
        "createaccount": "Кхолла декъашхочун дӀаяздар",
-       "gotaccount": "ДӀаяздар кхоьллин ахь? '''$1'''.",
-       "gotaccountlink": "Довзийта",
-       "userlogin-resetlink": "Чуволу/йолу хаам биц бела?",
        "userlogin-resetpassword-link": "Пароль кхоссар?",
        "userlogin-helplink2": "Системин чудахаран гӀодар",
        "userlogin-loggedin": "Хьо {{GENDER:$1|$1}} цӀарца чохь ву/ю.\nЛахара форманца кхин цӀарца чугӀо.",
        "createacct-another-email-ph": "ДӀаязде электронан поштан адрес",
        "createaccountmail": "Лелае цахууш нисйина хана пароль, кхин язйинчу электронан адрес тӀе яийта и.",
        "createacct-realname": "Хьан цӀе (ца язйича мега)",
-       "createaccountreason": "Бахьана:",
        "createacct-reason": "Бахьана",
        "createacct-reason-ph": "Стен кхуллуш ду ахьа керла декъашхочун дӀаяздар",
        "createacct-submit": "Кхолла декъашхочун дӀаяздар",
        "rcfilters-filter-editsbyself-label": "Хьан дисдарш",
        "rcfilters-filter-editsbyself-description": "Хьан нисдарш.",
        "rcfilters-filter-editsbyother-label": "Кхечу декъашхойн нисдарш",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Керланиш",
-       "rcfilters-filter-userExpLevel-learner-label": "Доьшуш берш",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Керланиш",
+       "rcfilters-filter-user-experience-level-learner-label": "Доьшуш берш",
        "rcnotefrom": "Лахахь гайтина тӀера <strong>$2</strong> (хийцамаш <strong>$1</strong> кӀезиг).",
        "rclistfrom": "Гайта хийцам {{CURRENTYEAR}} шеран {{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTTIME}} бина болу",
        "rcshowhideminor": "$1 кегийра нисдарш",
        "uploadnologintext": "Серверан чу файлаш яха ахьа дан дезарг ду $1.",
        "uploaderror": "Файл чуяккхаран гӀалат",
        "upload-recreate-warning": "'''Тегам бе: иштта цӀе йолу файл дӀаяьккхина я цӀе хийцина.'''\n\nЛахахьа гойтуш ю хӀокху агӀона тептар:",
-       "uploadtext": "Ð\9bелайе Ñ\85Ó\80аÑ\80а Ð°Ð³Ó\80о Ñ\81еÑ\80веÑ\80 Ñ\87Ñ\83 Ñ\84айлаÑ\88 Ð¹Ð¾Ñ\85Ñ\83Ñ\88.\nÐ¥Ñ\8cалÑ\85о Ñ\87Ñ\83Ñ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 Ñ\85Ñ\8cажа,  [[Special:FileList|кÑ\85Ñ\83заÑ\85Ñ\8c]]. Ð\9aÑ\85ин Ñ\87Ñ\83Ñ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 Ð´Ó\80аÑ\8fзло [[Special:Log/upload|Ñ\87Ñ\83Ñ\8fÑ\85аÑ\80ан Ñ\82епÑ\82аÑ\80 Ñ\87оÑ\85Ñ\8c]], Ð´Ó\80аÑ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 ÐºÐ°Ñ\80о Ð¹Ð¸Ñ\88 Ñ\8e [[Special:Log/delete|кÑ\85Ñ\83заÑ\85Ñ\8c]].\n\nФайл Ð°Ð³Ó\80она Ñ\87Ñ\83йилла Ð»ÐµÐ»Ð°Ð±Ðµ Ð»Ð°Ñ\85аÑ\80а Ð¼Ð¾Ð³Ó\80анаÑ\88:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''' Ñ\84айла Ð¤Ð°Ð¹Ð»Ð°Ð½ Ñ\8eÑ\8cззина Ð²ÐµÑ\80Ñ\81и Ñ\87Ñ\83йиллÑ\83Ñ\88;\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|Ñ\86Ñ\83наÑ\85Ñ\8c Ð»Ð°Ñ\8cÑ\86на Ñ\85аам]]</nowiki></code>''' 200 Ð¿Ð¸ÐºÑ\81елÑ\8c Ð±Ð°Ñ\80амеÑ\85Ñ\8c Ñ\84айл Ñ\87Ñ\83йилаÑ\80 Ð±Ñ\83Ñ\85аÑ\85Ñ\8c Ñ\86Ñ\83наÑ\85Ñ\8c лаьцна могӀа а болуш;\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' файлан тӀе хьажорг хӀотайо файл агӀонгахь ца гуш.",
+       "uploadtext": "Ð\9bелайе Ñ\85Ó\80аÑ\80а Ð°Ð³Ó\80о Ñ\81еÑ\80веÑ\80 Ñ\87Ñ\83 Ñ\84айлаÑ\88 Ð¹Ð¾Ñ\85Ñ\83Ñ\88.\nÐ¥Ñ\8cалÑ\85о Ñ\87Ñ\83Ñ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 Ñ\85Ñ\8cажа,  [[Special:FileList|кÑ\85Ñ\83заÑ\85Ñ\8c]]. Ð\9aÑ\85ин Ñ\87Ñ\83Ñ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 Ð´Ó\80аÑ\8fзло [[Special:Log/upload|Ñ\87Ñ\83Ñ\8fÑ\85аÑ\80ан Ñ\82епÑ\82аÑ\80 Ñ\87оÑ\85Ñ\8c]], Ð´Ó\80аÑ\8fÑ\8cÑ\85на Ñ\84айлаÑ\88 ÐºÐ°Ñ\80о Ð¹Ð¸Ñ\88 Ñ\8e [[Special:Log/delete|кÑ\85Ñ\83заÑ\85Ñ\8c]].\n\nФайл Ð°Ð³Ó\80она Ñ\87Ñ\83йилла Ð»ÐµÐ»Ð°Ð±Ðµ Ð»Ð°Ñ\85аÑ\80а Ð¼Ð¾Ð³Ó\80анаÑ\88:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''' Ñ\84айла Ð¤Ð°Ð¹Ð»Ð°Ð½ Ñ\8eÑ\8cззина Ð²ÐµÑ\80Ñ\81и Ñ\87Ñ\83йиллÑ\83Ñ\88;\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|Ñ\86Ñ\83наÑ\85Ñ\8c Ð»Ð°Ñ\8cÑ\86на Ñ\85аам]]</nowiki></code>''' 200 Ð¿Ð¸ÐºÑ\81елÑ\8c Ð±Ð°Ñ\80амеÑ\85Ñ\8c Ñ\84айл Ñ\87Ñ\83йиллаÑ\80 Ð±Ñ\83Ñ\85аÑ\85Ñ\8c Ñ\86Ñ\83Ñ\8cнаÑ\85 лаьцна могӀа а болуш;\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' файлан тӀе хьажорг хӀотайо файл агӀонгахь ца гуш.",
        "upload-permitted": "Магийна файлийн тайпанаш: $1.",
        "upload-preferred": "Магийна файлийн тайпанаш: $1.",
        "upload-prohibited": "Магийна доцу файлийн тайпанаш: $1.",
        "logentry-managetags-create": "$1 {{GENDER:$2|Кхоьллина}} билгало «$4»",
        "log-name-tag": "Билгалонийн тептар",
        "rightsnone": "(яц)",
-       "revdelete-summary": "хийцамах лаьцна",
        "feedback-adding": "АгӀона хетарг тӀетохар...",
        "feedback-back": "ЮхагӀо",
        "feedback-bugornote": "Хьайн техникин халонах лаьцна яздан хӀума делахь, дехар до, [$1 хаам бе тхоьга].\nДацахь хьан йиш ю хӀокху атта кепаца «[$3 $2]» агӀонг къамел тӀетоха хьан декъашхочун цӀарца, кхин лелош йолу браузер билгал еш.",
index 2a9ecd5..ba0fb7f 100644 (file)
@@ -11,7 +11,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Leeheonjin",
-                       "Macofe"
+                       "Macofe",
+                       "Bentong Isles"
                ]
        },
        "tog-underline": "Binadlisan nga mga sumpay:",
        "actions": "Mga lihok",
        "namespaces": "Mga ngalang espasyo",
        "variants": "Mga baryant",
+       "navigation-heading": "Menu sa nabigasyon",
        "errorpagetitle": "Sayop",
        "returnto": "Balik sa $1.",
        "tagline": "Gikan sa {{SITENAME}}",
        "searcharticle": "Sige",
        "history": "Kaagi ning panid",
        "history_short": "Kaagi",
+       "history_small": "kaagi",
        "updatedmarker": "na-update sugod sa akong kataposang bisita",
        "printableversion": "Mapatik nga bersiyon",
        "permalink": "Permanenteng sumpay",
        "print": "I-print",
+       "view": "Tan-awa",
+       "view-foreign": "Tan-awa sa $1",
        "edit": "Usba",
        "create": "Himoa",
        "editthispage": "Usba kining panid",
        "unprotectthispage": "Ayaw na kini protektahi",
        "newpage": "Bag-ong panid",
        "talkpage": "Hisgoti kining panid",
-       "talkpagelinktext": "Hisgot",
+       "talkpagelinktext": "hisgot",
        "specialpage": "Espesyal nga panid",
        "personaltools": "Personal nga galamiton",
        "articlepage": "Tan-awa ang panid sa sulod",
        "otherlanguages": "Sa ubang pinulongan",
        "redirectedfrom": "(Naredirek gikan sa $1)",
        "redirectpagesub": "Panid sa redirekta",
+       "redirectto": "Iadto sa:",
        "lastmodifiedat": "Kini nga panid kataposang giusab niadtong $2, $1.",
        "viewcount": "Naablihan na sa {{PLURAL:$1|maka-usa|$1 ka higayon}} ang kining panid.",
        "protectedpage": "Giprotektahang panid",
        "viewsourceold": "tan-awa ang ginikanan",
        "editlink": "usba",
        "viewsourcelink": "tan-awa ang ginikanan",
-       "editsectionhint": "Usba ang seksyong: $1",
+       "editsectionhint": "Usba ang seksyon: $1",
        "toc": "Mga sulod",
        "showtoc": "ipakita",
        "hidetoc": "tagoa",
        "nstab-template": "Plantilya",
        "nstab-help": "Panid sa tabang",
        "nstab-category": "Kategoriya",
+       "mainpage-nstab": "Unang Panid",
        "nosuchaction": "Walay maong aksyon",
        "nosuchactiontext": "Ang aksyon nga anaa sa URL wala gi-ila sa wiki",
        "nosuchspecialpage": "Walay maong espesyal nga panid",
        "externaldberror": "May nahitabong authentication database error o kaha wala ka tugoti nga mag-update sa imong eksternal nga akawnt.",
        "login": "Sulod",
        "nav-login-createaccount": "Rehistro / Dayon",
-       "userlogin": "Rehistro / Dayon",
        "logout": "Biya",
        "userlogout": "Bıveciye",
        "notloggedin": "Wala ka pa masulod",
-       "nologin": "Wala pay akawnt? '''$1'''.",
-       "nologinlink": "Paghimo og akawnt",
+       "userlogin-noaccount": "Wala pay akawnt?",
+       "userlogin-joinproject": "Apil sa {{SITENAME}}",
        "createaccount": "Paghimo'g akawnt",
-       "gotaccount": "Naa ka nay akawnt? '''$1'''.",
-       "gotaccountlink": "Dayon",
+       "createacct-emailoptional": "Adres sa email (opsyonal)",
        "createaccountmail": "sa e-mail",
+       "createacct-benefit-heading": "Ang {{SITENAME}} hinimo sa mga tawo sama kanimo.",
+       "createacct-benefit-body3": "labing bag-ong {{PLURAL:$1|ka tampo|ka mga tampo}}",
        "badretype": "Ang mga pasword nga imong gientra wala mag-match.",
        "userexists": "Ang ngalan sa tiggamit nga imong gisulat nagamit na.\nPalihug pagpili og lain nga ngalan.",
        "loginerror": "Sayop sa pagdayon",
        "wrongpassword": "Sayop nga pasword ang naentra.\nPalihog sulayi'g usab.",
        "mailmypassword": "I-email ang bag-ong pasword",
        "loginlanguagelabel": "Pinulongan: $1",
+       "pt-login": "Sulod",
+       "pt-createaccount": "Paghimo og akawnt",
        "changepassword": "Usba ang pasword",
+       "passwordreset": "Usba ang password",
        "bold_sample": "Gilugom nga teksto",
        "bold_tip": "Gilugom nga teksto",
        "italic_sample": "Gitakilid nga teksto",
        "showdiff": "Ipakita ang kalainan",
        "anoneditwarning": "'''Pahibalo:''' Wala ikaw maka-login.\nAng imong ''IP address'' maoy itala sa kaagi niini nga panid.",
        "summary-preview": "Paunang tan-aw sa mubong sugid:",
+       "loginreqlink": "sulod",
        "newarticle": "(Bag-o)",
        "newarticletext": "Mitulpok ka sa sumpay ngadto sa usa ka wala pa masulat nga panid.\nAron mahimo ang maong panid, pagtayp sa kahon sa ubos (tan-awa ang [$1 panid sa tabang] alang sa dugang impormasyon).\nKon miabot ka dinhi pinaagi sa usa ka sayop, palihog tuploka ang back nga tuplokanan sa imong brawser.",
-       "noarticletext": "Sa kasamtangan walay sulod nga teksto ang kining panid.\nPuyde nimong  [[Special:Search/{{PAGENAME}}|pangitaon kining titulo sa panid]] sa ubang mga panid, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pangitaa ang related nga mga log],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} usba kining panid]</span>.",
+       "noarticletext": "Sa kasamtangan walay sulod nga teksto kining panid.\nPuyde nimong [[Special:Search/{{PAGENAME}}|pangitaon kining titulo sa panid]] sa ubang mga panid, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pangitaa ang mga may kalabotan nga log],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} mugnaa kining panid]</span>.",
        "userpage-userdoesnotexist": "Ang akawnt sa tiggamit nga \"<nowiki>$1</nowiki>\" wala marehistro. Palihug tan-awa kon buot nimong himoon/usbon ang kining panid.",
        "previewnote": "'''Hinumdomi nga kini usa lang ka paunang tan-aw; wala pa matipigi ang imong giusab!'''",
        "editing": "Nagausab sa $1",
        "notextmatches": "Walay misaktong teksto sa panid",
        "prevn": "miaging {{PLURAL:$1|$1}}",
        "nextn": "sunod {{PLURAL:$1|$1}}",
+       "nextn-title": "Sunod nga $1 {{PLURAL:$1|resulta|mga resulta}}",
+       "shown-title": "Pagpakita og $1 {{PLURAL:$1|ka resulta|ka mga resulta}} matag panid",
        "viewprevnext": "Tan-awa sa ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchprofile-everything": "Tanan ng bagay",
+       "searchprofile-articles": "Mga panid sa sulod",
+       "searchprofile-images": "Multimedia",
+       "searchprofile-everything": "Tanan",
+       "searchprofile-articles-tooltip": "Pangita sa $1",
+       "searchprofile-images-tooltip": "Pangita og mga payl",
+       "searchprofile-everything-tooltip": "Pangita sa tanang sulod (lakip ang mga panid sa hisgot)",
+       "searchprofile-advanced-tooltip": "Pangita sa mga espesyal nga <i>namespaces</i>",
        "search-result-size": "$1 ({{PLURAL:$2|1 pulong|$2 ka mga pulong}})",
-       "search-redirect": "(redirek $1)",
+       "search-redirect": "(redirek gikan sa $1)",
        "search-section": "(bahin $1)",
        "search-suggest": "Imo bang buot ipasabot: $1",
        "search-interwiki-caption": "Mga kaubang proyekto",
        "saveprefs": "Tipigi",
        "restoreprefs": "Ibalik ang tanang default settings",
        "prefs-editing": "Nagausab",
-       "rows": "Mga row:",
-       "columns": "Mga kolum:",
        "searchresultshead": "Pangitaa",
        "stub-threshold": "Threshold para sa <a href=\"#\" class=\"stub\">stub link</a> formatting (bytes):",
        "recentchangesdays": "Mga adlaw nga ipakita sa bag-ong giusab:",
        "prefs-signature": "Pirma",
        "group-sysop": "Mga tagdumala",
        "grouppage-sysop": "{{ns:project}}:Mga tigdumala",
+       "right-writeapi": "Paggamit sa write API",
        "newuserlogpage": "Log sa paghimo'g gumagamit",
        "newuserlogpagetext": "Kini mao ang ''log'' sa bag-ong namugnang mga gumagamit.",
        "rightslog": "Log sa mga katungod sa gumagamit",
        "action-undelete": "ayaaw papasa ang kining panid",
        "action-suppressrevision": "i-rebyu ug ibalik ang kining nakatagong rebisyon",
        "nchanges": "$1 {{PLURAL:$1|ka pag-usab|ka mga pag-usab}}",
+       "enhancedrc-history": "kaagi",
        "recentchanges": "Mga bag-ong giusab",
        "recentchanges-legend": "Mga opsyon sa bag-ong pag-usab",
        "recentchanges-feed-description": "Bantayi ang kinabag-ohang mga pag-usab sa wiki niining feed.",
+       "recentchanges-label-newpage": "Kining pag-usab nagmugna og bag-ong panid",
+       "recentchanges-label-minor": "Ginagmay nga pag-usab",
+       "recentchanges-label-bot": "Kining pag-usab ginamit ang <i>bot</i>",
+       "recentchanges-label-unpatrolled": "Kining maong pag-usab wala pa masuhid",
+       "recentchanges-label-plusminus": "Ang gidak-on sa panid nausab ning gidaghanona sa byte",
+       "recentchanges-legend-heading": "<strong>Pagpasabot:</strong>",
        "rclistfrom": "Ipakita ang mga bag-ong pag-usab gikan $3 $2",
        "rcshowhideminor": "$1 menor nga pag-usab",
+       "rcshowhideminor-hide": "Tagoa",
        "rcshowhidebots": "$1 mga bot",
-       "rcshowhideliu": "$1 mga gumagamit nga naka-log-in",
+       "rcshowhidebots-show": "Tagoa",
+       "rcshowhideliu": "$1 ka mga gumagamit ang natala",
+       "rcshowhideliu-hide": "Tagoa",
        "rcshowhideanons": "$1 mga wala mailhing gumagamit",
+       "rcshowhideanons-hide": "Tagoa",
        "rcshowhidemine": "$1 akong mga pag-usab",
+       "rcshowhidemine-hide": "Tagoa",
        "rclinks": "Ipakita ang miaging $1 ka kausaban sa miaging $2 ka mga adlaw<br />$3",
        "diff": "kalainan",
        "hist": "kaagi",
        "minoreditletter": "m",
        "newpageletter": "B",
        "boteditletter": "b",
+       "rc-change-size-new": "$1 {{PLURAL:$1|byte|mga byte}} human sa pag-usab",
        "rc-enhanced-expand": "Ipakita ang mga detalye (JavaScript kinahanglan)",
        "rc-enhanced-hide": "Tagoa ang mga detalye",
        "recentchangeslinked": "Mga may kalabotang kausaban",
        "filehist-user": "Tiggamit",
        "filehist-dimensions": "Mga dimensyon",
        "filehist-comment": "Komento",
-       "imagelinks": "Mga sumpay sa payl",
+       "imagelinks": "Mga paggamit sa payl",
        "linkstoimage": "Ang mosunod nga {{PLURAL:$1|mga panid misumpay|$1 panid misumpay}} niining payl:",
        "sharedupload": "Ang kining payl gikan sa $1 ug mahimong gigamit sa ubang mga proyekto.",
+       "sharedupload-desc-here": "Kining maong payl gikan sa $1 ug mahimong gigamit sa ubang proyekto.\nAng [$2 deskripsyon] niini didto gipakita sa ubos.",
        "uploadnewversion-linktext": "Pag-upload og bag-ong bersyon niining payl",
+       "upload-disallowed-here": "Dili mo mahimong pulihan kining maong payl.",
        "randompage": "Bisan unsang panid",
        "statistics": "Estadistika",
        "nbytes": "$1 {{PLURAL:$1|byte|mga byte}}",
        "pager-older-n": "{{PLURAL:$1|mas daan1|mas daan $1}}",
        "booksources": "Mga reperensiyang libro",
        "booksources-search-legend": "Pangitaa ang mga reperensiyang libro",
+       "booksources-search": "Pangita",
        "log": "Mga log",
        "allpages": "Tanang panid",
        "prevpage": "Miaging panid ($1)",
        "namespace": "Ngalang espasyo:",
        "invert": "Baliha ang gipili",
        "blanknamespace": "(Meyn)",
-       "contributions": "Mga tampo ning gumagamit",
+       "contributions": "Mga tampo ning {{GENDER:$1|gumagamit}}",
        "contributions-title": "Mga tampo sa gumagamit para kang $1",
        "mycontris": "Akong tampo",
+       "anoncontribs": "Mga tampo",
        "contribsub2": "Para $1 ($2)",
        "uctop": "(hitaas)",
        "month": "Gikan sa bulan (ug mas sayo pa):",
        "tooltip-pt-mycontris": "Talaan sa akong mga tampo",
        "tooltip-pt-login": "Gihangyo ka namo sa pag-''log-in'', apan wala kini gikinahanglan aron makausab ka sa mga panid.",
        "tooltip-pt-logout": "Biya",
+       "tooltip-pt-createaccount": "Gidasig ka sa paghimo og akawnt ug sa pagpaila; apan dili kini kinahanglanon",
        "tooltip-ca-talk": "Panaghisgot kabahin sa panid",
-       "tooltip-ca-edit": "Mahimo mong usbon ang kining panid. Palihog gamita ang ''Paunang tan-aw'' nga tuplokanan bag-o nimotipigan ang panid.",
+       "tooltip-ca-edit": "Usba kining panid",
        "tooltip-ca-addsection": "Paghimo'g bag-ong seksyon",
        "tooltip-ca-viewsource": "Giprotektahan kining panid.\nPwede nimong tan-awon ang ginikanan.",
        "tooltip-ca-history": "Mga miaging rebisyon ning panid",
        "tooltip-n-mainpage-description": "Bisitaha ang Unang Panid",
        "tooltip-n-portal": "Kabahin sa proyekto, unsay imong mahimo, asa mangita sa mga impormasyon",
        "tooltip-n-currentevents": "Pangita og nahaunang impormasyon sa mga bag-ong panghitabo",
-       "tooltip-n-recentchanges": "Ang talaan sa mga bag-ong giusab sa wiki.",
+       "tooltip-n-recentchanges": "Ang talaan sa mga bag-ong giusab sa wiki",
        "tooltip-n-randompage": "Pag-abli og bisan unsang panid",
        "tooltip-n-help": "Ang dapit nga angay mong pangitaan.",
        "tooltip-t-whatlinkshere": "Talaan sa mga wiki nga panid nga misumpay dinhi",
        "tooltip-watch": "Ipuno kining maong panid sa imong mga gibantayan",
        "tooltip-rollback": "\"Rollback\" mo-revert sa (mga) pag-usab niining panid ngadto sa kinaulhing mitampo sa usa lang ka klik",
        "tooltip-undo": "Ang \"undo\" mo-revert niining pag-usab ug moabli sa edit form sa paunang tan-aw nga mode.\nPuyde dugangan og rason sa mubong sugid.",
+       "tooltip-summary": "Mubong sugid",
+       "simpleantispam-label": "Pagkontra sa <i>spam</i>.\n<strong>Ayaw</stong> ni sudli!",
+       "pageinfo-toolboxlink": "Impormasyon kabahin sa panid",
        "previousdiff": "Mas daang pag-usab",
        "nextdiff": "Mas bag-ong pag-usab →",
        "file-info-size": "$1 × $2 pixels, size sa payl: $3, MIME type: $4",
        "file-nohires": "Walay mas taas nga resolusyon.",
        "svg-long-desc": "SVG nga payl, nominally $1 × $2 pixels, size sa payl: $3",
        "show-big-image": "Tibuok resolusyon",
+       "show-big-image-preview": "Gidak-on ning maong paunang tan-aw: $1.",
+       "show-big-image-size": "$1 x $2 ka mga piksel",
        "bad_image_list": "Ang pormat mao ang mosunod:\n\nAng mga list items (mga linya nga nagsugod sa*) ang gikonsiderar.\nAng unang sumpay sa linya kinahanglang sumpay sa payl nga daot.\nAng bisan unsang mosunod nga mga sumpay sa parehong linya gikonsiderar nga mga eksepsyon, i.e. mga panid diin ang payl mahimong inline.",
        "metadata": "Metadata",
        "metadata-help": "Ang kining payl adunay dugang nga impormasyon, mahimong nadugang gikan sa digital camera o scanner nga gigamit sa paghimo o pag-digitize niini.\nKon ang payl namodipikar gikan sa orihinal nga estado, ang ubang detalye mamahimong dili moreplek sa namodipikar nga payl.",
        "metadata-expand": "Ipakita ang mas daghang detalye",
        "metadata-collapse": "Tagoa ang mga ekstended nga detalye",
-       "metadata-fields": "Ang XIF metadata fields nga nakatala niining mensahe iapil sa display sa panid sa imahen kon gi-collapse ang metadata table.\nAng uban default nga nakatago.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "metadata-fields": "Ang <i>XIF metadata fields</i> nga nakatala niining mensahe iapil sa panid sa hulagway kon gi-<i>collapse</i> ang han-ay sa <i>metadata</i>.\n\nAng uban nakatago gawas kon ipakita.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "namespacesall": "tanan",
        "monthsall": "tanan",
        "watchlisttools-view": "Tan-awa ang may kalabotan nga mga pag-usab",
        "watchlisttools-edit": "Tan-awa ug usba ang mga gibantayan",
        "watchlisttools-raw": "Usba ang hilaw nga talaan sa gibantayan",
        "specialpages": "Espesyal nga mga panid",
+       "tag-filter": "[[Special:Tags|Tag]] nga pilter:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Mga tag}}]]: $2)",
        "revdelete-restricted": "mga na-aplay nga restriksyon sa mga tagdumala",
        "revdelete-unrestricted": "gitangtang ang mga restriksyon alang sa mga tagdumala",
+       "logentry-newusers-create": "Ang akawnt nga $1 {{GENDER:$2|namugna}}",
        "rightsnone": "(wala)",
-       "revdelete-summary": "mubong sugid sa pag-usab"
+       "searchsuggest-search": "Pangitaa {{SITENAME}}"
 }
index c673fe0..3933338 100644 (file)
        "showhideselectedversions": "دیاریکردنی پێداچوونەوە ھەڵبژێردراوەکان بگۆڕە",
        "editundo": "پووچەڵکردنەوە",
        "diff-empty": "(بەبێ جیاوازی)",
+       "diff-multi-sameuser": "({{PLURAL:$1|یەک دەستکاری مامناوەند|$1 دەستکاری}} لەلایەن ھەمان بەکارھێنەرەوە بیشاننادرێت.",
        "searchresults": "ئاکامەکانی گەڕان",
        "searchresults-title": "ئاکامەکانی گەڕان بۆ «$1»",
        "titlematches": "ئەو سەرناوانەی دەگونجن",
        "undelete-show-file-submit": "بەڵێ",
        "namespace": "بۆشاییی ناو:",
        "invert": "ھەڵبژاردەکان پێچەوانە بکە",
+       "tooltip-invert": "نیشانە لەم بۆکسە بدە بۆ شاردنەوەی گۆڕانکارییەکانی ئەو پەڕانەی کە لە بۆشایی ناوی ھەڵبژێردراو و پەیوەندیدارەکاندا ئەنجام دراون.",
        "namespace_association": "بۆشاییی ناوی پەیوەندیدار",
+       "tooltip-namespace_association": "ھەروەھا نیشانە لەم بۆکسەش بدە بۆ شاردنەوەی پەڕەی لێدوانەکانیان",
        "blanknamespace": "(سەرەکی)",
        "contributions": "بەشدارییەکانی {{GENDER:$1|بەکارھێنەر}}",
        "contributions-title": "بەشدارییەکانی بەکارھێنەر $1",
index 455195f..0ddeac0 100644 (file)
@@ -47,7 +47,7 @@
        "tog-enotifminoredits": "Gyrru e-bost ataf hefyd ar gyfer golygiadau bychain i dudalennau a ffeiliau",
        "tog-enotifrevealaddr": "Datguddio fy nghyfeiriad e-bost mewn e-byst hysbysu",
        "tog-shownumberswatching": "Dangos y nifer o ddefnyddwyr sy'n gwylio",
-       "tog-oldsig": "Y llofnod cyfredol:",
+       "tog-oldsig": "Eich llofnod cyfredol:",
        "tog-fancysig": "Trin y llofnod fel testun wici (heb gyswllt wici awtomatig)",
        "tog-uselivepreview": "Defnyddio rhagolwg byw",
        "tog-forceeditsummary": "Tynnu fy sylw pan adawaf flwch crynodeb golygu yn wag",
@@ -64,7 +64,7 @@
        "tog-showhiddencats": "Dangos categorïau cuddiedig",
        "tog-norollbackdiff": "Hepgor dangos cymhariaeth ar ôl gwrthdroi golygiad",
        "tog-useeditwarning": "Tynnu fy sylw pan wyf ar fin gadael tudalen olygu heb roi'r newidiadau ar gadw",
-       "tog-prefershttps": "Defnyddio cysylltiad diogel bob amser tra fy mod wedi mewngofnodi",
+       "tog-prefershttps": "Defnyddio cysylltiad diogel bob amser tra'ch bod wedi mewngofnodi",
        "underline-always": "Bob amser",
        "underline-never": "Byth",
        "underline-default": "Rhagosodyn y porwr neu'r wedd",
        "newwindow": "(yn agor mewn ffenest newydd)",
        "cancel": "Diddymu",
        "moredotdotdot": "Rhagor...",
-       "morenotlisted": "Nid yw'r rhestr hon yn llawn.",
+       "morenotlisted": "Efallai nad yw'r rhestr hon yn gyflawn.",
        "mypage": "Tudalen defnyddiwr",
        "mytalk": "Sgwrs",
        "anontalk": "Sgwrs",
        "searcharticle": "Mynd",
        "history": "Hanes y dudalen",
        "history_short": "Hanes",
+       "history_small": "hanes",
        "updatedmarker": "diwygiwyd ers i mi ymweld ddiwethaf",
        "printableversion": "Fersiwn argraffu",
        "permalink": "Dolen barhaol",
        "talk": "Sgwrs",
        "views": "Golygon",
        "toolbox": "Offer",
+       "tool-link-userrights": "Newid {{GENDER:$1|grwpiau defnyddwyr}}",
+       "tool-link-userrights-readonly": "Cip ar {{GENDER:$1|grwpiau defnyddwyr}}",
+       "tool-link-emailuser": "Ebostiwch y {{GENDER:$1|defnyddiwr}}",
        "userpage": "Gweld tudalen y defnyddiwr",
        "projectpage": "Gweld tudalen y wici",
        "imagepage": "Gweld tudalen y ffeil",
        "pool-timeout": "Cafwyd goroedi wrth aros am y clo",
        "pool-queuefull": "Mae cwt y gronfa brosesu yn llawn",
        "pool-errorunknown": "Gwall anhysbys",
+       "pool-servererror": "Nid yw'r gwasanaeth ''pool counter'' ar gael ($1).",
        "poolcounter-usage-error": "Gwall defnydd: $1",
        "aboutsite": "Ynglŷn â {{SITENAME}}",
        "aboutpage": "Project:Amdanom",
        "virus-scanfailed": "methodd y sgan (côd $1)",
        "virus-unknownscanner": "gwrthfirysydd anhysbys:",
        "logouttext": "'''Rydych wedi allgofnodi.'''\n\nSylwer y bydd rhai tudalennau yn parhau i ymddangos fel ag yr oeddent pan oeddech wedi mewngofnodi hyd nes i chi glirio celc eich porwr.",
+       "cannotlogoutnow-title": "Ni ellir allgofnodi ar hyn o bryd",
+       "cannotlogoutnow-text": "Ni ellir allgofnodi tra'n defnyddio $1.",
        "welcomeuser": "Croeso, $1!",
        "welcomecreation-msg": "Peidiwch ag anghofio newid eich [[Special:Preferences|{{SITENAME}} preferences]].",
        "yourname": "Eich enw defnyddiwr:",
        "createacct-yourpasswordagain-ph": "Rhowch eich cyfrinair eto",
        "userlogin-remembermypassword": "Cadw'r sesiwn yn fyw tan i mi allgofnodi",
        "userlogin-signwithsecure": "Defnyddio cysylltiad diogel",
+       "cannotlogin-title": "Methwyd mewngofnodi",
+       "cannotlogin-text": "Mae mewngofnodi'n amhosibl.",
+       "cannotloginnow-title": "Ni ellir mewngofnodi ar hyn o bryd.",
+       "cannotloginnow-text": "Ni ellir mewngofnodi tra defnyddir $1.",
        "cannotcreateaccount-title": "Yn methu creu cyfrif",
+       "cannotcreateaccount-text": "Ni ellir creu cyfrif ar y wici hon.",
        "yourdomainname": "Eich parth:",
        "password-change-forbidden": "Ni allwch newid cyfrineiriau ar y wici hwn.",
        "externaldberror": "Naill ai: cafwyd gwall dilysu allanol ar databas neu: ar y llaw arall efallai nad oes hawl gennych chi i ddiwygio'ch cyfrif allanol.",
        "login": "Mewngofnodi",
+       "login-security": "Gwirwch eich ID.",
        "nav-login-createaccount": "Mewngofnodi",
-       "userlogin": "Mewngofnodi / creu cyfrif",
-       "userloginnocreate": "Mewngofnodi",
        "logout": "Allgofnoder",
        "userlogout": "Allgofnodi",
        "notloggedin": "Nid ydych wedi mewngofnodi",
        "userlogin-noaccount": "Dim cyfrif gennych?",
        "userlogin-joinproject": "Ymuno â {{SITENAME}}",
-       "nologin": "Dim cyfrif gennych? $1.",
-       "nologinlink": "Crëwch gyfrif",
        "createaccount": "Creu cyfrif newydd",
-       "gotaccount": "Oes cyfrif gennych eisoes? $1.",
-       "gotaccountlink": "Mewngofnodi",
-       "userlogin-resetlink": "Ydych chi wedi anghofio eich manylion mewngofnodi?",
        "userlogin-resetpassword-link": "Wedi anghofio eich cyfrinair?",
        "userlogin-helplink2": "Cymorth wrth fewngofnodi",
        "userlogin-loggedin": "Rydych eisoes wedi mewngofnodi wrth yr enw {{GENDER:$1|$1}}.\nDefnyddiwch y ffurflen isod i fewngofnodi wrth rhyw enw arall.",
+       "userlogin-reauth": "Mae'n rhaid i chi fewngofnodi eto er mwyn gwiro eich bod yn {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Creu cyfrif ychwanegol",
        "createacct-emailrequired": "Cyfeiriad ebost",
        "createacct-emailoptional": "Cyfeiriad ebost (dewisol)",
        "createacct-another-email-ph": "Rhowch y cyfeiriad e-bost",
        "createaccountmail": "Defnyddio cyfrinair ar hap dros dro a'i anfon i'r cyfeiriad e-bost a roddwyd",
        "createacct-realname": "Enw cywir (dewisiol)",
-       "createaccountreason": "Rheswm:",
        "createacct-reason": "Rheswm",
        "createacct-reason-ph": "Pam ydych yn creu cyfrif arall?",
+       "createacct-reason-help": "Dangosir y neges yn y log creu cyfri.",
        "createacct-submit": "Creer y cyfrif",
        "createacct-another-submit": "Creu cyfrif",
        "createacct-continue-submit": "Parhau i greu cyfrif",
+       "createacct-another-continue-submit": "Parhau i greu cyfrif",
        "createacct-benefit-heading": "Ffrwyth llafur pobl fel chi yw {{SITENAME}}.",
        "createacct-benefit-body1": "{{PLURAL:$1|golygiad|golygiad|olygiad|golygiad}}",
        "createacct-benefit-body2": "{{PLURAL:$1|tudalen|dudalen|dudalen|tudalen|thudalen|tudalen}}",
        "createacct-benefit-body3": "{{PLURAL:$1|cyfranwyr yn|wedi cyfrannu'n}} ddiweddar",
        "badretype": "Nid yw'r cyfrineiriau'n union yr un fath.",
+       "usernameinprogress": "Mae creu cyfrif i'r enw-defnyddiwr hwn wrthi'n cael ei brosesu. Daliwch eich gafael!",
        "userexists": "Mae rhywun arall wedi dewis yr enw defnyddiwr hwn. \nDewiswch un arall os gwelwch yn dda.",
        "loginerror": "Problem mewngofnodi",
        "createacct-error": "Nam wrth greu cyfrif",
        "nocookiesnew": "Mae'r cyfrif defnyddiwr wedi cael ei greu, ond nid ydych wedi mewngofnodi. Mae {{SITENAME}} yn defnyddio cwcis wrth i ddefnyddwyr fewngofnodi. Rydych chi wedi analluogi cwcis. Mewngofnodwch eto gyda'ch enw defnyddiwr a'ch cyfrinair newydd os gwelwch yn dda, ar ôl galluogi cwcis.",
        "nocookieslogin": "Mae {{SITENAME}} yn defnyddio cwcis wrth i ddefnyddwyr fewngofnodi. Rydych chi wedi analluogi cwcis. Trïwch eto os gwelwch yn dda, ar ôl galluogi cwcis.",
        "nocookiesfornew": "Ni chrëwyd cyfrif defnyddiwr newydd, oherwydd na allem gadarnhau ei ffynhonnell.\nSicrhewch eich bod wedi galluogi cwcis, yna ail-lwythwch y dudalen hon a cheisiwch eto.",
+       "createacct-loginerror": "Crewyd y cyfrif yn llwyddiannus, ond nid oedd yn bosibl i chi fewngofnodi'n otomatig. Ewch i [[Special:UserLogin|fewngofnodi drwy law a llygad]].",
        "noname": "Dydych chi ddim wedi cynnig enw defnyddiwr dilys.",
        "loginsuccesstitle": "Llwyddodd y mewngofnodi",
        "loginsuccess": "'''Yr ydych wedi mewngofnodi i {{SITENAME}} fel \"$1\".'''",
        "wrongpassword": "Nid yw'r cyfrinair a deipiwyd yn gywir. Rhowch gynnig arall arni, os gwelwch yn dda.",
        "wrongpasswordempty": "Roedd y cyfrinair yn wag. Rhowch gynnig arall arni.",
        "passwordtooshort": "Mae'n rhaid fod gan gyfrinair o leia $1 {{PLURAL:$1|nod}}.",
+       "passwordtoolong": "Ni chaiff cyfrinair fod yn hirach na {{PLURAL:$1|1 llythyren|$1 llythyren}}.",
+       "passwordtoopopular": "Chewch chi ddim defnyddio cyfreinair rhy syml, rhy gyffredin. Dewisiwch un unigryw!",
        "password-name-match": "Rhaid i'ch cyfrinair a'ch enw defnyddiwr fod yn wahanol i'w gilydd.",
        "password-login-forbidden": "Gwaharddwyd defnyddio'r enw defnyddiwr a'r cyfrinair hwn.",
        "mailmypassword": "Ailosoder y cyfrinair",
        "createacct-another-realname-tip": "Gallwch ddewis roi eich enw go iawn.\nOs y gwnewch, fe gaiff yr enw go iawn ei defnyddio wrth dadogi'ch gwaith.",
        "pt-login": "Mewngofnodi",
        "pt-login-button": "Mewngofnoder",
+       "pt-login-continue-button": "Parhau i fewngofnodi",
        "pt-createaccount": "Crëwch gyfrif",
        "pt-userlogout": "Allgofnodi",
        "php-mail-error-unknown": "Gwall anhysbys yng ngweithrediad post() PHP",
        "resetpass_submit": "Gosod y cyfrinair a mewngofnodi",
        "changepassword-success": "Newidiwyd eich cyfrinair!",
        "changepassword-throttled": "Rydych wedi ceisio logio mewn yn rhy aml.\nArhoswch am $1 cyn trio eto.",
+       "botpasswords": "Cyfrineiriau bots",
+       "botpasswords-label-appid": "Enw bot:",
        "botpasswords-label-create": "Dechrau",
        "botpasswords-label-update": "Diweddaru",
        "botpasswords-label-cancel": "Canslo",
        "passwordreset-emailtext-ip": "Mae rhywun (chi yn ôl pob tebyg, o'r cyfeiriad IP $1) wedi gofyn am ailosod eich cyfrinair ar {{SITENAME}} ($4). Mae'r {{PLURAL:$3|cyfrif|cyfrif|cyfrifon}} canlynol ynghlwm wrth y cyfeiriad e-bost hwn:\n\n$2\n\nBydd y {{PLURAL:$3||cyfrinair dros dro hwn|cyfrineiriau dros dro hyn}} yn dod i ben ymhen {{PLURAL:$5||diwrnod |deuddydd|tridiau|$5 diwrnod}}. Dylech fewngofnodi nawr a dewis cyfrinair newydd. Os mai rhywun arall a ofynodd am ailosod y cyfrinair, neu os ydych wedi cofio eich cyfrinair gwreiddiol, neu os nad ydych am ei newid bellach, gallwch anwybyddu'r neges hon a pharhau i ddefnyddio eich hen gyfrinair.",
        "passwordreset-emailtext-user": "Gofynodd y defnyddiwr $1 ar {{SITENAME}} am gael ailosod ei gyfrinair ar {{SITENAME}}\n($4). Mae'r {{PLURAL:$3||cyfrif|cyfrifon}} canlynol ynghlwm wrth y cyfeiriad e-bost hwn:\n\n$2\n\nBydd y {{PLURAL:$3||cyfrinair|cyfrineiriau}} dros dro hyn yn dod i ben ymhen {{PLURAL:$5||diwrnod|deuddydd|tridiau|$5 diwrnod}}.\nDylech fewngofnodi nawr a dewis cyfrinair newydd. Os mai rhywun arall a ofynodd am ailosod y cyfrinair, neu os ydych wedi cofio eich cyfrinair gwreiddiol, neu os nad ydych am ei newid bellach, gallwch anwybyddu'r neges hon a pharhau i ddefnyddio eich hen gyfrinair.",
        "passwordreset-emailelement": "Enw'r defnyddiwr: \n$1\n\nY cyfrinair dros dro: \n$2",
-       "passwordreset-emailsentemail": "Anfonwyd e-bost i ailosod eich cyfrinair atoch.",
+       "passwordreset-emailsentemail": "Os yw'r ebost hwn ynghlwm wrth eich cyfrif, yna anfonir e-bost i ailosod eich cyfrinair atoch.",
+       "passwordreset-emailsentusername": "Os oes ebost ynghlwm wrth eich cyfrif, yna anfonir e-bost i ailosod eich cyfrinair atoch.",
+       "passwordreset-nocaller": "Rhaid darparu'r wybodaeth wreiddiol",
+       "passwordreset-nosuchcaller": "Nid yw'r ffynhonnell yn bodoli: $1",
        "passwordreset-ignored": "Ailosod y cyfrinair nad ymdriniwyd â. Efallai y nid y darparwr yn osod?",
        "passwordreset-invalidemail": "Cyfeiriad e-bost annilys",
+       "passwordreset-nodata": "Ni wnaethoch ddarparu ebost na chyfeiriad",
        "changeemail": "Newid y cyfeiriad e-bost",
        "changeemail-header": "Cwbwlhewch y ffurflen hon i newid cyfeiriad e-bost y cyfrifi. I ddileu pob cysylltiad i bob cyfeiriad ebost, gadewch e'n wag.",
        "changeemail-no-info": "Ni allwch fynd at y dudalen hon heblaw eich bod wedi mewngofnodi.",
        "changeemail-password": "Eich cyfrinair ar {{SITENAME}}:",
        "changeemail-submit": "Newidier y cyfeiriad e-bost",
        "changeemail-throttled": "'Da chi wedi trio mewngofnodi gormod o weithiau.\nDaliwch eich gafael am $1 cyn ceisio eto.",
+       "changeemail-nochange": "Darparwch cyfeiriad ebost newydd a gwahanol.",
        "resettokens": "Ailosod tocynnau",
        "resettokens-text": "Gallwch ailosod tocynnau sy'n galluogi mynediad at rai manylion preifat sydd ynghlwm wrth y cyfrif yma.\n\nDylech wneud hyn os wnaethoch rannu'r manylion hyn gyda rhywun ar ddamwain neu os oes problem wedi codi gyda diogelwch eich cyfrif.",
        "resettokens-no-tokens": "Nid oes unrhyw docynnau i'w hailosod.",
        "missingcommentheader": "<strong>Nodyn atgoffa:</strong> \nNid ydych wedi cynnig unrhywbeth yn y blwch 'Pwnc:'. Os y cliciwch \"{{int:savearticle}}\" eto fe gedwir y golygiad heb bennawd.",
        "summary-preview": "Rhagolwg o'r crynodeb:",
        "subject-preview": "Rhagolwg pwnc:",
+       "previewerrortext": "Cafwyd nam tra'n ceisio creu rhagolwg o'r newidiadau.",
        "blockedtitle": "Mae'r defnyddiwr hwn wedi cael ei flocio",
        "blockedtext": "'''Mae eich enw defnyddiwr neu gyfeiriad IP wedi cael ei flocio.'''\n\n$1 a osododd y bloc.\nY rheswm a roddwyd dros y blocio yw: ''$2''.\n\n*Dechreuodd y bloc am: $8\n*Bydd y bloc yn dod i ben am: $6\n*Bwriadwyd blocio: $7\n\nGallwch gysylltu â $1 neu un arall o'r [[{{MediaWiki:Grouppage-sysop}}|gweinyddwyr]] i drafod y bloc.\nSylwch mai dim ond y rhai sydd wedi gosod cyfeiriad e-bost yn eu [[Special:Preferences|dewisiadau defnyddiwr]], a hwnnw heb ei flocio, sydd yn gallu 'anfon e-bost at ddefnyddiwr' trwy'r wici.\n$3 yw eich cyfeiriad IP presennol. Cyfeirnod y bloc yw #$5.\nPan yn ysgrifennu at weinyddwr, cofiwch gynnwys yr holl fanylion uchod, os gwelwch yn dda.",
        "autoblockedtext": "Rhoddwyd bloc yn awtomatig ar eich cyfeiriad IP oherwydd iddo gael ei ddefnyddio gan ddefnyddiwr arall, a bod bloc wedi ei roi ar hwnnw gan $1.\nY rheswm a roddwyd dros y bloc oedd:\n\n:''$2''\n\n*Dechreuodd y bloc am: $8\n*Daw'r bloc i ben am: $6\n*Bwriadwyd blocio: $7\n\nGallwch gysylltu â $1 neu un arall o'r [[{{MediaWiki:Grouppage-sysop}}|gweinyddwyr]] i drafod y bloc.\n\nSylwch mai dim ond y rhai sydd wedi gosod cyfeiriad e-bost yn eu [[Special:Preferences|dewisiadau defnyddiwr]], a hwnnw heb ei flocio, sydd yn gallu 'anfon e-bost at ddefnyddiwr' trwy'r wici.\n\nEich cyfeiriad IP presennol yw $3. Cyfeirnod y bloc yw $5. Nodwch y manylion hyn wrth drafod y bloc.",
        "userpage-userdoesnotexist": "Nid oes defnyddiwr a'r enw \"<nowiki>$1</nowiki>\" yn bod. Gwnewch yn siwr eich bod am greu/golygu'r dudalen hon.",
        "userpage-userdoesnotexist-view": "Nid yw'r cyfrif defnyddiwr \"$1\" wedi ei gofrestri.",
        "blocked-notice-logextract": "Mae'r defnyddiwr hwn wedi ei flocio ar hyn o bryd.\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
-       "clearyourcache": "'''Sylwer - Wedi i chi roi'r dudalen ar gadw, efallai y bydd angen mynd heibio celc eich porwr er mwyn gweld y newidiadau.'''\n*'''Firefox / Safari:''' Pwyswch ar ''Shift'' tra'n clicio ''Ail-lwytho/Reload'', neu gwasgwch ''Ctrl-F5'' neu ''Ctrl-R'' (''⌘-R'' ar Mac);\n*'''Google Chrome:'''Pwyswch ar Ctrl-Shift-R'' (''⌘-Shift-R'' ar Mac)\n*'''Internet Explorer:''' Pwyswch ar ''Ctrl'' tra'n clicio ''Adnewyddu/Refresh'', neu gwasgwch ''Ctrl-F5''. \n*'''Opera:''' gwacewch y celc yn llwyr trwy ''Offer → Dewisiadau / Tools→Preferences'';",
+       "clearyourcache": "'''Sylwer - Wedi i chi roi'r dudalen ar gadw, efallai y bydd angen mynd heibio celc eich porwr er mwyn gweld y newidiadau.'''\n*'''Firefox / Safari:''' Pwyswch ''Shift'' tra'n clicio ''Ail-lwytho/Reload'', neu bwyswch ''Ctrl-F5'' neu ''Ctrl-R'' (''⌘-R'' ar Mac);\n*'''Google Chrome:'''Pwyswch ar Ctrl-Shift-R'' (''⌘-Shift-R'' ar Mac)\n*'''Internet Explorer:''' Pwyswch ''Ctrl'' tra'n clicio ''Adnewyddu/Refresh'', neu bwyswch ''Ctrl-F5''. \n*'''Opera:''' gwagiwch y celc yn llwyr trwy ''Offer → Dewisiadau / Tools→Preferences'';",
        "usercssyoucanpreview": "'''Tip:''' Defnyddiwch y botwm \"{{int:showpreview}}\" er mwyn profi eich CSS newydd cyn ei gadw.",
        "userjsyoucanpreview": "'''Tip:''' Defnyddiwch y botwm \"{{int:showpreview}}\" er mwyn profi eich JS newydd cyn ei gadw.",
        "usercsspreview": "'''Cofiwch - dim ond rhagolwg o'ch CSS defnyddiwr yw hwn.'''\n'''Nid yw wedi'i gadw eto!'''",
        "yourdiff": "Gwahaniaethau",
        "copyrightwarning": "Mae pob cyfraniad i {{SITENAME}} yn cael ei ryddhau o dan termau'r Drwydded Ddogfen Rhydd ($2) (gwelwch $1 am fanylion). Os nad ydych chi'n fodlon i'ch gwaith gael ei olygu heb drugaredd, neu i gopïau ymddangos ar draws y we, peidiwch a'i gyfrannu yma.<br />\nRydych chi'n cadarnhau mai chi yw awdur y cyfraniad, neu eich bod chi wedi'i gopïo o'r parth cyhoeddus (''public domain'') neu rywle rhydd tebyg. '''Nid''' yw'r mwyafrif o wefannau yn y parth cyhoeddus.\n\n'''PEIDIWCH Â CHYFRANNU GWAITH O DAN HAWLFRAINT HEB GANIATÂD!'''",
        "copyrightwarning2": "Sylwch fod pob cyfraniad i {{SITENAME}} yn cael ei ryddhau o dan termau'r Drwydded Ddogfen Rhydd (gwelwch $1 am fanylion).\nOs nad ydych chi'n fodlon i'ch gwaith gael ei olygu heb drugaredd, neu i gopïau ymddangos ar draws y we, peidiwch a'i gyfrannu yma.<br />\nRydych chi'n cadarnhau mai chi yw awdur y cyfraniad, neu eich bod chi wedi'i gopïo o'r parth cyhoeddus (''public domain'') neu rywle rhydd tebyg.<br />\n'''PEIDIWCH Â CHYFRANNU GWAITH O DAN HAWLFRAINT HEB GANIATÂD!'''",
+       "editpage-cannot-use-custom-model": "Ni ellir newid cynnwys y ddalen.",
        "longpageerror": "'''GWALL: Mae'r testun yr ydych wedi ei osod yma yn {{PLURAL:$1|$1 cilobeit}} o hyd, ac yn hwy na'r hyd eithaf o {{PLURAL:$2|$2}} cilobeit.\nNi ellir ei roi ar gadw.'''",
-       "readonlywarning": "'''RHYBUDD: Mae'r gronfa ddata wedi'i chloi am gyfnod er mwyn cynnal a chadw, felly fyddwch chi ddim yn gallu cadw'ch golygiadau ar hyn o bryd.''' \nGallwch gopïo'r testun a'i gludo i ffeil destun er mwyn ei gadw tan yn hwyrach.\n\nCynigiodd y gweinyddwr a glodd y gronfa ddata y rheswm hwn dros ei chloi: $1",
+       "readonlywarning": "<strong>Rhybudd: Mae'r gronfa ddata wedi'i chloi am gyfnod er mwyn cynnal a chadw, felly fyddwch chi ddim yn gallu cadw'ch golygiadau ar hyn o bryd.</strong>\nGallwch gopïo'r testun a'i gludo i ffeil destun er mwyn ei gadw tan yn hwyrach.\n\nCynigiodd y gweinyddwr a glodd y gronfa ddata y rheswm hwn dros ei chloi: $1",
        "protectedpagewarning": "'''RHYBUDD: Mae'r dudalen hon wedi'i diogelu. Dim ond gweinyddwyr sydd yn gallu ei golygu.'''\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
        "semiprotectedpagewarning": "'''Sylwer:''' Mae'r dudalen hon wedi ei chloi; dim ond defnyddwyr cofrestredig a allant ei golygu.\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
-       "cascadeprotectedwarning": "'''Dalier sylw:''' Mae'r dudalen hon wedi ei diogelu fel nad ond defnyddwyr â galluoedd gweinyddwyr sy'n gallu ei newid, oherwydd ei bod yn rhan o'r {{PLURAL:$1|dudalen ganlynol|dudalen ganlynol|tudalennau canlynol}} sydd wedi {{PLURAL:$1|ei sgydol-ddiogelu|ei sgydol-ddiogelu|eu sgydol-diogelu}}.",
+       "cascadeprotectedwarning": "<strong>Dalier sylw:</strong> Mae'r dudalen hon wedi ei chloi fel mai dim ond defnyddwyr â galluoedd 'Gweinyddwyr' all ei newid, oherwydd ei bod yn rhan o'r {{PLURAL:$1|dudalen ganlynol|dudalen ganlynol|tudalennau canlynol}} sydd wedi {{PLURAL:$1|ei sgydol-ddiogelu|ei diogelu|eu diogelu}}.",
        "titleprotectedwarning": "'''RHYBUDD:  Mae'r dudalen hon wedi ei chloi; dim ond rhai defnyddwyr sydd â'r [[Special:ListGroupRights|gallu]] i'w chreu.'''\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
        "templatesused": "Defnyddir y {{PLURAL:$1|nodyn hwn|nodyn hwn|nodiadau hyn|nodiadau hyn|nodiadau hyn|nodiadau hyn}} yn y dudalen hon:",
        "templatesusedpreview": "Defnyddir y {{PLURAL:$1|nodyn hwn|nodyn hwn|nodiadau hyn|nodiadau hyn|nodiadau hyn|nodiadau hyn}} yn y rhagolwg hwn:",
        "permissionserrorstext-withaction": "Nid yw'r gallu hwn ($2) ganddoch, am y {{PLURAL:$1|rheswm|rheswm|rhesymau|rhesymau|rhesymau|rhesymau}} canlynol:",
        "recreate-moveddeleted-warn": "'''Dalier sylw: Rydych yn ail-greu tudalen a ddilewyd rhywdro.'''\n\nYstyriwch a fyddai'n dda o beth i barhau i olygu'r dudalen hon.\nDyma'r logiau dileu a symud ar gyfer y dudalen, er gwybodaeth:",
        "moveddeleted-notice": "Dilëwyd y dudalen hon.\nDangosir y logiau dileu a symud ar gyfer y dudalen isod.",
+       "moveddeleted-notice-recent": "Ymddiheurwn! Dilewyd y ddalen hon yn ddiweddar (yn y 24 awr diwethaf).\nEr gwybodaeth, darperir isod yr holl wybodaeth berthnasol.",
        "log-fulllog": "Gweld y lòg cyflawn",
        "edit-hook-aborted": "Terfynwyd y golygiad cyn pryd gan fachyn.\nNi roddodd eglurhad.",
        "edit-gone-missing": "Ni ellid diweddaru'r dudalen.\nYmddengys iddi gael ei dileu.",
        "invalid-content-data": "Data annilys i'r cynnwys",
        "content-not-allowed-here": "Nid yw cynnwys ar ffurf \"$1\" yn cael ei ganiatau ar y dudalen [[$2]]",
        "editwarning-warning": "Os y gadewch y dudalen hon mae'n bosib y collwch eich newidiadau iddi.\nOs ydych wedi mewngofnodi gallwch ddiddymu'r rhybudd hwn yn yr adran \"{{int:prefs-editing}}\" yn eich dewisiadau.",
+       "editpage-invalidcontentmodel-title": "Nid yw'r model yma ar gael.",
+       "editpage-invalidcontentmodel-text": "Nid yw'r model \"$1\" ar gael.",
        "editpage-notsupportedcontentformat-title": "Dydy fformat y cynnwys hwn ddim yn cael ei gefnogi gennym.",
        "editpage-notsupportedcontentformat-text": "Dydy'r fformat $1 ar y cynnwys ddim yn cael ei gefnogi gan y model $2.",
        "content-model-wikitext": "cystrawen wici",
        "content-model-css": "CSS",
        "content-json-empty-object": "Dim gwrthrych",
        "content-json-empty-array": "Rhesi gwag",
+       "duplicate-args-warning": "<strong>Rhybudd:</strong> Mae [[:$1]] yn galw [[:$2]] gyda mwy nag un gwerthrif (''value'') i baramedr \"$3\". Dim ond y gwerthrif diwethaf gaiff ei ddefnyddio.",
        "duplicate-args-category": "Tudalennau gyda meysydd deublyg yn y Nodion",
        "duplicate-args-category-desc": "Mae'r dudalen hon yn cynnwys meysydd yn y Nodion, ddwy waith e.e.  <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> neu <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "'''Rhybudd:''' Mae gormod o alwadau ar ffwythiannau dosrannu sy'n dreth ar adnoddau yn y dudalen hon.\n\nDylai fod llai na $2 {{PLURAL:$2|galwad|alwad|alwad|galwad}} yn y dudalen, ond ar hyn o bryd mae $1 {{PLURAL:$1|galwad|alwad|alwad|galwad}} ynddi.",
        "history-feed-description": "Hanes diwygio'r dudalen hon ar y wici",
        "history-feed-item-nocomment": "$1 am $2",
        "history-feed-empty": "Nid yw'r dudalen a ofynwyd amdani'n bod.\nGall fod iddi gael ei dileu neu ei hailenwi.\nGallwch [[Special:Search|chwilio'r]] wici am dudalennau eraill perthnasol.",
+       "history-edit-tags": "Tagiau golygu y fersiynau a ddewisiwyd",
        "rev-deleted-comment": "(dilëwyd crynodeb y golygiad)",
        "rev-deleted-user": "(enw defnyddiwr wedi ei ddiddymu)",
        "rev-deleted-event": "(tynnwyd manylion y lòg)",
        "rev-showdeleted": "dangos",
        "revisiondelete": "Dileu/dad-ddileu diwygiadau",
        "revdelete-nooldid-title": "Anelwyd at olygiad annilys",
-       "revdelete-nooldid-text": "Naill ai; nid ydych wedi dynodi diwygiad yn darged y weithred, neu nid yw'r diwygiad penodedig yn bod, neu rydych wedi ceisio cuddio'r diwygiad presennol.",
+       "revdelete-nooldid-text": "Naill ai nid ydych wedi dynodi diwygiad yn darged y weithred, neu nid yw'r diwygiad penodedig yn bod, neu rydych wedi ceisio cuddio'r diwygiad presennol.",
        "revdelete-no-file": "Nid yw'r ffeil a nodwyd yn bod.",
        "revdelete-show-file-confirm": "Ydych chi'n sicr eich bod am weld y diwygiad dilëedig o'r ffeil \"<nowiki>$1</nowiki>\" a roddwyd ar gadw am $3 ar $2?",
        "revdelete-show-file-submit": "Ydw",
        "revdelete-unsuppress": "Tynnu'r cyfyngiadau ar y golygiadau a adferwyd",
        "revdelete-log": "Rheswm:",
        "revdelete-submit": "Rhoi ar waith ar y {{PLURAL:$1|golygiad|golygiad|golygiadau|golygiadau|golygiadau|golygiadau}} dewisedig",
-       "revdelete-success": "'''Diweddarwyd y gallu i weld golygiadau.'''",
+       "revdelete-success": "Diweddarwyd y golygiad.",
        "revdelete-failure": "'''Ni ellid newid y cyfyngiadau ar y gallu i weld y golygiad:'''\n$1",
-       "logdelete-success": "'''Llwyddwyd i guddio neu i ddatguddio'r digwyddiad rhag y lòg.'''",
+       "logdelete-success": "Cuddiwyd/datguddiwyd y digwyddiad rhag y lòg.",
        "logdelete-failure": "'''Ni ellid gosod cyfyngiadau ar y gallu i weld y cofnod lòg:'''\n$1",
        "revdel-restore": "Newid gwelededd",
        "pagehist": "Hanes y dudalen",
        "mergehistory-go": "Dangos y golygiadau y gellir eu cyfuno",
        "mergehistory-submit": "Cyfuner y diwygiadau",
        "mergehistory-empty": "Ni ellir cyfuno unrhyw ddiwygiadau.",
-       "mergehistory-done": "Cyfunwyd $3 {{PLURAL:$3|diwygiad|diwygiad|ddiwygiad|diwygiad|diwygiad|diwygiad}} o $1 yn llwyddiannus i'r dudalen [[:$2]].",
+       "mergehistory-done": "Cyfunwyd $3 {{PLURAL:$3|diwygiad}} o $1 a [[:$2]].",
        "mergehistory-fail": "Methodd y cyfuno hanes; a wnewch wirio paramedrau'r dudalen a'r amser unwaith eto.",
+       "mergehistory-fail-bad-timestamp": "Mae'r stamp-amser yn annilys.",
+       "mergehistory-fail-invalid-source": "Mae'r ddalen wreiddiol (y ''source page'') yn annilys.",
+       "mergehistory-fail-invalid-dest": "Mae'r ddalen-darged (y ''destination page'') yn annilys.",
+       "mergehistory-fail-no-change": "Ni chyfunwyd hanes yr adolygiadau. Gwirwch y ddalen a pharamedrau amser, os yw'n bodoli.",
+       "mergehistory-fail-permission": "Nid oes gennych yr hawliau digonol i gyfuno yr hanes.",
+       "mergehistory-fail-self-merge": "Mae'r dalennau gwreiddiol a tharged yr un! (''Source and destination pages are the same.'')",
        "mergehistory-no-source": "Nid yw'r dudalen gwreiddiol $1 yn bod.",
        "mergehistory-no-destination": "Nid yw'r dudalen cyrchfan $1 yn bod.",
        "mergehistory-invalid-source": "Rhaid bod teitl dilys gan y dudalen gwreiddiol.",
        "searchprofile-advanced-tooltip": "Chwilio drwy parthau dewisol",
        "search-result-size": "$1 ({{PLURAL:$2|dim geiriau|$2 gair|$2 air|$2 gair|$2 gair|$2 gair|}})",
        "search-result-category-size": "{{PLURAL:$1|$1 aelod}} ({{PLURAL:$2|$2 is-gategori}}, {{PLURAL:$3|$3 ffeil}})",
-       "search-redirect": "(ailgyfeiriad $1)",
+       "search-redirect": "(ailgyfeiriad $1)",
        "search-section": "(adran $1)",
        "search-category": "(categori $1)",
        "search-file-match": "(yn cyfateb i gynnwys y ffeil)",
        "search-suggest": "Ai am hyn y chwiliwch: $1",
+       "search-rewritten": "Dangosir canlyniadauar gyfer $1. Chwiliwch am $2.",
        "search-interwiki-caption": "Chwaer-brosiectau",
        "search-interwiki-default": "Y canlyniadau o $1:",
        "search-interwiki-more": "(rhagor)",
+       "search-interwiki-more-results": "rhagor o ganlyniadau",
        "search-relatedarticle": "Erthyglau eraill tebyg",
        "searchrelated": "erthyglau eraill tebyg",
        "searchall": "oll",
        "showingresultsinrange": "Yn dangos hyd at {{PLURAL:$1||<strong>1</strong> canlyniad|<strong>$1</strong> ganlyniad|$1 o ganlyniadau}} isod yn yr ystod #<strong>$2</strong> i #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Result <strong>$1</strong> of <strong>$3</strong>|Canlyniadau: <strong>$1 - $2</strong> o <strong>$3</strong>}}",
        "search-nonefound": "Ni chafwyd dim canlyniadau i'r ymholiad.",
+       "search-nonefound-thiswiki": "Doedd dim un canlyniad er chwilio'r wefan gyfan.",
        "powersearch-legend": "Chwiliad uwch",
        "powersearch-ns": "Chwilio yn y parthau:",
        "powersearch-togglelabel": "Dewis:",
        "search-external": "Chwiliad allanol",
        "searchdisabled": "Mae'r teclyn chwilio ar {{SITENAME}} wedi'i analluogi dros dro.\nYn y cyfamser gallwch chwilio drwy Google.\nCofiwch y gall mynegeion Google o gynnwys {{SITENAME}} fod ar ei hôl hi.",
        "search-error": "Cafwyd gwall wrth chwilio: $1",
+       "search-warning": "Cafwyd rhybudd tra'n chwilio: $1",
        "preferences": "Dewisiadau",
        "mypreferences": "Dewisiadau",
        "prefs-edits": "Nifer y golygiadau:",
        "prefs-personal": "Data personol",
        "prefs-rc": "Newidiadau diweddar",
        "prefs-watchlist": "Rhestr wylio",
+       "prefs-editwatchlist": "Golygu'r rhestr wylio",
+       "prefs-editwatchlist-label": "Golygiadau ar eich rhestr wylio:",
+       "prefs-editwatchlist-edit": "Gosod a dileu teitlau o'ch rhestr wylio",
+       "prefs-editwatchlist-raw": "Golygu cod eich rhestr wylio",
+       "prefs-editwatchlist-clear": "Clirio eich rhestr wylio",
        "prefs-watchlist-days": "Nifer y diwrnodau i'w dangos yn y rhestr wylio:",
        "prefs-watchlist-days-max": "Hyd at $1 {{PLURAL:$1||diwrnod|ddiwrnod|diwrnod|diwrnod|diwrnod}}",
        "prefs-watchlist-edits": "Nifer y golygiadau i'w dangos wrth ehangu'r rhestr wylio:",
        "youremail": "Eich cyfeiriad e-bost",
        "username": "{{GENDER:$1|Enw defnyddiwr}}:",
        "prefs-memberingroups": "Yn {{GENDER:$2|aelod}} o'r {{PLURAL:$1|grŵp|grŵp|grwpiau}} canlynol:",
+       "group-membership-link-with-expiry": "$1 (gyd at $2)",
        "prefs-registration": "Amser dechrau'r cyfrif:",
        "yourrealname": "Eich enw cywir*",
        "yourlanguage": "Iaith y rhyngwyneb",
        "prefswarning-warning": "Rydych wedi newid eich Dewisiadau, ac nid ydynt eto wedi'u cadw.\nOs gadewch y dudalen hon heb glicio \"$1\" yna fe gollwch y newidiadau hyn.",
        "prefs-tabs-navigation-hint": "Awgrym: Gallwch ddefnyddio'r allweddellau sy'n saethau i'r chwith neu i'r dde i lywio rhwng y tabiau ar restr y tabiau.",
        "userrights": "Rheoli galluoedd defnyddwyr",
-       "userrights-lookup-user": "Rheoli grwpiau defnyddiwr",
+       "userrights-lookup-user": "Dewisiwch ddefnyddiwr",
        "userrights-user-editname": "Rhowch enw defnyddiwr:",
-       "editusergroup": "Golygu Grwpiau {{GENDER:$1|Defnyddwyr}}",
+       "editusergroup": "Golygu grwpiau defnyddwyr",
        "editinguser": "Newid galluoedd {{GENDER:$1|y defnyddiwr}} <strong>[[User:$1|$1]]</strong> $2",
-       "userrights-editusergroup": "Golygu grwpiau defnyddwyr",
+       "viewinguserrights": "Gweld galluoedd {{GENDER:$1|y defnyddiwr}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "Golygu grwpiau {{GENDER:$1|defnyddwyr}}",
+       "userrights-viewusergroup": "Gweld {{GENDER:$1|grwpiau defnyddwyr}}",
        "saveusergroups": "Cadw'r Grwpiau {{GENDER:$1|Defnyddwyr}}",
        "userrights-groupsmember": "Yn aelod o:",
        "userrights-groupsmember-auto": "Ac ynghlwm wrth aelodaeth y grwpiau uchod, yn aelod o:",
        "userrights-nodatabase": "Nid yw'r bas data $1 yn bod neu nid yw'n un lleol.",
        "userrights-changeable-col": "Grwpiau y gallwch eu newid",
        "userrights-unchangeable-col": "Grwpiau na allwch eu newid",
+       "userrights-expiry-current": "Daw i ben $1",
+       "userrights-expiry-none": "Diderfyn, o ran amser",
+       "userrights-expiry": "Daw i ben:",
+       "userrights-expiry-existing": "Amser dod i ben: $3, $2",
+       "userrights-expiry-othertime": "Cyfnod arall:",
+       "userrights-expiry-options": "1 dydd:1 day,1 wythnos:1 week,1 mis:1 month,3 mis:3 months,6 mis:6 months,1 blynedd:1 year",
        "userrights-conflict": "Gwrthdaro oherwydd newid i hawliau defnyddiwr! Adolygwch eich newidiadau a'u cadarnhau.",
        "group": "Grŵp:",
        "group-user": "Defnyddwyr",
        "right-siteadmin": "Cloi a datgloi'r databas",
        "right-override-export-depth": "Allforio tudalennau gan gynnwys tudalennau cysylltiedig hyd at ddyfnder o 5",
        "right-sendemail": "Anfon e-bost at ddefnyddwyr eraill",
-       "right-managechangetags": "Creu a dileu [[Special:Tags|tagiau]] o'r gronfa ddata",
+       "right-managechangetags": "Creu a dileu [[Special:Tags|tagiau]]",
+       "right-applychangetags": "Gosodwch y [[Special:Tags|tagiau]] gyda'r newidiadau",
+       "grant-generic": "Bwdel hawliau \"$1\"",
+       "grant-group-page-interaction": "Rhyngweithiwch gyda'r dalennau",
+       "grant-group-watchlist-interaction": "Rhyngweithiwch gyda'ch rhestr wylio",
        "grant-group-email": "Danfon ebost",
+       "grant-group-customization": "Eich dewisiadau chi",
+       "grant-group-administration": "Rhoi materion gweinyddol ar waith",
+       "grant-blockusers": "Blociwch a dadflociwch defnyddwyr",
+       "grant-createaccount": "Crewch gyfri",
+       "grant-createeditmovepage": "Creu, golygu a symud dalennau",
+       "grant-delete": "Dileu dalennau, adolygiadau a chofnodion y log",
+       "grant-editinterface": "Golygwch ''namespace'' MediaWiki a CSS/JavaScript y defnyddiwr",
+       "grant-editmycssjs": "Golygwch eich CSS/JavaScript",
+       "grant-editmyoptions": "Golygwch eich dewisiadau",
+       "grant-editmywatchlist": "Golygwch eich rhestr wylio",
+       "grant-editpage": "Golygwch dudalennau sy'n bodoli",
+       "grant-editprotected": "Golygwch dalennau sydd wedi'u cloi",
+       "grant-highvolume": "Golygu nifer ar yr un pryd",
+       "grant-oversight": "Cuddiwch y defnyddwyr a'u golygiadau",
+       "grant-patrol": "Newidiadau patrol y dalennau",
+       "grant-protect": "Cloi a datgloi dalennau",
+       "grant-rollback": "Dadwneud newidiadau dalennau",
+       "grant-sendemail": "Danfonwch ebost i ddefnyddwyr",
+       "grant-uploadeditmovefile": "Uwchlwythwch, ailosodwch a symudwch ffeiliau",
+       "grant-uploadfile": "Uwchlwythwch ffeiliau newydd",
        "grant-basic": "Hawliau sylfaenol",
        "grant-viewdeleted": "Gweld ffeiliau a ddilewyd",
        "grant-viewmywatchlist": "Gweld eich Rhestr Wylio",
+       "grant-viewrestrictedlogs": "Gweld cofnodion log cuddiedig",
        "newuserlogpage": "Lòg creu cyfrifon defnyddwyr newydd",
        "newuserlogpagetext": "Dyma restr o'r defnyddwyr newydd sydd wedi ymuno â'r wici.",
        "rightslog": "Lòg galluoedd defnyddiwr",
        "action-upload_by_url": "uwchlwytho'r ffeil o gyfeiriad URL",
        "action-writeapi": "defnyddio'r API i ysgrifennu a thrin y tudalennau",
        "action-delete": "dileu'r dudalen",
-       "action-deleterevision": "dileu'r golygiad",
-       "action-deletedhistory": "gweld hanes dilëedig y dudalen hon",
+       "action-deleterevision": "dileu'r newidiadau",
+       "action-deletelogentry": "dileu cofnodion log",
+       "action-deletedhistory": "gweld hanes dilëedig un ddalen",
+       "action-deletedtext": "Gweld newidiadau testun dalen a ddilewyd",
        "action-browsearchive": "chwilio drwy tudalennau dilëedig",
-       "action-undelete": "dad-ddileu'r dudalen",
+       "action-undelete": "dad-ddileu'r ddalen",
        "action-suppressrevision": "gweld ac adfer y golygiad cudd hwn",
        "action-suppressionlog": "gweld y lòg preifat hwn",
        "action-block": "atal y defnyddiwr hwn rhag golygu",
        "action-userrights-interwiki": "golygu galluoedd y defnyddwyr ar wicïau eraill",
        "action-siteadmin": "cloi neu ddatgloi'r databas",
        "action-sendemail": "anfon e-bost",
+       "action-editmyoptions": "golygwch eich dewisiadau",
        "action-editmywatchlist": "golygu eich rhestr wylio",
        "action-viewmywatchlist": "gweld eich rhestr wylio",
        "action-viewmyprivateinfo": "gweld eich manylion personol preifat",
        "action-editmyprivateinfo": "golygu eich manylion personol preifat",
        "action-editcontentmodel": "golygwch y cynnwys",
+       "action-managechangetags": "crewch a throwch y tagiau'n weithredol",
+       "action-applychangetags": "rhowch y tagiau ar waith, gyda'ch newidiadau",
+       "action-deletechangetags": "dilewch tagiau o'r gronfa ddata",
+       "action-purge": "carthwch y ddalen",
        "nchanges": "$1 {{PLURAL:$1|newid|newid|newid|newid|newid|o newidiadau}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ers eich ymweliad diwethaf}}",
        "enhancedrc-history": "hanes",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (gweler hefyd [[Special:NewPages|restr y tudalennau newydd]])",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Dangos",
+       "rcfilters-activefilters": "Hidlau sydd ar waith",
+       "rcfilters-restore-default-filters": "Ailosodwch y ffiltrau di-ofyn",
+       "rcfilters-clear-all-filters": "Cliriwch yr holl hidlau (ffiltrau)",
+       "rcfilters-search-placeholder": "Ffiltrwch y newidiadau diweddaraf",
+       "rcfilters-invalid-filter": "Hidl annilys",
+       "rcfilters-empty-filter": "Dim hidlau ar waith",
+       "rcfilters-filterlist-title": "Hidlau (ffiltrau)",
+       "rcfilters-filterlist-feedbacklink": "Rhowch adborth ar yr hidlau beta",
+       "rcfilters-highlightbutton-title": "Amlygwch y canlyniadau",
+       "rcfilters-highlightmenu-title": "Dewisiwch liw",
+       "rcfilters-highlightmenu-help": "Dewisiwch liw sy'n cyd-fynd gyda'r nodwedd hon",
+       "rcfilters-filterlist-noresults": "Dim hidl ar gael",
+       "rcfilters-filtergroup-registration": "Defnyddiwch y gofrestr",
+       "rcfilters-filter-registered-label": "Cofrestr",
+       "rcfilters-filter-registered-description": "Golygyddion sydd wedi mewngofnodi.",
+       "rcfilters-filter-unregistered-label": "Heb gofrestru",
+       "rcfilters-filter-unregistered-description": "Golygyddion nad ydynt wedi cofrestru.",
+       "rcfilters-filter-editsbyself-label": "Eich golygiadau chi",
+       "rcfilters-filter-editsbyself-description": "Golygiadau gennych chi.",
+       "rcfilters-filter-editsbyother-label": "Golygiadau gan eraill",
+       "rcfilters-filter-editsbyother-description": "Golygiadau a grewyd gan eraill (nid gennych chi).",
+       "rcfilters-filtergroup-userExpLevel": "Lefel profiad (defnyddwyr cofrestredig yn unig)",
+       "rcfilters-filter-patrolled-label": "Wedi derbyn patrol",
+       "rcfilters-filter-patrolled-description": "Marciwyd y golygiadau fel rhai sydd wedi derbyn patrol.",
+       "rcfilters-filter-unpatrolled-description": "Ni farciwyd y golygiadau fel rhai sydd wedi derbyn patrol.",
        "rcnotefrom": "Isod rhestrir pob newid er <strong>$3, $4</strong> (ymddengys <strong>$1</strong> ohonynt).",
        "rclistfrom": "Dangos newidiadau newydd, gan ddechrau ers $3 $2",
        "rcshowhideminor": "$1 golygiadau bychain",
        "upload-copy-upload-invalid-domain": "Ni ellir uwchlwytho ffeiliau o'r parth hwn.",
        "upload-dialog-title": "Uwchlwytho ffeil",
        "upload-dialog-button-cancel": "Canslo",
+       "upload-dialog-button-back": "Ewch nôl",
        "upload-dialog-button-done": "Wedi cwbwlhau",
        "upload-dialog-button-save": "Wedi ei gadw",
        "upload-dialog-button-upload": "Uwchlwytho",
        "uploadstash-badtoken": "Ni lwyddodd y weithred. Efallai oherwydd bod eich cymwysterau golygu wedi dod i ben. Ceisiwch eto.",
        "uploadstash-errclear": "Ni lwyddwyd i glirio'r ffeiliau.",
        "uploadstash-refresh": "Adnewyddu rhestr y ffeiliau",
+       "uploadstash-thumbnail": "gweld y ciplun",
        "invalid-chunk-offset": "Atred annilys i'r talpiau",
        "img-auth-accessdenied": "Ni chaniatawyd mynediad",
        "img-auth-nopathinfo": "PATH_INFO yn eisiau.\nNid yw'ch gweinydd wedi ei osod i fedru pasio'r wybodaeth hon.\nEfallai ei fod wedi ei seilio ar CGI, ac heb fod yn gallu cynnal img_auth.\nGweler https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "apisandbox-helpurls": "Dolennau cymorth",
        "apisandbox-examples": "Engreifftiau",
        "apisandbox-results": "Canlyniadau",
+       "apisandbox-continue": "Parhau",
+       "apisandbox-continue-clear": "Clirio",
+       "apisandbox-multivalue-all-namespaces": "$1 (Pob ''namespace'')",
+       "apisandbox-multivalue-all-values": "$1 (Pob gwerth)",
        "booksources": "Ffynonellau llyfrau",
        "booksources-search-legend": "Chwilier am lyfrau",
        "booksources-search": "Chwilio",
        "confirm-watch-top": "Ychwaneger y dudalen hon at eich rhestr wylio?",
        "confirm-unwatch-button": "Iawn",
        "confirm-unwatch-top": "Tynner y dudalen hon oddi ar eich rhestr wylio?",
+       "confirm-rollback-top": "Dadwneud golygiadau'r ddalen hon?",
        "quotation-marks": "'$1'",
        "imgmultipageprev": "← i'r dudalen gynt",
        "imgmultipagenext": "i'r dudalen nesaf →",
        "watchlistedit-raw-done": "Diweddarwyd eich rhestr wylio.",
        "watchlistedit-raw-added": "Ychwanegwyd {{PLURAL:$1|1 teitl|$1 teitl|$1 deitl|$1 theitl|$1 theitl|$1 o deitlau}}:",
        "watchlistedit-raw-removed": "Tynnwyd {{PLURAL:$1|1 teitl|$1 teitl|$1 deitl|$1 theitl|$1 theitl|$1 o deitlau}}:",
-       "watchlistedit-clear-title": "Cliriwyd y rhestr wylio",
+       "watchlistedit-clear-title": "Clirio'r rhestr wylio",
        "watchlistedit-clear-legend": "Clirier y rhestr wylio",
        "watchlistedit-clear-explain": "Bydd holl deitlau eich rhestr wylio'n cael eu tynnu oddi yno",
        "watchlistedit-clear-titles": "Teitlau:",
        "watchlisttools-edit": "Gweld a golygu'r rhestr wylio",
        "watchlisttools-raw": "Golygu'r rhestr wylio syml",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|sgwrs]])",
+       "timezone-local": "Lleol",
        "duplicate-defaultsort": "Rhybudd: Mae'r allwedd trefnu diofyn \"$2\" yn gwrthwneud yr allwedd trefnu diofyn blaenorol \"$1\".",
        "duplicate-displaytitle": "<strong>Gofal:</strong> Mae arddangos \"$2\" yn clirio'r arddangosiadau cynharach \"$1\".",
        "invalid-indicator-name": "<strong>Gan bwyll:</strong> Ni ddylid gadael y man nodi statws  <code>name</code> yn wag.",
        "logentry-upload-overwrite": "Mae $1 {{GENDER:$2|wedi uwchlwytho}} fersiwn newydd o $3",
        "logentry-upload-revert": "Mae $1 {{GENDER:$2|wedi uwchlwytho}} $3",
        "rightsnone": "(dim)",
-       "revdelete-summary": "crynodeb golygu",
        "feedback-adding": "Wrthi'n ychwanegu adborth i'r dudalen...",
        "feedback-bugcheck": "Iawn! Gwnewch yn siwr yn gyntaf nag ydy hwn yn un o'r [$1 bygiau hysbys].",
        "feedback-bugnew": "Edrychais ar y bygiau hysbys. Mae byg newydd gennyf i'w adrodd",
index 6a4a025..51d8ea3 100644 (file)
        "rcfilters-activefilters": "Aktive filtre",
        "rcfilters-invalid-filter": "Ugyldigt filter",
        "rcfilters-filterlist-title": "Filtre",
-       "rcfilters-filter-userExpLevel-experienced-label": "Erfarne brugere",
+       "rcfilters-filter-user-experience-level-experienced-label": "Erfarne brugere",
        "rcnotefrom": "Nedenfor er op til '''$1''' {{PLURAL:$5|ændring|ændringer}} siden '''$2''' vist.",
        "rclistfrom": "Vis nye ændringer startende fra den $3 kl. $2",
        "rcshowhideminor": "$1 mindre ændringer",
index d7d3cef..e1d254c 100644 (file)
        "prefs-help-prefershttps": "Diese Einstellung wird bei deiner nächsten Anmeldung wirksam.",
        "prefswarning-warning": "Du hast Änderungen an deinen Einstellungen durchgeführt, die noch nicht gespeichert wurden.\nWenn du diese Seite verlässt ohne auf „$1“ zu klicken, werden deine Einstellungen nicht aktualisiert.",
        "prefs-tabs-navigation-hint": "Tipp: Du kannst die linke und rechte Pfeiltasten benutzen, um zwischen den Registerkarten in der Reiterliste zu navigieren.",
-       "userrights": "Benutzerrechte verwalten",
+       "userrights": "Benutzerrechte",
        "userrights-lookup-user": "Einen Benutzer auswählen",
        "userrights-user-editname": "Benutzername:",
        "editusergroup": "Benutzergruppen laden",
        "rcfilters-filter-registered-description": "Angemeldete Autoren.",
        "rcfilters-filter-unregistered-label": "Unangemeldet",
        "rcfilters-filter-unregistered-description": "Autoren, die nicht angemeldet sind.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Der Filter „Unangemeldet“ ist inaktiv, da sein Effekt durch {{PLURAL:$2|den|die}} folgenden Erfahrungsfilter aufgehoben wird, {{PLURAL:$2|der nur angemeldete Benutzer finden kann|die nur angemeldete Benutzer finden können}}: $1",
        "rcfilters-filtergroup-authorship": "Bearbeitungs-Autorenschaft",
        "rcfilters-filter-editsbyself-label": "Deine eigenen Bearbeitungen",
        "rcfilters-filter-editsbyself-description": "Bearbeitungen von dir.",
        "rcfilters-filter-editsbyother-label": "Bearbeitungen von anderen",
        "rcfilters-filter-editsbyother-description": "Bearbeitungen von anderen Benutzern (nicht von dir).",
        "rcfilters-filtergroup-userExpLevel": "Erfahrungsniveau (nur für angemeldete Benutzer)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Neulinge",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Weniger als 10 Bearbeitungen und eine Aktivität von weniger als 4 Tagen.",
-       "rcfilters-filter-userExpLevel-learner-label": "Anfänger",
-       "rcfilters-filter-userExpLevel-learner-description": "Eine Aktivität von mehreren Tagen und mehr Bearbeitungen als „Neulinge“, aber weniger als „Erfahrene Benutzer“.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Erfahrene Benutzer",
-       "rcfilters-filter-userExpLevel-experienced-description": "Eine Aktivität von mehr als 30 Tagen und mehr als 500 Bearbeitungen.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Dieser Filter ist inaktiv, da er nur angemeldete Benutzer findet, so dass der Filter „Unangemeldet“ seinen Effekt aufhebt.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Der Filter „Unangemeldet“ befindet sich in Konflikt mit einem oder mehreren Erfahrungsfiltern. Erfahrungsfilter finden nur angemeldete Benutzer. Die kollidierenden Filter sind oben als inaktiv markiert.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Neulinge",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Weniger als 10 Bearbeitungen und eine Aktivität von weniger als 4 Tagen.",
+       "rcfilters-filter-user-experience-level-learner-label": "Anfänger",
+       "rcfilters-filter-user-experience-level-learner-description": "Eine Aktivität von mehreren Tagen und mehr Bearbeitungen als „Neulinge“, aber weniger als „Erfahrene Benutzer“.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Erfahrene Benutzer",
+       "rcfilters-filter-user-experience-level-experienced-description": "Eine Aktivität von mehr als 30 Tagen und mehr als 500 Bearbeitungen.",
        "rcfilters-filtergroup-automated": "Automatisierte Beiträge",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Bearbeitungen, die von automatisierten Werkzeugen durchgeführt wurden.",
        "rcfilters-filter-humans-label": "Mensch (kein Bot)",
        "rcfilters-filter-humans-description": "Bearbeitungen, die von menschlichen Autoren durchgeführt wurden.",
+       "rcfilters-filtergroup-reviewstatus": "Überprüfungsstatus",
+       "rcfilters-filter-patrolled-label": "Kontrolliert",
+       "rcfilters-filter-patrolled-description": "Bearbeitungen, die als kontrolliert markiert sind.",
+       "rcfilters-filter-unpatrolled-label": "Unkontrolliert",
+       "rcfilters-filter-unpatrolled-description": "Bearbeitungen, die nicht als kontrolliert markiert sind.",
        "rcfilters-filtergroup-significance": "Bedeutung",
        "rcfilters-filter-minor-label": "Kleine Änderungen",
        "rcfilters-filter-minor-description": "Bearbeitungen, die der Autor als geringfügig markiert hat.",
index a35eef7..860822c 100644 (file)
        "welcomecreation-msg": "Hesabê şıma abiyo.\n[[Special:Preferences|{{SITENAME}} vurnayişê tercihanê xo]], xo vir ra mekere.",
        "yourname": "Namey karberi:",
        "userlogin-yourname": "Namey karberi",
-       "userlogin-yourname-ph": "Namey xoyê karberi cı kewe",
+       "userlogin-yourname-ph": "Namey xoyê karberi cı kewê",
        "createacct-another-username-ph": "Nameyê karberi cı kewe",
        "yourpassword": "Parola",
        "userlogin-yourpassword": "Parola",
-       "userlogin-yourpassword-ph": "Parolay xo cı kewe",
+       "userlogin-yourpassword-ph": "Parolay xo cı kewê",
        "createacct-yourpassword-ph": "Parola cıkewe",
        "yourpasswordagain": "Parola reyna bınusne:",
        "createacct-yourpasswordagain": "Parola tesdiq ke",
        "minoredit": "No yew vırnayışo werdiyo",
        "watchthis": "Şıma bewnê ena perre",
        "savearticle": "Perre qeyd ke",
-       "savechanges": "Vırnayışan qeyd ke",
+       "savechanges": "Vırnayışan qeyd ke",
        "publishpage": "Perer bıhesırne",
        "publishchanges": "Vurnayışa vıla ke",
        "preview": "Verqayt",
        "tooltip-search": "{{SITENAME}} de cı geyre",
        "tooltip-search-go": "Ebe nê namey tami şo yew pela ke esta",
        "tooltip-search-fulltext": "Pelan miyan de nê metıni cı geyre",
-       "tooltip-p-logo": "Şo pela seri",
-       "tooltip-n-mainpage": "Şo pela seri",
-       "tooltip-n-mainpage-description": "Şo pela seri",
+       "tooltip-p-logo": "Şo perra seri",
+       "tooltip-n-mainpage": "Şo perra seri",
+       "tooltip-n-mainpage-description": "Şo perra seri",
        "tooltip-n-portal": "Heqa procey de, kes çı şeno bıkero, çı kamca vêniyeno",
        "tooltip-n-currentevents": "Vurnayışanê peyênan de melumatê pey bıvêne",
        "tooltip-n-recentchanges": "Wiki dı yew lista vurnayışanê peyênan",
index bf06e49..23bfebe 100644 (file)
@@ -29,7 +29,8 @@
                        "Aefgh39622",
                        "Anomie",
                        "Dr. Shikha Jaggi",
-                       "Xð"
+                       "Xð",
+                       "Matma Rex"
                ]
        },
        "tog-underline": "Link underlining:",
        "and": "&#32;and",
        "qbfind": "Find",
        "qbbrowse": "Browse",
-       "qbedit": "संपादित करें",
-       "qbpageoptions": "यह पन्ना",
-       "qbmyoptions": "मेरे पृष्ठों",
+       "qbedit": "Edit",
+       "qbpageoptions": "This page",
+       "qbmyoptions": "My pages",
        "faq": "FAQ",
        "faqpage": "Project:FAQ",
        "actions": "Actions",
index 18e75b4..61948ff 100644 (file)
        "prefs-help-prefershttps": "This preference will take effect on your next login.",
        "prefswarning-warning": "You've made changes to your preferences that have not been saved yet.\nIf you leave this page without clicking \"$1\" your preferences will not be updated.",
        "prefs-tabs-navigation-hint": "Tip: You can use the left and right arrow keys to navigate between the tabs in the tabs list.",
-       "userrights": "User rights management",
+       "userrights": "User rights",
        "userrights-summary": "",
        "userrights-lookup-user": "Select a user",
        "userrights-user-editname": "Enter a username:",
        "rcfilters-filter-registered-label": "Registered",
        "rcfilters-filter-registered-description": "Logged-in editors.",
        "rcfilters-filter-unregistered-label": "Unregistered",
-       "rcfilters-filter-unregistered-description": " Editors who aren’t logged in.",
+       "rcfilters-filter-unregistered-description": "Editors who aren’t logged in.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "The \"Unregistered\" filter is inactive because its effect is being canceled by the following Experience {{PLURAL:$2|filter|filters}}, which {{PLURAL:$2|finds|find}} only registered users: $1",
        "rcfilters-filtergroup-authorship": "Edit authorship",
        "rcfilters-filter-editsbyself-label": "Your own edits",
        "rcfilters-filter-editsbyself-description": "Edits by you.",
        "rcfilters-filter-editsbyother-label": "Edits by others",
        "rcfilters-filter-editsbyother-description": "Edits created by other users (not you).",
        "rcfilters-filtergroup-userExpLevel": "Experience level (for registered users only)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Newcomers",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Fewer than 10 edits and 4 days of activity.",
-       "rcfilters-filter-userExpLevel-learner-label": "Learners",
-       "rcfilters-filter-userExpLevel-learner-description": "More days of activity and edits than \"Newcomers\" but fewer than \"Experienced users\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Experienced users",
-       "rcfilters-filter-userExpLevel-experienced-description": "More than 30 days of activity and 500 edits.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "This filter is inactive because it finds only registered users, so the \"Unregistered\" filter is canceling its effect.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "The \"Unregistered\" filter is in conflict with one or more Experience filters. Experience filters find registered users only. The conflicting filters are marked as inactive above.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Newcomers",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Fewer than 10 edits and 4 days of activity.",
+       "rcfilters-filter-user-experience-level-learner-label": "Learners",
+       "rcfilters-filter-user-experience-level-learner-description": "More days of activity and edits than \"Newcomers\" but fewer than \"Experienced users\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Experienced users",
+       "rcfilters-filter-user-experience-level-experienced-description": "More than 30 days of activity and 500 edits.",
        "rcfilters-filtergroup-automated": "Automated contributions",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edits made by automated tools.",
        "rcfilters-filter-humans-label": "Human (not bot)",
        "rcfilters-filter-humans-description": "Edits made by human editors.",
+       "rcfilters-filtergroup-reviewstatus": "Review status",
+       "rcfilters-filter-patrolled-label": "Patrolled",
+       "rcfilters-filter-patrolled-description": "Edits marked as patrolled.",
+       "rcfilters-filter-unpatrolled-label": "Unpatrolled",
+       "rcfilters-filter-unpatrolled-description": "Edits not marked as patrolled.",
        "rcfilters-filtergroup-significance": "Significance",
        "rcfilters-filter-minor-label": "Minor edits",
        "rcfilters-filter-minor-description": "Edits the author labeled as minor.",
index aa5a6f2..5b5e0e4 100644 (file)
        "rcfilters-filter-editsbyother-label": "Ediciones de otros",
        "rcfilters-filter-editsbyother-description": "Ediciones creadas por otros usuarios (no por ti).",
        "rcfilters-filtergroup-userExpLevel": "Nivel de experiencia (solo para usuarios registrados)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Recién llegados",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Menos de 10 ediciones y 4 días de actividad.",
-       "rcfilters-filter-userExpLevel-learner-label": "Aprendices",
-       "rcfilters-filter-userExpLevel-learner-description": "Más días de actividad y ediciones que los «recién llegados», pero menos que los «usuarios experimentados».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Usuarios experimentados",
-       "rcfilters-filter-userExpLevel-experienced-description": "Más de 30 días de actividad y 500 ediciones.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Recién llegados",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 ediciones y 4 días de actividad.",
+       "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
+       "rcfilters-filter-user-experience-level-learner-description": "Más días de actividad y ediciones que los «recién llegados», pero menos que los «usuarios experimentados».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Usuarios experimentados",
+       "rcfilters-filter-user-experience-level-experienced-description": "Más de 30 días de actividad y 500 ediciones.",
        "rcfilters-filtergroup-automated": "Contribuciones automatizadas",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Ediciones realizadas por herramientas automatizadas.",
        "rcfilters-filter-humans-label": "Ser humano (no bot)",
        "rcfilters-filter-humans-description": "Ediciones realizadas por editores humanos.",
+       "rcfilters-filtergroup-reviewstatus": "Estado de revisión",
        "rcfilters-filtergroup-significance": "Significación",
        "rcfilters-filter-minor-label": "Ediciones menores",
        "rcfilters-filter-minor-description": "Ediciones etiquetadas por el autor como menores.",
index 6081e7f..a228ed8 100644 (file)
        "rcfilters-filterlist-feedbacklink": "ارسال بازخورد برای پالایه‌های جدید (آزمایشی)",
        "rcfilters-highlightbutton-title": "پررنگ کردن نتایج",
        "rcfilters-highlightmenu-title": "انتخاب رنگ",
+       "rcfilters-highlightmenu-help": "یک رنگ انتخاب کنید تا این خصوصیت پر رنگ شود",
        "rcfilters-filterlist-noresults": "پالایه‌ای یافت نشد",
        "rcfilters-filtergroup-registration": "ثبت‌نام کاربر",
        "rcfilters-filter-registered-label": "ثبت شده",
        "rcfilters-filter-registered-description": "ورود ویرایشگران.",
        "rcfilters-filter-unregistered-label": "ثبت‌نام نکرده",
        "rcfilters-filter-unregistered-description": "ویرایشگرانی که به سامانه وارد نشده‌اند.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "پالایهٔ ثبت‌نشده غیرفعال می‌شود و تاثیرات آن توسط  {{PLURAL:$2|پالایه|پالایه‌های}} تجربه شده که فقط توسط کاربران ثبت‌نام شده {{PLURAL:$2|می‌یابد|می‌یابند}}:$1",
        "rcfilters-filtergroup-authorship": "ویرایش نویسندگی",
        "rcfilters-filter-editsbyself-label": "ویرایش‌های شما",
        "rcfilters-filter-editsbyself-description": "ویرایش‌های انجام شده توسط شما.",
        "rcfilters-filter-editsbyother-label": "ویرایش‌های دیگران",
        "rcfilters-filter-editsbyother-description": "ویرایش‌های ایجاد شده توسط دیگران (نه شما).",
        "rcfilters-filtergroup-userExpLevel": "درجهٔ تجربه (فقط برای کاربران ثبت‌نام کرده)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "تازه‌واردها",
-       "rcfilters-filter-userExpLevel-newcomer-description": "کمتر از ۱۰ ویرایش یا ۴ روز فعالیت.",
-       "rcfilters-filter-userExpLevel-learner-label": "آموزندگان",
-       "rcfilters-filter-userExpLevel-learner-description": "فعالیت و تعداد روز بیشتر از تازه‌وارد ولی کمتر از کاربر باتجریه.",
-       "rcfilters-filter-userExpLevel-experienced-label": "کاربران باتجربه",
-       "rcfilters-filter-userExpLevel-experienced-description": "بیشتر از ۳۰ روز فعالیت و ۵۰۰ ویرایش.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "این پالایه غیرفعال شد چون فقط کاربران ثبت‌نام کرده را پیدا می کرد در نتیجه پالایه کاربران ثبت‌نام نکرده تاثیرات آن را لغو می‌کند.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "پالایه کاربران ثبت‌نام نکرده با پالایه‌های دیگر در تعارض است. پالایه‌‌های دیگر فقط کاربران ثبت‌نام شده را می‌یابند. این پالایه‌های در تعارض در بالا غیرفعال شده‌اند.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "تازه‌واردها",
+       "rcfilters-filter-user-experience-level-newcomer-description": "کمتر از ۱۰ ویرایش یا ۴ روز فعالیت.",
+       "rcfilters-filter-user-experience-level-learner-label": "آموزندگان",
+       "rcfilters-filter-user-experience-level-learner-description": "فعالیت و تعداد روز بیشتر از تازه‌وارد ولی کمتر از کاربر باتجریه.",
+       "rcfilters-filter-user-experience-level-experienced-label": "کاربران باتجربه",
+       "rcfilters-filter-user-experience-level-experienced-description": "بیشتر از ۳۰ روز فعالیت و ۵۰۰ ویرایش.",
        "rcfilters-filtergroup-automated": "ویرایش‌های خودکار",
        "rcfilters-filter-bots-label": "ربات",
        "rcfilters-filter-bots-description": "ویرایش توسط ابزارهای خودکار.",
        "rcfilters-filter-humans-label": "انسان (ربات نه)",
        "rcfilters-filter-humans-description": "ویرایش توسط انسان.",
+       "rcfilters-filtergroup-reviewstatus": "وضعیت بازبینی",
+       "rcfilters-filter-patrolled-label": "گشت‌خورده",
+       "rcfilters-filter-patrolled-description": "برچسب ویرایش به عنوان گشت‌خورده.",
+       "rcfilters-filter-unpatrolled-label": "گشت‌نخورده",
+       "rcfilters-filter-unpatrolled-description": "ویرایش‌ها به عنوان گشت‌نخورده.",
        "rcfilters-filtergroup-significance": "اهمیت",
        "rcfilters-filter-minor-label": "ویرایش‌های جزئی",
        "rcfilters-filter-minor-description": "ویرایش‌هایی که به عنوان جزئی برچسب خورده‌اند.",
index 10ec323..1f3a4e6 100644 (file)
        "rcfilters-filter-editsbyself-description": "Muokkauksesi",
        "rcfilters-filter-editsbyother-label": "Muiden muokkaukset",
        "rcfilters-filter-editsbyother-description": "Muutokset jotka tehneet muut käyttäjät (et sinä).",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Tulokkaat",
-       "rcfilters-filter-userExpLevel-learner-label": "Oppijat",
-       "rcfilters-filter-userExpLevel-experienced-label": "Kokeneet käyttäjät",
-       "rcfilters-filter-userExpLevel-experienced-description": "Enemmän kuin 30 päivää aktiivisena ja 500 muokkausta.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Tulokkaat",
+       "rcfilters-filter-user-experience-level-learner-label": "Oppijat",
+       "rcfilters-filter-user-experience-level-experienced-label": "Kokeneet käyttäjät",
+       "rcfilters-filter-user-experience-level-experienced-description": "Enemmän kuin 30 päivää aktiivisena ja 500 muokkausta.",
        "rcfilters-filtergroup-automated": "Automatisoidut muutokset",
        "rcfilters-filter-bots-label": "Botti",
        "rcfilters-filter-bots-description": "Muokkaukset jotka tehty automaattisilla työkaluilla.",
index 879e948..510e273 100644 (file)
        "rcfilters-filter-registered-description": "Éditeurs connectés.",
        "rcfilters-filter-unregistered-label": "Non connectés",
        "rcfilters-filter-unregistered-description": "Éditeurs qui ne sont pas connectés.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Le filtre « Non inscrit » est inactif parce que son effet est annulé par {{PLURAL:$2|le filtre d’expérience suivant|les filtres d’expérience suivants}}, qui ne {{PLURAL:$2|trouve|trouvent}} que des utilisateurs inscrits : $1",
        "rcfilters-filtergroup-authorship": "Modifier la paternité",
        "rcfilters-filter-editsbyself-label": "Vos propres modifications",
        "rcfilters-filter-editsbyself-description": "Vos modifications.",
        "rcfilters-filter-editsbyother-label": "Modifications par d’autres.",
        "rcfilters-filter-editsbyother-description": "Modifications créées par d’autres utilisateurs (pas vous).",
        "rcfilters-filtergroup-userExpLevel": "Niveau d’expérience (uniquement pour les utilisateurs enregistrés)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Nouveaux arrivants",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Moins de 10 modifications et 4 jours d’activité.",
-       "rcfilters-filter-userExpLevel-learner-label": "Apprentis",
-       "rcfilters-filter-userExpLevel-learner-description": "Davantage de jours d’activité et de modifications que les « Nouveaux arrivants » mais moins que les « Utilisateurs expérimentés ».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Utilisateurs expérimentés",
-       "rcfilters-filter-userExpLevel-experienced-description": "Plus de 30 jours d’activité et 500 modifications",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Ce filtre est désactivé car il ne recherche que les utilisateurs enregistrés, et le filtre \"Unregistered\" annule son effet.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Le filtre « Non inscrit » est en conflit avec au moins un filtre d’expérience. Les filtres d’expérience trouvent uniquement des utilisateurs inscrits. Les filtres en conflit sont marqués comme inactifs ci-dessus.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Nouveaux arrivants",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Moins de 10 modifications et 4 jours d’activité.",
+       "rcfilters-filter-user-experience-level-learner-label": "Apprentis",
+       "rcfilters-filter-user-experience-level-learner-description": "Davantage de jours d’activité et de modifications que les « Nouveaux arrivants » mais moins que les « Utilisateurs expérimentés ».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Utilisateurs expérimentés",
+       "rcfilters-filter-user-experience-level-experienced-description": "Plus de 30 jours d’activité et 500 modifications",
        "rcfilters-filtergroup-automated": "Contributions automatisées",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Modifications faites par des outils automatisés.",
        "rcfilters-filter-humans-label": "Humain (non robot)",
        "rcfilters-filter-humans-description": "Modifications faites par des éditeurs humains.",
+       "rcfilters-filtergroup-reviewstatus": "Etat de révision",
+       "rcfilters-filter-patrolled-label": "Patrouillé",
+       "rcfilters-filter-patrolled-description": "Modifications marquées comme patrouillées.",
+       "rcfilters-filter-unpatrolled-label": "Non patrouillé",
+       "rcfilters-filter-unpatrolled-description": "Modifications non marquées comme patrouillées.",
        "rcfilters-filtergroup-significance": "Signification",
        "rcfilters-filter-minor-label": "Modifications mineures",
        "rcfilters-filter-minor-description": "Modifications que l’auteur a marquées comme mineures.",
        "htmlform-user-not-exists": "<strong>$1</strong> n’existe pas.",
        "htmlform-user-not-valid": "<strong>$1</strong> n’est pas un nom d’utilisateur valide.",
        "logentry-delete-delete": "$1 {{GENDER:$2|a supprimé}} la page $3",
-       "logentry-delete-delete_redir": "$1 a {{GENDER:$2|supprimé}} la redirection vers $3 par écrasement.",
+       "logentry-delete-delete_redir": "$1 a {{GENDER:$2|supprimé}} la redirection vers $3 par écrasement",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restauré}} la page $3",
        "logentry-delete-event": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'un événement du journal|de $5 événements du journal}} sur $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'une révision|de $5 révisions}} sur la page $3 : $4",
index c12cd04..4b95c8d 100644 (file)
        "rcfilters-filterlist-feedbacklink": "Deixar comentarios sobre os novos filtros (en fase beta)",
        "rcfilters-highlightbutton-title": "Resaltar resultados",
        "rcfilters-highlightmenu-title": "Seleccione unha cor",
+       "rcfilters-highlightmenu-help": "Seleccione unha cor para resaltar esta propiedade",
        "rcfilters-filterlist-noresults": "Non se atoparon filtros",
        "rcfilters-filtergroup-registration": "Rexistro de usuario",
        "rcfilters-filter-registered-label": "Rexistrado",
        "rcfilters-filter-editsbyother-label": "Edicións doutros.",
        "rcfilters-filter-editsbyother-description": "Edicións creadas por outros usuarios (non por vostede).",
        "rcfilters-filtergroup-userExpLevel": "Nivel de experiencia (só para usuarios rexistrados)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Chegados recentemente",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Menos de 10 edicións e 4 días de actividade.",
-       "rcfilters-filter-userExpLevel-learner-label": "Aprendices",
-       "rcfilters-filter-userExpLevel-learner-description": "Máis días de actividade e edicións que \"novatos\" pero menos que \"usuarios experimentados\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Usuarios experimentados",
-       "rcfilters-filter-userExpLevel-experienced-description": "Máis de 30 días de actividade e 500 edicións.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Chegados recentemente",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edicións e 4 días de actividade.",
+       "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
+       "rcfilters-filter-user-experience-level-learner-description": "Máis días de actividade e edicións que \"novatos\" pero menos que \"usuarios experimentados\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Usuarios experimentados",
+       "rcfilters-filter-user-experience-level-experienced-description": "Máis de 30 días de actividade e 500 edicións.",
        "rcfilters-filtergroup-automated": "Contribucións automatizadas",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edicións realizadas por ferramentas automatizadas.",
index 40536bf..e0c1524 100644 (file)
        "prefs-help-prefershttps": "העדפה זו תיכנס לתוקף בכניסה הבאה לחשבון.",
        "prefswarning-warning": "ביצעת שינויים בהעדפות שלך, והם עדיין לא נשמרו.\nעזיבת דף זה ללא לחיצה על \"$1\" תגרום לכך שההעדפות שלך לא יעודכנו.",
        "prefs-tabs-navigation-hint": "טיפ: ניתן להשתמש במקשי החצים הימני והשמאלי כדי לנווט בין הלשוניות ברשימת הלשוניות.",
-       "userrights": "× ×\99×\94×\95×\9c ×\94רש×\90×\95ת ×\9eשת×\9eש",
+       "userrights": "הרשאות משתמש",
        "userrights-lookup-user": "בחירת משתמש",
        "userrights-user-editname": "שם משתמש:",
        "editusergroup": "טעינת קבוצות המשתמש",
        "rcfilters-filter-registered-description": "עורכים שנכנסו לחשבון.",
        "rcfilters-filter-unregistered-label": "לא רשומים",
        "rcfilters-filter-unregistered-description": "עורכים שלא נכנסו לחשבון.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "המסנן \"לא רשומים\" אינו פעיל כי השפעתו מתבטלת באמצעות {{PLURAL:$2|מסנן רמת הניסיון הבא, שמוצא|מסנני רמת הניסיון הבאים, שמוצאים}} רק משתמשים רשומים: $1",
        "rcfilters-filtergroup-authorship": "מבצעי העריכה",
        "rcfilters-filter-editsbyself-label": "עריכות שלך",
        "rcfilters-filter-editsbyself-description": "עריכות שביצעת בעצמך.",
        "rcfilters-filter-editsbyother-label": "עריכות של אחרים",
        "rcfilters-filter-editsbyother-description": "עריכות שבוצעו על־ידי משתמשים אחרים (מלבדך).",
-       "rcfilters-filtergroup-userExpLevel": "הניסיון והוותק (למשתמשים רשומים בלבד)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "חדשים",
-       "rcfilters-filter-userExpLevel-newcomer-description": "פחות מ־10 עריכות ומ־4 ימים של פעילות.",
-       "rcfilters-filter-userExpLevel-learner-label": "לומדים",
-       "rcfilters-filter-userExpLevel-learner-description": "יותר ימי פעילות ועריכות מ\"חדשים\", אבל פחות מ\"משתמשים מנוסים\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "משתמשים מנוסים",
-       "rcfilters-filter-userExpLevel-experienced-description": "יותר מ־30 ימים של פעילות ו־500 עריכות.",
+       "rcfilters-filtergroup-userExpLevel": "רמת ניסיון (למשתמשים רשומים בלבד)",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "המסנן הזה אינו פעיל כי הוא מוצא רק משתמשים רשומים, ולכן המסנן \"לא רשומים\" מבטל את השפעתו.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "המסנן \"לא רשומים\" מתנגש עם אחד או יותר ממסנני רמת הניסיון. מסנני רמת ניסיון מוצאים משתמשים רשומים בלבד. המסננים המתנגשים מסומנים לעיל כבלתי־פעילים.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "חדשים",
+       "rcfilters-filter-user-experience-level-newcomer-description": "פחות מ־10 עריכות ומ־4 ימים של פעילות.",
+       "rcfilters-filter-user-experience-level-learner-label": "לומדים",
+       "rcfilters-filter-user-experience-level-learner-description": "יותר ימי פעילות ועריכות מ\"חדשים\", אבל פחות מ\"משתמשים מנוסים\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "משתמשים מנוסים",
+       "rcfilters-filter-user-experience-level-experienced-description": "יותר מ־30 ימים של פעילות ו־500 עריכות.",
        "rcfilters-filtergroup-automated": "תרומות אוטומטיות",
        "rcfilters-filter-bots-label": "בוטים",
        "rcfilters-filter-bots-description": "עריכות שבוצעו על־ידי כלים אוטומטיים.",
        "rcfilters-filter-humans-label": "בני אדם (לא בוטים)",
        "rcfilters-filter-humans-description": "עריכות שבוצעו על־ידי עורכים אנושיים.",
+       "rcfilters-filtergroup-reviewstatus": "מצב סקירה",
+       "rcfilters-filter-patrolled-label": "בדוקות",
+       "rcfilters-filter-patrolled-description": "עריכות שסומנו כבדוקות.",
+       "rcfilters-filter-unpatrolled-label": "לא בדוקות",
+       "rcfilters-filter-unpatrolled-description": "עריכות שלא סומנו כבדוקות.",
        "rcfilters-filtergroup-significance": "חשיבות",
        "rcfilters-filter-minor-label": "עריכות משניות",
        "rcfilters-filter-minor-description": "עריכות שהוגדרו על־ידי העורכים כמשניות.",
index f2fefcf..6bbe11e 100644 (file)
        "rcfilters-filter-editsbyself-label": "आपके अपने संपादन",
        "rcfilters-filter-editsbyself-description": "आपके द्वारा संपादित",
        "rcfilters-filter-editsbyother-label": "दूसरों के द्वारा संपादित",
-       "rcfilters-filter-userExpLevel-newcomer-label": "अपरिचित",
-       "rcfilters-filter-userExpLevel-learner-label": "शिक्षार्थियों",
+       "rcfilters-filter-user-experience-level-newcomer-label": "अपरिचित",
+       "rcfilters-filter-user-experience-level-learner-label": "शिक्षार्थियों",
        "rcfilters-filter-bots-label": "बॉट",
        "rcfilters-filter-humans-label": "मानव (बॉट नहीं)",
        "rcfilters-filter-humans-description": "मानव संपादक द्वारा किए गए संपादन।",
index d0461f8..6e45a67 100644 (file)
        "category_header": "Stranice u kategoriji »$1«",
        "subcategories": "Potkategorije",
        "category-media-header": "Mediji u kategoriji »$1«",
-       "category-empty": "''U ovoj kategoriji trenutačno nema članaka ni medija.''",
+       "category-empty": "<em>U ovoj kategoriji trenutačno nema članaka ni medija.</em>",
        "hidden-categories": "{{PLURAL:$1|Skrivena kategorija|Skrivene kategorije|Skrivenih kategorija}}",
        "hidden-category-category": "Skrivene kategorije",
        "category-subcat-count": "Ova kategorija sadrži $2 {{PLURAL:$2|podkategoriju|podkategorije|podkategorija}}, ovaj popis prikazuje $1.",
        "category-subcat-count-limited": "Ova kategorija ima {{PLURAL:$1|podkategoriju|$1 podkategorije|$1 podkategorija}}.",
-       "category-article-count": "{{PLURAL:$2|Ova kategorija sadrži jednu stranicu.|{{PLURAL:$1|Prikazana je $1 stranica|Prikazane su $1 stranice|Prikazano je $1 stranica}} od njih $2 ukupno.}}",
+       "category-article-count": "{{PLURAL:$2|Ova kategorija sadrži samo sljedeću stranicu.|{{PLURAL:$1|Prikazana stranica jedina je|Prikazane su $1 stranice su|Prikazano je $1 stranica}} u ovoj kategoriji, od ukupno $2.}}",
        "category-article-count-limited": "{{PLURAL:$1|stranica je|$1 stranice su|$1 stranica je}} u ovoj kategoriji.",
        "category-file-count": "Ova kategorija sadrži $2 {{PLURAL:$2|datoteku|datoteke|datoteka}}. {{PLURAL:$1|Slijedi $1 datoteka|Slijede $1 datoteke|Slijedi $1 datoteka}}.",
        "category-file-count-limited": "{{PLURAL:$1|datoteka je|$1 datoteke su|$1 datoteka su}} u ovoj kategoriji.",
        "toc": "Sadržaj",
        "showtoc": "prikaži",
        "hidetoc": "sakrij",
-       "collapsible-collapse": "sklopi stablo",
-       "collapsible-expand": "raširi stablo",
+       "collapsible-collapse": "sklopi",
+       "collapsible-expand": "raširi",
        "confirmable-confirm": "Jeste li sigurni?",
        "confirmable-yes": "Da",
        "confirmable-no": "Ne",
        "createacct-reason-ph": "Zašto stvarate drugi račun",
        "createacct-submit": "Stvorite svoj suradnički račun",
        "createacct-another-submit": "Otvori račun",
+       "createacct-continue-submit": "Pritisni za stvaranje računa",
+       "createacct-another-continue-submit": "Nastavi za stvaranje računa",
        "createacct-benefit-heading": "{{SITENAME}} su stvorili ljudi poput Vas.",
        "createacct-benefit-body1": "{{PLURAL:$1|uređivanje|uređivanja}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stranica|stranice|stranica}}",
        "botpasswords-insert-failed": "Nije moguće dodavanje imena bota \"$1\". Možda je već dodano?",
        "botpasswords-update-failed": "Nije moguće ažurirati bot s imenom \"$1\". Možda je izbrisan?",
        "botpasswords-created-title": "Stvorena bot zaporka",
+       "botpasswords-updated-title": "Zaporka za Vašeg bota obnovljena je",
+       "botpasswords-updated-body": "Zaporka za bota imena »$1« suradnika »$2« obnovljena je.",
+       "botpasswords-deleted-title": "Zaporka je za Vašeg bota uklonjena",
        "resetpass_forbidden": "Zaporka ne može biti promijenjena",
        "resetpass-no-info": "Morate biti prijavljeni da biste izravno pristupili ovoj stranici.",
        "resetpass-submit-loggedin": "Promijeni zaporku",
        "template-protected": "(zaštićen)",
        "template-semiprotected": "(djelomično zaštićen)",
        "hiddencategories": "Ova stranica je član {{PLURAL:$1|1 skrivene kategorije|$1 skrivene kategorije|$1 skrivenih kategorija}}:",
-       "edittools": "<!-- Ovaj će tekst biti prikazan ispod obrasca za uređivanje i postavljanje. -->",
+       "edittools": "<div id=\"specialcharss\" class=\"toccolours specialchars\" style=\"margin-top:.5em; padding: .3em .5em; font-size: 100%; color:#aaa; text-align:left;\" title=\"{{int:bw-edittools-tooltip}}\">\n<p class=\"specialbasic\" id=\"Standard\">\n'''{{int:bw-edittools-lead-in}}''' \n<charinsert>Á á É é Í í Ó ó Ú ú Ý ý</charinsert> –\n<charinsert>À à È è Ì ì Ò ò Ù ù </charinsert> –\n<charinsert> â Ê ê Î î Ô ô Û û </charinsert> –\n<charinsert>Ä ä Ë ë Ï ï Ö ö Ü ü Ÿ ÿ</charinsert> –\n<charinsert>Æ æ Ø ø Œ œ ẞ ß </charinsert> –\n<charinsert>Å å Ů ů </charinsert> –\n<charinsert>àã Ẽ ẽ ɛ̃ Ĩ ĩ Ñ ñ Õ õ ɔ̃ Ũ ũ </charinsert> –\n<charinsert>Рð Þ þ </charinsert> –\n<charinsert>Ç ç Ģ ģ Ķ ķ Ļ ļ Ņ ņ Ŗ ŗ Ş ş Ţ ţ </charinsert> –\n<charinsert>Ć ć Ĺ ĺ Ń ń Ŕ ŕ Ś ś Ý ý Ź ź </charinsert> –\n<charinsert>Č č Ď ď Ľ ľ Ň ň Ř ř Š š Ť ť Ž ž </charinsert> –\n<charinsert>Ǎ ǎ Ě ě Ǐ ǐ Ǒ ǒ Ǔ ǔ </charinsert> –\n<charinsert>Ā ā Ē ē Ī ī Ō ō Ū ū </charinsert> –\n<charinsert>ǖ ǘ ǚ ǜ </charinsert> –\n<charinsert>Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŵ ŵ Ŷ ŷ </charinsert> –\n<charinsert>Ă ă Ğ ğ Ŭ ŭ </charinsert> –\n<charinsert>Ċ ċ Ė ė Ġ ġ Għ għ İ ı Ż ż </charinsert> –\n<charinsert>Ą ą Ę ę Į į Ų ų </charinsert> –\n<charinsert>Ő ő Ű ű </charinsert> –\n<charinsert>Đ đ Ħ ħ Ł ł Ŀ ŀ </charinsert> –\n<charinsert>Ɖ ɖ Ɛ ɛ Ƒ ƒ Ɣ ɣ Ŋ ŋ Ɔ ɔ Ʋ ʋ </charinsert> -\n<charinsert>Ə ə </charinsert> –\n<charinsert>– — ’</charinsert> –\n<charinsert>~ | ° ¹ ² ³ ⅛ ¼ ⅓ ⅜ ½ ⅝ ¾ ⅔ ⅞ € $ ¥ £ † × ← → ↔ ↑ ± ≠ © ® ™ ‰ »+« ›+‹ „+” ‚+’ ‚+‘ „+“ ¡ ¿ …</charinsert> –\n<charinsert>&amp;nbsp; &nbsp; #REDIRECT {{msg-mw|+|notext=1}} &#33;!FUZZY!! ~~~~  &lt;nowiki>+</nowiki></charinsert>\n<charinsert>ڈ ڑ ٹ </charinsert>\n<charinsert>ټ څ ځ ډ ړ ږ ښ ڼ ؤ ي ې ۍ ئ </charinsert>\n<charinsert>{{{+}}} {{+}} {{subst:+}} <noinclude>+</noinclude></charinsert>\n<charinsert>&lt;!--&nbsp;+&nbsp;--> &lt;br&nbsp;/></charinsert>\n</p></div>",
        "nocreatetext": "Na ovom je projektu ograničeno otvaranje novih stranica.\nMožete se vratiti i uređivati već postojeće stranice ili se [[Special:UserLogin|prijaviti ili otvoriti suradnički račun]].",
        "nocreate-loggedin": "Nemate ovlasti za stvaranje novih stranica.",
        "sectioneditnotsupported-title": "Uređivanje odjeljka nije podržano",
        "sectioneditnotsupported-text": "Uređivanje odjeljka nije podržano na ovoj stranici",
        "permissionserrors": "Pogrješka u pravima",
        "permissionserrorstext": "Nemate ovlasti za tu radnju iz sljedećih {{PLURAL:$1|razlog|razloga}}:",
-       "permissionserrorstext-withaction": "Nemate dopuštenje za $2, iz {{PLURAL:$1|razloga|razloga}}:",
+       "permissionserrorstext-withaction": "Nemate dopuštenje za $2, iz {{PLURAL:$1|navedenog|navedenih}} razloga:",
        "recreate-moveddeleted-warn": "'''Upozorenje: Ponovno stvarate stranicu koja je prethodno bila izbrisana.'''\n\nRazmotrite je li prikladno nastaviti s uređivanje ove stranice.\nZa Vašu informaciju slijedi evidencija brisanja i premještanja ove stranice:",
        "moveddeleted-notice": "Ova stranica je bila izbrisana.\nEvidencija brisanja i evidencija premještanja za ovu stranicu je prikazana niže.",
        "moveddeleted-notice-recent": "Žao nam je, ova stranica je izbrisana u prošla 24 sata. \nNiže je navedena evidencija brisanja i premještanja.",
        "group-bot": "Botovi",
        "group-sysop": "Administratori",
        "group-bureaucrat": "Birokrati",
-       "group-suppress": "Nadzornici",
+       "group-suppress": "Otajnici",
        "group-all": "(svi)",
        "group-user-member": "{{GENDER:$1|suradnik|suradnica}}",
        "group-autoconfirmed-member": "{{GENDER:$1|automatski potvrđen suradnik|automatski potvrđena suradnica}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator|administratorica}}",
        "group-bureaucrat-member": "{{GENDER:$1|birokrat|birokratica}}",
-       "group-suppress-member": "{{GENDER:$1|nadzornik|nadzornica}}",
+       "group-suppress-member": "{{GENDER:$1|otajnik|otajnica}}",
        "grouppage-user": "{{ns:project}}:Suradnici",
        "grouppage-autoconfirmed": "{{ns:project}}:automatski potvrđeni suradnici",
        "grouppage-bot": "{{ns:project}}:Botovi",
        "grouppage-sysop": "{{ns:project}}:Administratori",
        "grouppage-bureaucrat": "{{ns:project}}:Birokrati",
-       "grouppage-suppress": "{{ns:project}}:Nadzor",
+       "grouppage-suppress": "{{ns:project}}:Otajnost",
        "right-read": "Čitanje stranica",
        "right-edit": "Uređivanje stranica",
        "right-createpage": "Stvaranje stranica (stranica koje nisu razgovor)",
        "grant-group-email": "Pošalji e-mail",
        "grant-blockusers": "Blokiraj i odblokiraj korisnike",
        "grant-createaccount": "Otvori račune",
-       "grant-createeditmovepage": "Stvori, uredi i premjesti stranice",
+       "grant-createeditmovepage": "Stvaranje, uređivanje i premještanje stranica",
        "grant-editmyoptions": "Uređivanje vlastitih suradničkih postavki",
        "grant-editpage": "Uređivanje postojećih stranica",
        "grant-editprotected": "Uređivanje zaštićenih stranica",
        "grant-highvolume": "Uređivanja velikog opsega",
+       "grant-uploadeditmovefile": "Postavljanje, zamjena i premještanje datoteka",
+       "grant-uploadfile": "Postavljanje novih datoteka",
        "grant-basic": "Osnovna prava",
        "grant-viewdeleted": "Prikaz izbrisanih datoteka i stranica",
        "grant-viewmywatchlist": "Pregled popisa praćenih stranica",
        "withoutinterwiki-summary": "Sljedeće stranice nemaju poveznice na projekte na drugim jezicima:",
        "withoutinterwiki-legend": "Prefiks",
        "withoutinterwiki-submit": "Prikaži",
-       "fewestrevisions": "Članci s najmanje izmjena",
+       "fewestrevisions": "Stranice s najmanje izmjena",
        "nbytes": "$1 {{PLURAL:$1|bajt|bajta|bajtova}}",
        "ncategories": "$1 {{PLURAL:$1|kategorija|kategorije|kategorija}}",
        "ninterwikis": "$1 međuwiki {{PLURAL:$1|poveznica|poveznice|poveznica}}",
        "mostlinked": "Stranice na koje vodi najviše poveznica",
        "mostlinkedcategories": "Kategorije na koje vodi najviše poveznica",
        "mostlinkedtemplates": "Predlošci na koje vodi najviše poveznica",
-       "mostcategories": "Popis članaka po broju kategorija",
+       "mostcategories": "Popis stranica po broju kategorija",
        "mostimages": "Slike na koje vodi najviše poveznica",
        "mostinterwikis": "Stranice s najviše međuwiki poveznica",
-       "mostrevisions": "Popis članaka po broju uređivanja",
+       "mostrevisions": "Popis stranica po broju uređivanja",
        "prefixindex": "Sve stranice prema početku naslova",
        "prefixindex-namespace": "Sve stranice s predmetkom (imenski prostor $1)",
        "prefixindex-submit": "Prikaži",
        "sp-contributions-hideminor": "Sakrij manje izmjene",
        "sp-contributions-submit": "Traži",
        "whatlinkshere": "Što vodi ovamo",
-       "whatlinkshere-title": "Stranice koje vode na \"$1\"",
+       "whatlinkshere-title": "Stranice koje vode na »$1«",
        "whatlinkshere-page": "Stranica:",
        "linkshere": "Sljedeće stranice povezuju ovamo ([[:$1]]):",
        "nolinkshere": "Nijedna stranica ne vodi ovamo (tj. nema poveznica na stranicu [[:$1]]).",
index 2586a71..b679a47 100644 (file)
        "login": "Bejelentkezés",
        "login-security": "Személyazonosságod igazolása",
        "nav-login-createaccount": "Bejelentkezés / fiók létrehozása",
-       "userlogin": "Bejelentkezés / fiók létrehozása",
-       "userloginnocreate": "Bejelentkezés",
        "logout": "Kijelentkezés",
        "userlogout": "Kijelentkezés",
        "notloggedin": "Nem vagy bejelentkezve",
        "userlogin-noaccount": "Nem rendelkezel még felhasználói fiókkal?",
        "userlogin-joinproject": "Csatlakozz a(z) {{SITENAME}} wikihez",
-       "nologin": "Nem rendelkezel még felhasználói fiókkal? $1.",
-       "nologinlink": "Itt regisztrálhatsz",
        "createaccount": "Regisztráció",
-       "gotaccount": "Már van fiókod? $1.",
-       "gotaccountlink": "Bejelentkezés",
-       "userlogin-resetlink": "Elfelejtetted a bejelentkezési adataidat?",
        "userlogin-resetpassword-link": "Elfelejtetted a jelszavad?",
        "userlogin-helplink2": "Segítség a bejelentkezéshez",
        "userlogin-loggedin": "Már be vagy jelentkezve mint {{GENDER:$1|$1}}. Ha más néven akarsz belépni, alább megteheted.",
        "createaccountmail": "Átmeneti, véletlenszerű jelszó beállítása és kiküldése a megadott e-mail címre",
        "createaccountmail-help": "A jelszó megismerése nélkül készíthető valaki másnak fiók.",
        "createacct-realname": "Igazi neved (nem kötelező)",
-       "createaccountreason": "Indoklás:",
        "createacct-reason": "Indoklás",
        "createacct-reason-ph": "Miért hozol létre egy másik fiókot",
        "createacct-reason-help": "A fióklétrehozási naplóban megjelenő üzenet",
        "search-interwiki-caption": "Társlapok",
        "search-interwiki-default": "$1 találatok:",
        "search-interwiki-more": "(több)",
+       "search-interwiki-more-results": "további eredmények",
        "search-relatedarticle": "Kapcsolódó",
        "searchrelated": "kapcsolódó",
        "searchall": "mind",
        "editinguser": "<strong>[[User:$1|$1]]</strong> felhasználó jogainak megváltoztatása $2",
        "viewinguserrights": "<strong>[[User:$1|$1]]</strong> felhasználói csoportjainak megtekintése $2",
        "userrights-editusergroup": "Szerkesztőcsoportok módosítása",
-       "userrights-viewusergroup": "Felhasználói csoportok megtekintése",
+       "userrights-viewusergroup": "{{GENDER:$1|Felhasználói}} csoportok megtekintése",
        "saveusergroups": "{{GENDER:$1|Szerkesztőcsoportok}} mentése",
        "userrights-groupsmember": "Csoporttag:",
        "userrights-groupsmember-auto": "Alapértelmezetten tagja:",
        "rcfilters-invalid-filter": "Érvénytelen szűrő",
        "rcfilters-empty-filter": "Nincs aktív szűrő. Minden közreműködés látható.",
        "rcfilters-filterlist-title": "Szűrők",
+       "rcfilters-highlightmenu-title": "Válassz egy színt",
        "rcfilters-filterlist-noresults": "Nem található szűrő",
        "rcfilters-filter-registered-label": "Regisztrált",
        "rcfilters-filter-registered-description": "Bejelentkezett szerkesztők.",
        "rcfilters-filter-editsbyself-description": "Saját szerkesztések",
        "rcfilters-filter-editsbyother-label": "Mások szerkesztései",
        "rcfilters-filter-editsbyother-description": "Más felhasználók által végzett szerkesztések (nem sajátok)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Újoncok",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Kevesebb mint 10 szerkesztés és 4 nap aktivitás.",
-       "rcfilters-filter-userExpLevel-learner-label": "Tanulók",
-       "rcfilters-filter-userExpLevel-learner-description": "Több aktív nap és szerkesztés, mint az „újoncok”, de kevesebb, mint a „tapasztalt szerkesztők”.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Tapasztalt szerkesztők",
-       "rcfilters-filter-userExpLevel-experienced-description": "Több mint 30 nap aktivitás és 500 szerkesztés.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Újoncok",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Kevesebb mint 10 szerkesztés és 4 nap aktivitás.",
+       "rcfilters-filter-user-experience-level-learner-label": "Tanulók",
+       "rcfilters-filter-user-experience-level-learner-description": "Több aktív nap és szerkesztés, mint az „újoncok”, de kevesebb, mint a „tapasztalt szerkesztők”.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Tapasztalt szerkesztők",
+       "rcfilters-filter-user-experience-level-experienced-description": "Több mint 30 nap aktivitás és 500 szerkesztés.",
        "rcfilters-filtergroup-automated": "Automatikus szerkesztések",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Automatizált eszközök szerkesztései.",
        "rcfilters-filter-humans-label": "Ember (nem bot)",
        "rcfilters-filter-humans-description": "Emberi szerkesztők szerkesztései",
+       "rcfilters-filter-patrolled-label": "Ellenőrzött",
+       "rcfilters-filter-patrolled-description": "Ellenőrzöttnek jelölt szerkesztések",
+       "rcfilters-filter-unpatrolled-label": "Ellenőrizetlen",
+       "rcfilters-filter-unpatrolled-description": "Ellenőrzöttnek nem jelölt szerkesztések",
        "rcfilters-filter-minor-label": "Apró szerkesztések",
        "rcfilters-filter-minor-description": "Szerző által aprónak jelölt szerkesztések",
        "rcfilters-filter-major-label": "Nem apró szerkesztések",
        "editcomment": "A szerkesztési összefoglaló <em>$1</em> volt.",
        "revertpage": "Visszaállítottam a lap korábbi változatát: [[Special:Contributions/$2|$2]]  ([[User talk:$2|vita]]) szerkesztéséről [[User:$1|$1]] szerkesztésére",
        "revertpage-nouser": "Visszaállítottam a lap korábbi változatát (szerkesztőnév eltávolítva) szerkesztéséről [[User:$1|$1]] szerkesztésére",
-       "rollback-success": "$1 szerkesztéseit visszaállítottam $2 utolsó változatára.",
+       "rollback-success": "{{GENDER:$3|$1}} szerkesztéseit visszaállítottam {{GENDER:$4|$2}} utolsó változatára.",
        "rollback-success-notify": "$1 szerkesztései visszaállítva;\nhelyreállítva $2 utolsó változata. [$3 Változtatások megtekintése]",
        "sessionfailure-title": "Munkamenethiba",
        "sessionfailure": "Úgy látszik, hogy probléma van a bejelentkezési munkameneteddel;\nez a művelet a munkamenet eltérítése miatti óvatosságból megszakadt.\nKérjük, hogy nyomd meg a „vissza” gombot, és töltsd le újra az oldalt, ahonnan jöttél, majd próbáld újra.",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|frissítette}} a címkéket a(z) $3 lap $4 változatánál ({{PLURAL:$7|hozzáadva}}: $6; {{PLURAL:$9|eltávolítva}}: $8)",
        "logentry-tag-update-logentry": "$1 {{GENDER:$2|frissítette}} a címkéket a(z) $3 lap $5 napló bejegyzésénél ({{PLURAL:$7|hozzáadta}}: $6; {{PLURAL:$9|eltávolította}}: $8)",
        "rightsnone": "(semmi)",
-       "revdelete-summary": "a szerkesztési összefoglalóját",
        "rightslogentry-temporary-group": "$1 (ideiglenesen, a következő időpontig: $2)",
        "feedback-adding": "Visszajelzés elmentése...",
        "feedback-back": "Vissza",
index 789b53e..9ba13eb 100644 (file)
        "rcfilters-filter-editsbyself-description": "Suntingan oleh Anda",
        "rcfilters-filter-editsbyother-label": "Suntingan orang lain",
        "rcfilters-filter-editsbyother-description": "Suntingan dibuat oleh orang lain (bukan Anda)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Pendatang baru",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Kurang dari 10 suntingan dan aktivitas selama 4 hari.",
-       "rcfilters-filter-userExpLevel-learner-label": "Pelajar",
-       "rcfilters-filter-userExpLevel-experienced-label": "Pengguna berpengalaman",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Pendatang baru",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Kurang dari 10 suntingan dan aktivitas selama 4 hari.",
+       "rcfilters-filter-user-experience-level-learner-label": "Pelajar",
+       "rcfilters-filter-user-experience-level-experienced-label": "Pengguna berpengalaman",
        "rcfilters-filtergroup-automated": "Kontribusi otomatis",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Suntingan yang dibuat dengan perkakas terotomatisasi.",
index e8f90af..fbadb16 100644 (file)
@@ -37,7 +37,7 @@
        "tog-enotifminoredits": "Esuratannak pay para kadagiti bassit a panagurnos kadagiti panid ken papeles",
        "tog-enotifrevealaddr": "Iparang ti pagtaengan ti esuratko iti panagipakaaammo kadagiti esurat",
        "tog-shownumberswatching": "Ipakita ti bilang dagiti agbuybuya nga agar-aramat",
-       "tog-oldsig": "Ti adda a pirma:",
+       "tog-oldsig": "Ti adda a pirmam:",
        "tog-fancysig": "Tratuen ti pirma a kas wikitext (awanan iti automatiko a silpo)",
        "tog-uselivepreview": "Usaren ti agdama a panagipadas",
        "tog-forceeditsummary": "Pakaammuannak no sumrek iti blanko a pakabuklan ti panagurnos",
@@ -54,7 +54,7 @@
        "tog-showhiddencats": "Ipakita dagiti nailemmeng a kategoria",
        "tog-norollbackdiff": "Saan nga ipakita ti paggiddiatan kalpasan ti panagaramid ti panagisubli",
        "tog-useeditwarning": "Pakaunaannak no pumanawak iti maysa pagurnosan a panid nga addaan iti saan a naidulin a sinuksukatan",
-       "tog-prefershttps": "Kankanayon nga agusar ti natalged a koneksion no nakastrek",
+       "tog-prefershttps": "Kankanayon nga agusar iti natalged a koneksion bayat a nakastrek",
        "underline-always": "Kanayon",
        "underline-never": "Saan uray kaanoman",
        "underline-default": "Kudil wenno kasisigud a pagbasabasa",
        "newwindow": "(aglukat iti baro a tawa)",
        "cancel": "Ukasen",
        "moredotdotdot": "Adu pay...",
-       "morenotlisted": "Daytoy a listaan ket saan a kompleto.",
+       "morenotlisted": "Daytoy a listaan ket mabalin a saan a kompleto.",
        "mypage": "Panid",
        "mytalk": "Tungtungan",
        "anontalk": "Tungtungan",
        "searcharticle": "Inkan",
        "history": "Pakasaritaan ti panid",
        "history_short": "Pakasaritaan",
+       "history_small": "pakasaritaan",
        "updatedmarker": "napabaro sipud ti naudi nga isasarungkarko",
        "printableversion": "Bersion a maimaldit",
        "permalink": "Agnanayon a silpo",
        "talk": "Pagtungtungan",
        "views": "Dagiti pangkitaan",
        "toolbox": "Ramramit",
+       "tool-link-userrights": "Baliwan dagiti grupo ti {{GENDER:$1|agar-aramat}}",
+       "tool-link-userrights-readonly": "Kitaen dagiti grupo ti {{GENDER:$1|agar-aramat}}",
+       "tool-link-emailuser": "Esuratam daytoy nga {{GENDER:$1|agar-aramat}}",
        "userpage": "Kitaen ti panid ti agar-aramat",
        "projectpage": "Kitaen ti panid ti proyekto",
        "imagepage": "Kitaen ti panid ti papeles",
        "login": "Sumrek",
        "login-security": "Pasingkedan ti identidadmo",
        "nav-login-createaccount": "Sumrek / agpartuat iti pakabilangan",
-       "userlogin": "Sumrek / agpartuat iti pakabilangan",
-       "userloginnocreate": "Sumrek",
        "logout": "Rummuar",
        "userlogout": "Rummuar",
        "notloggedin": "Saan a nakastrek",
        "userlogin-noaccount": "Awan ti pakabilangam?",
        "userlogin-joinproject": "Tumipon iti {{SITENAME}}",
-       "nologin": "Awan pakabilangam? $1.",
-       "nologinlink": "Agpartuat iti pakabilangan",
        "createaccount": "Agpartuat iti pakabilangan",
-       "gotaccount": "Addaanka kadin iti pakabilangan? $1.",
-       "gotaccountlink": "Sumrek",
-       "userlogin-resetlink": "Nalipatam dagiti salaysay ti panagserrekmo?",
        "userlogin-resetpassword-link": "Nalipatam ti kontraseniasmo?",
        "userlogin-helplink2": "Tulong iti panagserrek",
        "userlogin-loggedin": "Nakastrekkan a kas ni {{GENDER:$1|$1}}.\nUsaren ti porma dita baba tapno sumrek a kas sabali nga agar-aramat.",
        "createaccountmail": "Agusar iti pugto a temporario a kontrasenias ken ipatulod iti naisangayan nga esurat a pagtaengan",
        "createaccountmail-help": "Mabalin a mausar a panagpartuat ti pakabilangan para iti sabali a tao a saan a makaammo iti kontrasenias.",
        "createacct-realname": "Pudno a nagan (pagpilian)",
-       "createaccountreason": "Rason:",
        "createacct-reason": "Rason",
        "createacct-reason-ph": "Apay nga agparpartuatka manen iti sabali a pakabilangan",
        "createacct-reason-help": "Ti mensahe a naipakita iti listaan iti panagpartuat ti pakabilangan",
        "selfredirect": "<strong>Ballaag:</strong> Ibawbaw-ingmo daytoy a panid iti isu met laeng a panid.\nMabalinmo nga innaganan ti kamali a puntaan para iti baw-ing, wenno mabalin nga ur-urnosem ti kamali a panid.\nNo pindutem manen ti \"{{int:savearticle}}\" , mapartuatto lattan ti baw-ing.",
        "missingcommenttext": "Pangngaasi nga agikabil ti komentario dita baba.",
        "missingcommentheader": "<strong>Palagip:</strong> Saanka pay a nakaited iti suheto para iti daytoy a komentario.\nNo pindutem manen ti \"{{int:savearticle}}\", maidulinto ti inurnosmo nga awan ti pakabuklanna.",
-       "summary-preview": "Naipadas a pakabuklan:",
-       "subject-preview": "Naipadas a suheto:",
+       "summary-preview": "Panangipadas ti pakabuklan ti panagurnos:",
+       "subject-preview": "Panangipadas ti suheto:",
        "previewerrortext": "Adda napasamak a maysa a biddut bayat a nagpadpadas kadagiti binawbaliwam.",
        "blockedtitle": "Naseraan ti agar-aramat",
        "blockedtext": "<strong>Naseraan ti naganmo nga agar-aramat wenno ti IP a pagtaengam.</strong>\n\nTi serra ket inaramid babaen ni $1. \nTi rason a naited ket <em>$2</em>.\n\n* Rugi ti serra: $8\n* Panagpaso ti serra: $6\n* Naikeddeng a serraanna: $7\n\nMabalinmo a kontaken ni $1 wenno sabali pay nga [[{{MediaWiki:Grouppage-sysop}}|administrador]] no kayatmo a maipalawag daytoy a panagserra.\nDimo mabalin nga aramaten ti ramit nga esuratan daytoy nga agar-aramat malaksid no adda napudno nga esurat a pagtaengan a nainaganan iti [[Special:Preferences|pakabilangan ti kakaykayatm]] ken no saanka a naparitan nga agaramat iti daytoy.\nTi agdama nga IP a pagtaengam ket $3, ti naserraan nga ID ket #$5. \nPangngaasi nga iramanmo amin dagiti salaysay dita ngato kadagiti ania man nga aramidem nga usisa.",
        "searchprofile-advanced-tooltip": "Agbirukka kadagiti naiduma a nagan ti espasio",
        "search-result-size": "$1 ({{PLURAL:$2|iti 1 a balikas|kadagiti $2 a balikas}})",
        "search-result-category-size": "{{PLURAL:$1|1 a kameng| dagiti $1 a kameng}} ({{PLURAL:$2|1 a subkategoria|dagiti $2 a subkategoria}}, {{PLURAL:$3|1 a papeles|dagiti $3 a papeles}})",
-       "search-redirect": "(baw-ing $1)",
+       "search-redirect": "(baw-ing manipud iti $1)",
        "search-section": "(seksion $1)",
        "search-category": "(kategoria $1)",
        "search-file-match": "(maipada ti linaon a papeles)",
        "search-interwiki-caption": "Dagiti kakabsat a proyekto",
        "search-interwiki-default": "Dagiti resulta manipud iti $1:",
        "search-interwiki-more": "(adu pay)",
+       "search-interwiki-more-results": "adu pay a resresulta",
        "search-relatedarticle": "Mainaig",
        "searchrelated": "mainaig",
        "searchall": "amin",
        "search-external": "Akinruar a panagbiruk",
        "searchdisabled": "Nabaldado ti panagbiruk iti {{SITENAME}}.\nMabalinmo itan ti agbiruk iti Google.\nLaglagipem laeng a dagiti pagsurotan a linaon ti {{SITENAME}} ket mabalin a baak.",
        "search-error": "Adda napasamak a biddut bayat nga agbirbiruk: $1",
+       "search-warning": "Adda napasamak a ballaag bayat nga agbirbiruk: $1",
        "preferences": "Kakaykayatan",
        "mypreferences": "Kakaykayatan",
        "prefs-edits": "Bilang dagiti inurnos:",
        "prefs-help-recentchangescount": "Daytoy ket mangiraman iti kaudian a balbaliw, dagiti pakasaritaan ti panid, ken dagiti listaan.",
        "prefs-help-watchlist-token2": "Daytoy ti sekreto a tulbek iti pakan ti web iti listaan ti bambantayam.\nTi sinoman a makaammo daytoy ket mabalinda a basaen ti listaan ti bambantayam, isu a saanmo nga ipabingay.\nNo masapulmo, [[Special:ResetTokens|mabalinmo nga isaad manen]].",
        "savedprefs": "Naidulinen dagiti kakaykayatam.",
-       "savedrights": "Naidulinen dagiti karbengan ti agar-aramat ni {{GENDER:$1|$1}}.",
+       "savedrights": "Naidulinen dagiti grupo ti agar-aramat ni {{GENDER:$1|$1}}.",
        "timezonelegend": "Sona ti oras:",
        "localtime": "Lokal nga oras:",
        "timezoneuseserverdefault": "Usaren ti kasisigud ti wiki ($1)",
        "youremail": "Esurat:",
        "username": "{{GENDER:$1|Nagan ti agar-aramat}}:",
        "prefs-memberingroups": "{{GENDER:$2|Kameng}} ti {{PLURAL:$1|a grupo|a grupgrupo}}:",
+       "group-membership-link-with-expiry": "$1 (aginggana intono $2)",
        "prefs-registration": "Oras a nagrehistro:",
        "yourrealname": "Pudno a nagan:",
        "yourlanguage": "Pagsasao:",
        "prefswarning-warning": "Nagaramikka kadagiti panagbalbaliw kadagiti kakaykayatam a saan pay a naidulin.\nNo panawan daytoy a panid a saan nga agpindut iti \"$1\" dagiti kakaykayatam ket saanto a mapabaro.",
        "prefs-tabs-navigation-hint": "Pakaammo: Mabalinmo nga usaren dagiti kanigid ken kanawan a tekla ti pana tapno madaliasat ti baetan dagiti etiketa iti listaan dagiti etiketa.",
        "userrights": "Panagtaripato kadagiti karbengan ti agar-aramat",
-       "userrights-lookup-user": "Agtaripato kadagiti grupo ti agar-aramat",
+       "userrights-lookup-user": "Agpili iti agar-aramat",
        "userrights-user-editname": "Mangiserrek iti nagan ti agar-aramat:",
-       "editusergroup": "Urnosen dagiti grupo ti {{GENDER:$1|agar-aramat}}",
+       "editusergroup": "Ikarga dagiti grupo ti agar-aramat",
        "editinguser": "Suksukatan ti karbengan ni {{GENDER:$1|agar-aramat}} <strong>[[User:$1|$1]]</strong> $2",
-       "userrights-editusergroup": "Urnosen dagiti grupo ti agar-aramat",
+       "userrights-editusergroup": "Urnosen dagiti grupo ti {{GENDER:$1|agar-aramat}}",
+       "userrights-viewusergroup": "Kitaen dagiti grupo ti {{GENDER:$1|agar-aramat}}",
        "saveusergroups": "Idulin dagiti grupo ti {{GENDER:$1|agar-aramat}}",
        "userrights-groupsmember": "Kameng ti:",
        "userrights-groupsmember-auto": "Napudno a kameng ti:",
        "userrights-nodatabase": "Awan ti database a $1 wenno saan a lokal.",
        "userrights-changeable-col": "Dagiti grupo a mabalinmo a baliwan",
        "userrights-unchangeable-col": "Dagiti grupo a dimo mabalin a baliwan",
+       "userrights-expiry-current": "Agpaso $1",
+       "userrights-expiry-none": "Saan nga agpaso",
+       "userrights-expiry": "Agpaso:",
+       "userrights-expiry-existing": "Ti adda a panagpaso ti oras: $3, $2",
+       "userrights-expiry-othertime": "Sabali nga oras:",
+       "userrights-invalid-expiry": "Imbalido ti oras a panagpaso para iti grupo \"$1\".",
+       "userrights-expiry-in-past": "Napalabasen ti oras a panagpaso para iti grupo \"$1\".",
        "userrights-conflict": "Suppiat dagiti panagbaliw kadagiti karbengan ti agar-aramat! Pangngaasi nga irepasom ken pasingkedam dagiti sinuksukatam.",
        "group": "Grupo:",
        "group-user": "Dagiti agar-aramat",
        "action-upload_by_url": "agikarga iti daytoy a papeles manipud iti URL",
        "action-writeapi": "agusar iti panagsurat ti API",
        "action-delete": "agikkat iti daytoy a panid",
-       "action-deleterevision": "agikkat iti daytoy a rebision",
-       "action-deletedhistory": "agkita kadagiti naikkat a pakasaritaan iti daytoy a panid",
+       "action-deleterevision": "agikkat kadagiti rebision",
+       "action-deletedhistory": "agkita iti naikkat a pakasaritaan ti panid",
+       "action-deletedtext": "agkita iti naikkat a rebision ti teksto",
        "action-browsearchive": "agbiruk kadagiti naikkat a panid",
-       "action-undelete": "agisubli iti pannakaikkat iti daytoy a panid",
-       "action-suppressrevision": "agrepaso ken agisubli iti daytoy a nailemmeng a rebision",
+       "action-undelete": "agisubli iti pannakaikkat kadagiti panid",
+       "action-suppressrevision": "agrepaso ken agisubli kadagiti nailemmeng a rebision",
        "action-suppressionlog": "agkita iti daytoy a pribado a listaan",
        "action-block": "agserra iti daytoy nga agar-aramat manipud iti panagurnos",
        "action-protect": "mangsukat kadagiti lessaad ti salaknib para iti daytoy a panid",
        "action-userrights-interwiki": "agurnos kadagiti karbengan ti agar-aramat dagiti agar-aramat kadagiti sabali a wiki",
        "action-siteadmin": "mangkandado wenno manglukat ti database",
        "action-sendemail": "agipatulod kadagiti esurat",
+       "action-editmyoptions": "urnosem dagiti kakaykayatam",
        "action-editmywatchlist": "agurnos iti bukodmo a listaan ti bambantayan",
        "action-viewmywatchlist": "agkita iti bukodmo a listaan ti bambantayan",
        "action-viewmyprivateinfo": "agkita iti bukodmo a pribado a pakaammo",
        "recentchanges-legend-heading": "<strong>Leyenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (kitaen pay ti [[Special:NewPages|listaan ti baro a pampanid]])",
        "recentchanges-submit": "Ipakita",
+       "rcfilters-activefilters": "Dagiti aktibo a sagat",
+       "rcfilters-restore-default-filters": "Isubli dagiti kasisigud a sagat",
+       "rcfilters-clear-all-filters": "Dalusan amin dagiti sagat",
+       "rcfilters-search-placeholder": "Sagaten ti kaudian a balbaliw (agbasabasa wenno mangrugi nga agmakinilia)",
+       "rcfilters-invalid-filter": "Imbalido a sagat",
+       "rcfilters-empty-filter": "Awan dagiti aktibo a sagat. Naipakita amin dagiti kontribusion.",
+       "rcfilters-filterlist-title": "Dagiti sagat",
+       "rcfilters-highlightmenu-title": "Agpili iti maris",
+       "rcfilters-filterlist-noresults": "Awan dagiti nabirukan a sagat",
+       "rcfilters-filter-registered-label": "Nakarehistro",
+       "rcfilters-filter-registered-description": "Dagiti nakastrek nga editor.",
+       "rcfilters-filter-unregistered-label": "Saan a nakarehistro",
+       "rcfilters-filter-unregistered-description": "Dagiti editor a saan a nakastrek.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Dagiti agdadamo",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Basbassit ngem 10 nga inur-urnos ken 4 nga aldaw iti aktibidad.",
+       "rcfilters-filter-user-experience-level-learner-label": "Dagiti agad-adal",
+       "rcfilters-filter-user-experience-level-learner-description": "Ad-adu nga al-aldaw iti aktibidad ken inur-urnos ngem \"Dagiti agdadamo\" ngem basbassit ngem \"Dagiti nasanay nga agar-aramat\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Dagiti nasanay nga agar-aramat",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ad-adu ngem 30 nga aldaw iti aktibidad ken 500 nga inur-urnos.",
+       "rcfilters-filtergroup-automated": "Dagiti automado a kontribusion",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-bots-description": "Dagiti inurnos nga inaramid babaen ti automado a ramramit.",
+       "rcfilters-filter-humans-label": "Tao (saan a bot)",
+       "rcfilters-filter-humans-description": "Dagiti inurnos nga inaramid babaen dagiti editor a tao.",
+       "rcfilters-filtergroup-reviewstatus": "Irepaso ti kasasaad",
+       "rcfilters-filter-patrolled-label": "Napatruliaan",
+       "rcfilters-filter-patrolled-description": "Dagiti inurnos a namarkaan a kas napatruliaan.",
+       "rcfilters-filter-unpatrolled-label": "Di napatruliaan",
+       "rcfilters-filter-unpatrolled-description": "Dagiti inurnos a saan a namarkaan a kas napatruliaan.",
+       "rcfilters-filtergroup-changetype": "Kita ti panagbaliw",
        "rcnotefrom": "Dita baba ket {{PLURAL:$5|ti sinukatan|dagiti sinukatan}} manipud idi <strong>$3, $4</strong> (aginggana iti <strong>$1</strong> a naipakita).",
        "rclistfrom": "Ipakita dagiti kabarbaro a sinukatan a mangrugi manipud idi $2, $3",
        "rcshowhideminor": "$1 dagiti bassit a panagurnos",
        "upload-dialog-disabled": "Nabaldado iti daytoy a wiki dagiti panangikarga ti papeles iti daytoy a dialogo.",
        "upload-dialog-title": "Agikarga iti papeles",
        "upload-dialog-button-cancel": "Ukasen",
+       "upload-dialog-button-back": "Agsubli",
        "upload-dialog-button-done": "Nalpasen",
        "upload-dialog-button-save": "Idulin",
        "upload-dialog-button-upload": "Agikarga",
        "apisandbox-results-fixtoken-fail": "Napaay ti panagala iti tangdan ti \"$1\".",
        "apisandbox-alert-page": "Saan nga umiso dagiti pagikabilan iti daytoy a panid.",
        "apisandbox-alert-field": "Saan nga umiso ti pateg iti daytoy a pagikabilan.",
+       "apisandbox-continue": "Agtuloy",
+       "apisandbox-continue-clear": "Dalusan",
+       "apisandbox-multivalue-all-namespaces": "$1 (Amin a nagan ti espasio)",
+       "apisandbox-multivalue-all-values": "$1 (Amin a patpateg)",
        "booksources": "Dagiti taudan ti libro",
        "booksources-search-legend": "Agbiruk para kadagiti taudan ti libro",
        "booksources-search": "Biruken",
        "booksources-text": "Dita baba ket listaan dagiti silpo ti sabali a sitio nga aglaklako ti baro ken saan a nausar a liblibro, ken mabalin nga addaan pay iti adu a pakaammo a maipanggep kadagiti libro a birbirukem:",
        "booksources-invalid-isbn": "Ti naited nga ISBN ket kasla saan nga umisu; kitaen dagiti biddut ti panagtulad manipud ti kasisigud a taudan.",
+       "magiclink-tracking-rfc": "Pampanid nga agus-usar kadagiti salamangka a silpo ti RFC",
+       "magiclink-tracking-rfc-desc": "Daytoy a panid ket agus-usar kadagiti salamangka a silpo ti RFC. Kitaen ti [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] no kasano iti mangiyakar.",
+       "magiclink-tracking-pmid": "Pampanid nga agus-usar kadagiti salamangka a silpo ti PMID",
+       "magiclink-tracking-pmid-desc": "Daytoy a panid ket agus-usar kadagiti salamangka a silpo ti PMID. Kitaen ti [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] no kasano iti mangiyakar.",
+       "magiclink-tracking-isbn": "Pampanid nga agus-usar kadagiti salamangka a silpo ti ISBN",
+       "magiclink-tracking-isbn-desc": "Daytoy a panid ket agus-usar kadagiti salamangka a silpo ti ISBN. Kitaen ti [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] no kasano iti mangiyakar.",
        "specialloguserlabel": "Nangitungpal:",
        "speciallogtitlelabel": "Puntaan (titulo wenno {{ns:user}}:nagan ti agar-aramat para iti agar-aramat):",
        "log": "Dagiti listaan",
        "activeusers-intro": "Daytoy ti listaan dagiti agar-aramat nga adda inar-aramid iti kaunegan dagiti napalabas a $1 {{PLURAL:$1|nga aldaw|nga al-aldaw}}.",
        "activeusers-count": "$1 a {{PLURAL:$1|tignay|tigtignay}} iti napalabas {{PLURAL:$3|nga aldaw|a $3 nga al-aldaw}}",
        "activeusers-from": "Iparang dagiti agar-aramat a mangrugi iti:",
+       "activeusers-groups": "Iparang dagiti agar-aramat a maitagikua kadagiti grupo:",
+       "activeusers-excludegroups": "Ilaksid dagiti agar-aramat a maitagikua kadagiti grupo:",
        "activeusers-noresult": "Awan ti nasarakan nga agar-aramat.",
        "activeusers-submit": "Ipakita dagiti aktibo nga agar-aramat",
        "listgrouprights": "Dagiti karbengan ti grupo ti agar-aramat",
        "pageinfo-length": "Kaatiddog ti panid (kadagiti byte)",
        "pageinfo-article-id": "ID ti panid",
        "pageinfo-language": "Pagsasao ti naglaon a panid",
+       "pageinfo-language-change": "baliwan",
        "pageinfo-content-model": "Modelo ti linaon ti panid",
        "pageinfo-content-model-change": "baliwan",
        "pageinfo-robot-policy": "Panagpasurot babaen dagiti robot",
        "pageinfo-category-pages": "Bilang dagiti panid",
        "pageinfo-category-subcats": "Bilang dagiti subkategoria",
        "pageinfo-category-files": "Bilang dagiti papeles",
+       "pageinfo-user-id": "ID ti agar-aramat",
        "markaspatrolleddiff": "Markaan a kas napatruliaan",
        "markaspatrolledtext": "Markaan daytoy a panid a kas napatruliaan",
        "markaspatrolledtext-file": "Markaan daytoy a bersion ti papeles a kas napatruliaan",
        "patrol-log-header": "Daytoy ket listaan dagiti napatruliaan a rebision.",
        "log-show-hide-patrol": "$1 listaan ti napatruliaan",
        "log-show-hide-tag": "$1 ti listaan ti etiketa",
+       "confirm-markpatrolled-button": "Sige",
+       "confirm-markpatrolled-top": "Markaan ti rebision $3 iti $2 a kas napatruliaan?",
        "deletedrevision": "Naikkat a daan a rebision ti $1",
        "filedeleteerror-short": "Biddut ti panakaikkat ti papeles: $1",
        "filedeleteerror-long": "Adda nasarakan a biddut idi agikikkat ti papeles:\n\n$1",
        "logentry-managetags-create": "{{GENDER:$2|Nagpartuat}} ni $1 ti etiketa ti \"$4\"",
        "log-name-tag": "Listaan ti etiketa",
        "rightsnone": "(awan)",
-       "revdelete-summary": "Pakabuklan ti inurnos",
        "feedback-adding": "Agnaynayon ti feedback iti panid...",
        "feedback-back": "Agsubli",
        "feedback-bugcheck": "Nasayaaten! Kitaem tapno saan a dagiti adda idin a [$1 nga ammo a parparikut].",
        "feedback-thanks": "Agyaman! Ti feedbackmo ket naipablaak iti panid \"[$2 $1]\".",
        "feedback-thanks-title": "Agyamanak!",
        "feedback-useragent": "Ahente ti agar-aramat:",
-       "searchsuggest-search": "Biruken",
+       "searchsuggest-search": "Agbiruk iti {{SITENAME}}",
        "searchsuggest-containing": "naglaon ti...",
        "api-error-badtoken": "Akin-uneg a biddut: Dakes a tandaan.",
        "api-error-emptypage": "Agparprtuat ti baro, dagiti awan ti linaon a panid ket saan a maipalubos.",
        "pagelang-language": "Pagsasao",
        "pagelang-use-default": "Usaren ti kasisigud a pagsasao",
        "pagelang-select-lang": "Agpili iti pagsasao",
+       "pagelang-reason": "Rason",
        "pagelang-submit": "Ited",
        "right-pagelang": "Baliwan ti pagsasao ti panid",
        "action-pagelang": "baliwan ti pagsasao ti panid",
        "mw-widgets-dateinput-no-date": "Awan ti napili a petsa",
        "mw-widgets-dateinput-placeholder-day": "TTTT-BB-AA",
        "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
+       "mw-widgets-mediasearch-input-placeholder": "Agbiruk para iti midia",
+       "mw-widgets-mediasearch-noresults": "Awan dagiti nabirukan a resulta.",
        "mw-widgets-titleinput-description-new-page": "awan pay ti panid",
        "mw-widgets-titleinput-description-redirect": "ibaw-ing iti $1",
+       "mw-widgets-categoryselector-add-category-placeholder": "Agnayon iti kategoria...",
+       "mw-widgets-usersmultiselect-placeholder": "Agnayon pay iti adu...",
        "sessionmanager-tie": "Saan a mabalin nga itipon dagiti nadumaduma kita ti kiddaw ti pammasingked: $1.",
        "sessionprovider-generic": "Dagiti sesion ti $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "dagiti sesion a naibatay iti galieta",
        "log-action-filter-block-block": "Serra",
        "log-action-filter-block-reblock": "Panagbaliw ti serra",
        "log-action-filter-block-unblock": "Ikkaten ti serra",
-       "log-action-filter-contentmodel-change": "Panagbaliw ti Contentmodel",
-       "log-action-filter-contentmodel-new": "Panagpartuat ti panid iti saan a pagalagadan a Contentmodel",
+       "log-action-filter-contentmodel-change": "Panagbaliw ti modelo ti linaon",
+       "log-action-filter-contentmodel-new": "Panagpartuat ti panid iti saan a kasisigud a modelo ti linaon",
        "log-action-filter-delete-delete": "Panagikkat ti panid",
        "log-action-filter-delete-restore": "Panangisubli ti panagikkat ti panid",
        "log-action-filter-delete-event": "Panagikkat ti listaan",
        "authmanager-authn-autocreate-failed": "Napaay ti automatiko a panagpartuat iti lokal a pakabilangan: $1",
        "authmanager-change-not-supported": "Dagiti naited a kredensial ket saan a mabaliwan, gapu ta awan ti mangusar kaniada.",
        "authmanager-create-disabled": "Nabaldado ti panagpartuat ti pakabilangan.",
-       "authmanager-create-from-login": "Tapno mapartuat ti pakabilangam, pangngaasi a punnuen dagiti pagikabilan dita baba.",
+       "authmanager-create-from-login": "Tapno mapartuat ti pakabilangam, pangngaasi a punnuen dagiti pagikabilan.",
        "authmanager-create-not-in-progress": "Saan nga agprogprogreso ti panagpartuat ti pakabilangan wenno napukaw ti datos ti sesion. Pangngaasi a mangrugi manen iti pagrugian.",
        "authmanager-create-no-primary": "Dagiti naited a kredensial ket saan a mabalin a mausar para iti panagpartuat ti pakabilangan.",
        "authmanager-link-no-primary": "Dagiti naited a kredensial ket saan a mabalin a mausar para iti panangisilpo ti pakabilangan.",
        "unlinkaccounts-success": "Ti pakabilangan ket naikkat iti pannakaisilpo.",
        "authenticationdatachange-ignored": "Saan a natengngel ti panagbaliw ti datos ti pammasingked. Mabalin nga awan ti nakompigura a mangited?",
        "userjsispublic": "Pangngaasi a laglagipen: Dagiti subpanid ti JavaScript ket nasken a saan nga aglaon iti datos a nailemed gapu ta makita dagitoy babaen dagiti sabali nga agar-aramat.",
-       "usercssispublic": "Pangngaasi a laglagipen: Dagiti subpanid ti CSS ket nasken a saan nga aglaon iti datos a nailemed gapu ta makita dagitoy babaen dagiti sabali nga agar-aramat."
+       "usercssispublic": "Pangngaasi a laglagipen: Dagiti subpanid ti CSS ket nasken a saan nga aglaon iti datos a nailemed gapu ta makita dagitoy babaen dagiti sabali nga agar-aramat.",
+       "restrictionsfield-badip": "Imbalido nga adres ti IP wenno sakup: $1",
+       "restrictionsfield-label": "Dagiti mapalubosan a sakup ti IP:",
+       "revid": "rebision $1",
+       "pageid": "ID ti panid $1"
 }
index 9c70b14..b701fcf 100644 (file)
        "rcfilters-filter-registered-label": "Registrato",
        "rcfilters-filtergroup-authorship": "Modifica paternità",
        "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-patrolled-label": "Verificate",
+       "rcfilters-filter-patrolled-description": "Modifiche contrassegnate come verificate.",
+       "rcfilters-filter-unpatrolled-label": "Non verificate",
+       "rcfilters-filter-unpatrolled-description": "Modifiche non contrassegnate come verificate.",
        "rcfilters-filtergroup-significance": "Significato",
        "rcfilters-filter-minor-label": "Modifiche minori",
        "rcfilters-filter-major-label": "Modifiche non minori",
index b8f0d75..3f7fd96 100644 (file)
        "feedback-thanks": "გმადლობთ! თქვენი შეფასება განთავსებულია „[$2 $1]“ გვერდზე.",
        "feedback-thanks-title": "გმადლობთ!",
        "feedback-useragent": "მომხმარებლის აგენტი:",
-       "searchsuggest-search": "ძიება {{grammar:prepositional|{{SITENAME}}}}-ში",
+       "searchsuggest-search": "ძიება {{grammar:prepositional|{{SITENAME}}}}ში",
        "searchsuggest-containing": "შეიცავს...",
        "api-error-badtoken": "შიდა შეცდომა: ცუდი ტოკენი.",
        "api-error-emptypage": "ახალი, ცარიელი გვერდების შექმნა აკრძალულია.",
index f99455d..b6a9ad9 100644 (file)
        "rcfilters-filterlist-feedbacklink": "새로운 (베타) 필터에 대한 의견을 주세요",
        "rcfilters-highlightbutton-title": "결과 강조",
        "rcfilters-highlightmenu-title": "색 선택",
+       "rcfilters-highlightmenu-help": "이 속성을 강조할 색을 선택하십시오",
        "rcfilters-filterlist-noresults": "필터를 찾을 수 없습니다",
        "rcfilters-filtergroup-registration": "사용자 등록",
        "rcfilters-filter-registered-label": "등록됨",
        "rcfilters-filter-editsbyother-label": "다른 사용자의 편집",
        "rcfilters-filter-editsbyother-description": "다른 사용자에 의한 편집 (당신의 편집이 아님).",
        "rcfilters-filtergroup-userExpLevel": "경험 수준 (등록된 사용자 전용)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "신규 사용자",
-       "rcfilters-filter-userExpLevel-newcomer-description": "10회 미만의 편집 및 4일 미만의 활동.",
-       "rcfilters-filter-userExpLevel-learner-label": "학습자",
-       "rcfilters-filter-userExpLevel-learner-description": "\"신규 사용자\" 보다 활동일 및 편집 수가 더 많지만 \"능숙한 사용자\" 보다는 적습니다.",
-       "rcfilters-filter-userExpLevel-experienced-label": "능숙한 사용자",
-       "rcfilters-filter-userExpLevel-experienced-description": "30일 이상의 활동 및 500개 이상의 편집.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "이 필터는 등록 사용자만 찾기 때문에 비활성화되어 있으므로 \"미등록\" 필터는 효과를 취소합니다.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "\"미등록\" 필터는 하나 이상의 경험 필터와 충돌합니다. 경험 필터는 등록된 사용자만 찾습니다. 충돌되는 필터는 위에서 비활성화된 것으로 표시됩니다.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "신규 사용자",
+       "rcfilters-filter-user-experience-level-newcomer-description": "10회 미만의 편집 및 4일 미만의 활동.",
+       "rcfilters-filter-user-experience-level-learner-label": "학습자",
+       "rcfilters-filter-user-experience-level-learner-description": "\"신규 사용자\" 보다 활동일 및 편집 수가 더 많지만 \"능숙한 사용자\" 보다는 적습니다.",
+       "rcfilters-filter-user-experience-level-experienced-label": "능숙한 사용자",
+       "rcfilters-filter-user-experience-level-experienced-description": "30일 이상의 활동 및 500개 이상의 편집.",
        "rcfilters-filtergroup-automated": "자동으로 된 기여",
        "rcfilters-filter-bots-label": "봇",
        "rcfilters-filter-bots-description": "자동 도구를 이용한 편집.",
index 32af23f..d572833 100644 (file)
        "login": "Aloggen",
        "login-security": "Iwwerpréift Är Idnetitéit",
        "nav-login-createaccount": "Aloggen / Benotzerkont uleeën",
-       "userlogin": "Aloggen / Benotzerkont uleeën",
-       "userloginnocreate": "Umellen",
        "logout": "Ofmellen",
        "userlogout": "Ausloggen",
        "notloggedin": "Net ageloggt",
        "userlogin-noaccount": "Hutt Dir kee Benotzerkont?",
        "userlogin-joinproject": "Maacht mat bei {{SITENAME}}",
-       "nologin": "Hutt Dir kee Benotzerkont? $1.",
-       "nologinlink": "Neie Benotzerkont maachen",
        "createaccount": "Neie Kont opmaachen",
-       "gotaccount": "Dir hutt schonn e Benotzerkont? '''$1'''.",
-       "gotaccountlink": "Umellen",
-       "userlogin-resetlink": "Hutt Dir d'Detailer vun Ärem Login vergiess?",
        "userlogin-resetpassword-link": "Hutt Dir Äert Passwuert vergiess?",
        "userlogin-helplink2": "Hëllef beim Aloggen",
        "userlogin-loggedin": "Dir sidd schonn als {{GENDER:$1|$1}} ageloggt.\nBenotzt de Formulaire hei drënner fir Iech als een anere Benotzer anzeloggen.",
        "createaccountmail": "En temporäert zoufällegt Passwuert benotzen an et per E-Mail un déi spezifizéiert E-Mailadress schécken",
        "createaccountmail-help": "Ka benotzt gi fir e Benotzerkont fir eng aner Persoun unzeleeën ouni d'Passwuert gewuer ze ginn.",
        "createacct-realname": "Richtegen Numm (fakultativ)",
-       "createaccountreason": "Grond:",
        "createacct-reason": "Grond",
        "createacct-reason-ph": "Fir wat Dir een anere Benotzerkonnt uleet",
        "createacct-reason-help": "Message deen am Logbuch vun den ugeluechte Benotzerkonte gewise gëtt",
        "rcfilters-search-placeholder": "Rezent Ännerunge filteren (duerchsichen oder ufänke mat tippen)",
        "rcfilters-invalid-filter": "Net valabele Filter",
        "rcfilters-filterlist-title": "Filteren",
+       "rcfilters-highlightbutton-title": "Resultater ervirhiewen",
        "rcfilters-highlightmenu-title": "Eng Faarf eraussichen",
        "rcfilters-filterlist-noresults": "Keng Filtere fonnt",
+       "rcfilters-filter-unregistered-description": "Auteuren déi net ageloggt sinn.",
        "rcfilters-filter-editsbyself-label": "Är eegen Ännerungen",
        "rcfilters-filter-editsbyself-description": "Ännerunge vun Iech.",
        "rcfilters-filter-editsbyother-label": "Ännerunge vun Aneren",
        "rcfilters-filter-editsbyother-description": "Ännerunge vun anere Benotzer (net vun Iech).",
        "rcfilters-filtergroup-userExpLevel": "Niveau vun der Erfahrung (just fir registréiert Benotzer)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Neier",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Manner wéi 10 Ännerungen a manner wéi 4 Deeg Aktivitéit.",
-       "rcfilters-filter-userExpLevel-learner-label": "Ufänger",
-       "rcfilters-filter-userExpLevel-learner-description": "Aktivitéit vu méi Deeg a méi Ännerunge wéi ''Nei Benotzer'' awer manner wéi ''Erfuere Benotzer''.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Erfuere Benotzer.",
-       "rcfilters-filter-userExpLevel-experienced-description": "Méi wéi 30 Deeg Aktivitéit a méi wéi 500 Ännerungen.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Neier",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Manner wéi 10 Ännerungen a manner wéi 4 Deeg Aktivitéit.",
+       "rcfilters-filter-user-experience-level-learner-label": "Ufänger",
+       "rcfilters-filter-user-experience-level-learner-description": "Aktivitéit vu méi Deeg a méi Ännerunge wéi ''Nei Benotzer'' awer manner wéi ''Erfuere Benotzer''.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Erfuere Benotzer.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Méi wéi 30 Deeg Aktivitéit a méi wéi 500 Ännerungen.",
+       "rcfilters-filter-patrolled-label": "Nogekuckt",
+       "rcfilters-filter-patrolled-description": "Ännerungen déi als nogekuckt markéiert sinn.",
+       "rcfilters-filter-unpatrolled-label": "Net nogekuckt",
+       "rcfilters-filter-unpatrolled-description": "Ännerungen déi net als nogekuckt markéiert sinn.",
        "rcfilters-filter-minor-label": "Kleng Ännerungen",
        "rcfilters-filter-major-label": "Keng kleng Ännerungen",
        "rcfilters-filter-pageedits-label": "Säitenännerungen",
        "logentry-upload-revert": "$1 huet $3 {{GENDER:$2|eropgelueden}}",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2|huet}} d'{{PLURAL:$7|Markéierung|Markéierunge(n)}} $6 op d'Versioun $4 vun der Säit $3 dobäigesat",
        "rightsnone": "(keen)",
-       "revdelete-summary": "Resumé änneren",
        "rightslogentry-temporary-group": "$1 (temporär, bis $2)",
        "feedback-adding": "Feedback gëtt bei d'Säit derbäigesat...",
        "feedback-back": "Zréck",
index 082f525..dfe50d3 100644 (file)
        "rcfilters-filter-editsbyother-label": "Fiovana nataon'ny hafa",
        "rcfilters-filter-editsbyother-description": "Fiovana noforonin'ny mpikambana hafa (tsy ianao).",
        "rcfilters-filtergroup-userExpLevel": "Lentan'ny traikefa (ho an'ireo mpikambana nisoratra anarana ihany)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Vao tonga",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Latsaky ny fiovana folo ary latsaky ny 4 andro niasana",
-       "rcfilters-filter-userExpLevel-learner-label": "Mpianatra",
-       "rcfilters-filter-userExpLevel-learner-description": "Efa betsaka andro nandraisana anjara ary nanova im-betsaka nohon'ny \"Vao tonga\" fa mbola kely nohon'ny \"Manan-traikefa\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Mpikambana manan-traikefa",
-       "rcfilters-filter-userExpLevel-experienced-description": "Mahery ny 30 andro nandraisana anjara ary fiovana mahery ny 500.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Vao tonga",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Latsaky ny fiovana folo ary latsaky ny 4 andro niasana",
+       "rcfilters-filter-user-experience-level-learner-label": "Mpianatra",
+       "rcfilters-filter-user-experience-level-learner-description": "Efa betsaka andro nandraisana anjara ary nanova im-betsaka nohon'ny \"Vao tonga\" fa mbola kely nohon'ny \"Manan-traikefa\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Mpikambana manan-traikefa",
+       "rcfilters-filter-user-experience-level-experienced-description": "Mahery ny 30 andro nandraisana anjara ary fiovana mahery ny 500.",
        "rcfilters-filtergroup-automated": "Fandraisan'anjara alefa ho azy",
        "rcfilters-filter-bots-label": "Rôbô",
        "rcfilters-filter-bots-description": "Fiovana nataon'ny rôbô aotômatika.",
index fd8922e..42841f0 100644 (file)
        "rcfilters-filter-registered-description": "Најавени уредници.",
        "rcfilters-filter-unregistered-label": "Нерегистрирани",
        "rcfilters-filter-unregistered-description": "Уредници кои не се најавени.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Филтерот „Нерегистрирани“ е неактивен бидејќи неговата работа се поништува од {{PLURAL:$2|следниов филтер|следниве филтри}} за искуство, {{PLURAL:$2|кој наоѓа|кои наоѓаат}} само регистрирани корисници: $1",
        "rcfilters-filtergroup-authorship": "Уреди авторство",
        "rcfilters-filter-editsbyself-label": "Ваши сопствени уредувања",
        "rcfilters-filter-editsbyself-description": "Ваши уредувања.",
        "rcfilters-filter-editsbyother-label": "Туѓи уредувања",
        "rcfilters-filter-editsbyother-description": "Уредувања направени од други корисници (не од вас).",
        "rcfilters-filtergroup-userExpLevel": "Корисничка искусност (само за регистрирани)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Новодојденци",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Помалку од 10 уредувања и 4 дена активност.",
-       "rcfilters-filter-userExpLevel-learner-label": "Ученици",
-       "rcfilters-filter-userExpLevel-learner-description": "Повеќе денови активност од „новодојденците“, но помалку од „искусните корисници“.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Искусни корисници",
-       "rcfilters-filter-userExpLevel-experienced-description": "Повеќе од 30 дена активност и 500 уредувања.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Филтерот е неактивен бидејќи наоѓа само регистрирани корисници, па филтерот „Нерегистрирани“ му ја поништува работата.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Филтерот „Нерегистрирани“ е во спротиставеност со еден или повеќе од филтрите за искуство. Тие филтри наоѓаат само регистрирани корисници. Спротиставените филтри погоре се означени како неактивни.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Новодојденци",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Помалку од 10 уредувања и 4 дена активност.",
+       "rcfilters-filter-user-experience-level-learner-label": "Ученици",
+       "rcfilters-filter-user-experience-level-learner-description": "Повеќе денови активност од „новодојденците“, но помалку од „искусните корисници“.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Искусни корисници",
+       "rcfilters-filter-user-experience-level-experienced-description": "Повеќе од 30 дена активност и 500 уредувања.",
        "rcfilters-filtergroup-automated": "Автоматизирани придонеси",
        "rcfilters-filter-bots-label": "Ботовски",
        "rcfilters-filter-bots-description": "Уредувања со автоматизирани алатки.",
        "rcfilters-filter-humans-label": "Човечки (неботовски)",
        "rcfilters-filter-humans-description": "Уредувања направени од човечки уредници.",
+       "rcfilters-filtergroup-reviewstatus": "Провереност",
+       "rcfilters-filter-patrolled-label": "Проверено",
+       "rcfilters-filter-patrolled-description": "Уредувања кои се означени како проверени.",
+       "rcfilters-filter-unpatrolled-label": "Непроверено",
+       "rcfilters-filter-unpatrolled-description": "Уредувања кои не се означени како проверени.",
        "rcfilters-filtergroup-significance": "Значајност",
        "rcfilters-filter-minor-label": "Ситни уредувања",
        "rcfilters-filter-minor-description": "Уредувања кои авторот ги означил како ситни.",
index ef4d79c..858a91d 100644 (file)
        "externaldberror": "這可能是資料庫驗證錯誤,抑是無允准你改外部的口座。",
        "login": "Teng-ji̍p",
        "nav-login-createaccount": "Teng-ji̍p / khui sin kháu-chō",
-       "userlogin": "Teng-ji̍p / khui sin kháu-chō",
-       "userloginnocreate": "Teng-ji̍p",
        "logout": "Teng-chhut",
        "userlogout": "Teng-chhut",
        "notloggedin": "Bô teng-ji̍p",
        "userlogin-noaccount": "Káⁿ bô kháu-chō?",
        "userlogin-joinproject": "Ka-ji̍p {{SITENAME}}",
-       "nologin": "Bô kháu-chō thang teng-ji̍p? $1",
-       "nologinlink": "Khui 1 ê kháu-chō",
        "createaccount": "Khui sin kháu-chō",
-       "gotaccount": "Í-keng ū kháu-chō? '''$1'''.",
-       "gotaccountlink": "Teng-ji̍p",
-       "userlogin-resetlink": "你敢袂記得你登入的資料?",
        "userlogin-resetpassword-link": "Bi̍t-bé boē-kì-tit?",
        "userlogin-helplink2": "Pang-chān goá teng-ji̍p",
        "userlogin-loggedin": "你已經用{{GENDER:$1|$1}}登入,用下跤的表,登入別个名。",
        "createacct-another-email-ph": "Phah lí--ê tiān-chú-phoe tē-chí",
        "createaccountmail": "Iōng chi̍t-ê lîm-sî loān-sò͘ sán-seng ê bi̍t-bé , kià khì goá chí-tēng ê tiān-chú-phoe tē-chí.",
        "createacct-realname": "真正的名",
-       "createaccountreason": "Lí-iû:",
        "createacct-reason": "理由:",
        "createacct-reason-ph": "為啥物你欲開一另外一个口座?",
        "createacct-submit": "Khui lí--ê kháu-chō",
        "tooltip-invert": "勾起來的框;相關的名空間會先藏起來。",
        "namespace_association": "相關的名空間",
        "tooltip-namespace_association": "勾起來的框;相關的討論抑物件空間會包括入來",
-       "blanknamespace": "(Thâu-ia̍h)",
+       "blanknamespace": "(Chú-ia̍h)",
        "contributions": "{{GENDER:$1|Iōng-chiá}} ê kòng-hiàn",
        "contributions-title": "Iōng-chiá $1--ê kòng-hiàn",
        "mycontris": "Góa ê kòng-hiàn",
index c427e5f..0f7cdb2 100644 (file)
        "rcfilters-filter-editsbyother-label": "Redigeringer av andre",
        "rcfilters-filter-editsbyother-description": "Redigeringer som er gjort av andre brukere enn deg.",
        "rcfilters-filtergroup-userExpLevel": "Erfaringsnivå (kun for registrerte brukere)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Nykommere",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Færre enn 10 redigeringer og 4 dagers aktivitet.",
-       "rcfilters-filter-userExpLevel-learner-label": "Nybegynnere",
-       "rcfilters-filter-userExpLevel-learner-description": "Flere dagers aktivitet enn «Nykommere», men mindre enn «Erfarne brukere».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Erfarne brukere",
-       "rcfilters-filter-userExpLevel-experienced-description": "Mer enn 30 dagers aktivitet og 500 redigeringer.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Nykommere",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Færre enn 10 redigeringer og 4 dagers aktivitet.",
+       "rcfilters-filter-user-experience-level-learner-label": "Nybegynnere",
+       "rcfilters-filter-user-experience-level-learner-description": "Flere dagers aktivitet enn «Nykommere», men mindre enn «Erfarne brukere».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Erfarne brukere",
+       "rcfilters-filter-user-experience-level-experienced-description": "Mer enn 30 dagers aktivitet og 500 redigeringer.",
        "rcfilters-filtergroup-automated": "Automatiske bidrag",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Redigeringer gjort med automatiske verktøy.",
index 7fedf9d..af1d4f1 100644 (file)
        "rcfilters-filter-registered-description": "Ingelogde gebruikers.",
        "rcfilters-filter-unregistered-label": "Niet-geregistreerd",
        "rcfilters-filter-unregistered-description": "Gebruikers die niet zijn ingelogd.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Het \"Niet geregistreerd\" filter is niet actief, omdat het effect ongedaan wordt gemaakt door de volgende {{PLURAL:$2|filter|filters}} die alleen geregistreerde gebruikers {{PLURAL:$2|vindt|vinden}}: $1",
        "rcfilters-filtergroup-authorship": "Bewerken auteurschap",
        "rcfilters-filter-editsbyself-label": "Uw eigen bewerkingen",
        "rcfilters-filter-editsbyself-description": "Bewerkingen door u.",
        "rcfilters-filter-editsbyother-label": "Bewerkingen door anderen",
        "rcfilters-filter-editsbyother-description": "Bewerkingen die gemaakt zijn door andere gebruikers (niet door uzelf).",
        "rcfilters-filtergroup-userExpLevel": "Gebruikerservarings niveau (alleen voor geregistreerde gebruikers)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Nieuwkomers",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Minder dan 10 bewerkingen en 4 dagen van activiteit.",
-       "rcfilters-filter-userExpLevel-learner-label": "Leerlingen",
-       "rcfilters-filter-userExpLevel-learner-description": "Meer dagen van activiteit en bewerkingen dan \"Nieuwkomers\", maar minder dan \"Ervaren gebruikers\".",
-       "rcfilters-filter-userExpLevel-experienced-label": "Ervaren gebruikers",
-       "rcfilters-filter-userExpLevel-experienced-description": "Meer dan 30 dagen van activiteit en 500 bewerkingen.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Nieuwkomers",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Minder dan 10 bewerkingen en 4 dagen van activiteit.",
+       "rcfilters-filter-user-experience-level-learner-label": "Leerlingen",
+       "rcfilters-filter-user-experience-level-learner-description": "Meer dagen van activiteit en bewerkingen dan \"Nieuwkomers\", maar minder dan \"Ervaren gebruikers\".",
+       "rcfilters-filter-user-experience-level-experienced-label": "Ervaren gebruikers",
+       "rcfilters-filter-user-experience-level-experienced-description": "Meer dan 30 dagen van activiteit en 500 bewerkingen.",
        "rcfilters-filtergroup-automated": "Automatische bijdragen",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "De wijzigingen van geautomatiseerde hulpmiddelen.",
        "rcfilters-filter-humans-label": "Menselijk (geen bot)",
        "rcfilters-filter-humans-description": "Bewerkingen door menselijke bewerkers.",
+       "rcfilters-filter-patrolled-label": "Gecontroleerd",
+       "rcfilters-filter-patrolled-description": "Bewerkingen gemarkeerd als gecontroleerd.",
+       "rcfilters-filter-unpatrolled-label": "Niet gecontroleerd",
+       "rcfilters-filter-unpatrolled-description": "Bewerkingen die niet zijn gemarkeerd als gecontroleerd.",
        "rcfilters-filtergroup-significance": "Belangrijkheid",
        "rcfilters-filter-minor-label": "Kleine bewerkingen",
        "rcfilters-filter-minor-description": "Bewerkingen die door de bewerker zijn gelabeld als klein.",
index 4d5d129..34c9782 100644 (file)
        "mw-widgets-titleinput-description-new-page": "sida finst ikkje enno",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
        "randomrootpage": "Tilfeldig rotsida",
+       "log-action-filter-rights": "Type endring av rettar:",
        "authmanager-userdoesnotexist": "Brukarkontoen «$1» er ikkje oppretta.",
        "userjsispublic": "Merk: JavaScript-undersider bør ikkje innehalda konfidensielle data sidan dei er synlege for andre brukarar.",
        "usercssispublic": "Merk: CSS-undersider bør ikkje innehalda konfidensielle data sidan dei er synlege for andre brukarar."
index e7efcc3..91e926a 100644 (file)
        "rcfilters-filter-editsbyother-label": "Edycje inne niż Twoje",
        "rcfilters-filter-editsbyother-description": "Edycje wykonane przez innych (nie Twoje).",
        "rcfilters-filtergroup-userExpLevel": "Poziom doświadczenia (tylko dla zarejestrowanych użytkowników)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Początkujący",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Mniej niż 10 edycji i 4 dni aktywności.",
-       "rcfilters-filter-userExpLevel-learner-label": "Uczący się",
-       "rcfilters-filter-userExpLevel-learner-description": "Więcej dni aktywności i edycji niż „Nowicjusze”, ale mniej niż „Doświadczeni użytkownicy”.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Doświadczeni użytkownicy",
-       "rcfilters-filter-userExpLevel-experienced-description": "Ponad 30 dni aktywności i 500 edycji.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Początkujący",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Mniej niż 10 edycji i 4 dni aktywności.",
+       "rcfilters-filter-user-experience-level-learner-label": "Uczący się",
+       "rcfilters-filter-user-experience-level-learner-description": "Więcej dni aktywności i edycji niż „Nowicjusze”, ale mniej niż „Doświadczeni użytkownicy”.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Doświadczeni użytkownicy",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ponad 30 dni aktywności i 500 edycji.",
        "rcfilters-filtergroup-automated": "Zmiany zautomatyzowane",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Zmiany wykonane z użyciem zautomatyzowanych narzędzi.",
index e2dfa9a..c3a871f 100644 (file)
        "rcfilters-filter-registered-description": "Description for the filter for showing edits made by logged-in users.",
        "rcfilters-filter-unregistered-label": "Label for the filter for showing edits made by logged-out users.",
        "rcfilters-filter-unregistered-description": " Description for the filter for showing edits made by logged-out users.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Tooltip shown when hovering over a Unregistered filter tag, when a User Experience Level filter is also selected.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.\n\nThis indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.  Parameters:\n* $1 - Comma-separated string of selected User Experience Level filters, e.g. \"Newcomer, Experienced\"\n* $2 - Count of selected User Experience Level filters, for PLURAL",
        "rcfilters-filtergroup-authorship": "Title for the filter group for edit authorship. This filter group allows the user to choose between \"Your own edits\" and \"Edits by others\". More info: https://phabricator.wikimedia.org/T149859\n\n{{doc-important|This is another typical example of ambiguity in the English language. Only the documentation will reveal that this message means \"(filter by) authorship of these edits\", not \"edit the authorship\". That is, \"edit\" is a modifying noun, not a verb.}}",
        "rcfilters-filter-editsbyself-label": "Label for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyself-description": "Description for the filter for showing edits made by the current user.",
        "rcfilters-filter-editsbyother-label": "Label for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filter-editsbyother-description": "Description for the filter for showing edits made by anyone other than the current user.",
        "rcfilters-filtergroup-userExpLevel": "Title for the filter group for user experience levels.",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Label for the filter for showing edits made by new editors.",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Description for the filter for showing edits made by new editors.",
-       "rcfilters-filter-userExpLevel-learner-label": "Label for the filter for showing edits made by learning editors.",
-       "rcfilters-filter-userExpLevel-learner-description": "Description for the filter for showing edits made by learning editors.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Label for the filter for showing edits made by experienced editors.",
-       "rcfilters-filter-userExpLevel-experienced-description": "Description for the filter for showing edits made by experienced editors.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Tooltip shown when hovering over a User Experience Level filter tag, when only Unregistered users are being shown.  This indicates that no results will be shown, because users matched by the User Experience Level groups are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Message shown in the result area when both a User Experience Level filter and the Unregistered filter are selected.  This indicates that no results will be shown because users selected by the User Experience Filter are never unregistered.\n\n\"Unregistered\" is {{msg-mw|Rcfilters-filter-unregistered-label}}.\n\n\"Experience\" is based on {{msg-mw|Rcfilters-filtergroup-userExpLevel}}.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Label for the filter for showing edits made by new editors.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Description for the filter for showing edits made by new editors.",
+       "rcfilters-filter-user-experience-level-learner-label": "Label for the filter for showing edits made by learning editors.",
+       "rcfilters-filter-user-experience-level-learner-description": "Description for the filter for showing edits made by learning editors.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Label for the filter for showing edits made by experienced editors.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Description for the filter for showing edits made by experienced editors.",
        "rcfilters-filtergroup-automated": "Title for the filter group for editor automation type.",
        "rcfilters-filter-bots-label": "Label for the filter for showing edits made by automated tools.\n{{Identical|Bot}}",
        "rcfilters-filter-bots-description": "Description for the filter for showing edits made by automated tools.",
        "rcfilters-filter-humans-label": "Label for the filter for showing edits made by human editors.",
        "rcfilters-filter-humans-description": "Description for the filter for showing edits made by human editors.",
+       "rcfilters-filtergroup-reviewstatus": "Title for the filter group about review status (in core this is whether it's been patrolled)",
+       "rcfilters-filter-patrolled-label": "Label for the filter for showing patrolled edits",
+       "rcfilters-filter-patrolled-description": "Label for the filter showing patrolled edits",
+       "rcfilters-filter-unpatrolled-label": "Label for the filter for showing unpatrolled edits",
+       "rcfilters-filter-unpatrolled-description": "Description for the filter for showing unpatrolled edits",
        "rcfilters-filtergroup-significance": "Title for the filter group for edit significance.\n{{Identical|Significance}}",
        "rcfilters-filter-minor-label": "Label for the filter for showing edits marked as minor.",
        "rcfilters-filter-minor-description": "Description for the filter for showing edits marked as minor.",
index 814f123..4279d24 100644 (file)
        "login": "Autentificare",
        "login-security": "Verificați-vă identitatea",
        "nav-login-createaccount": "Creare cont / Autentificare",
-       "userlogin": "Creare cont / Autentificare",
-       "userloginnocreate": "Autentificare",
        "logout": "Închidere sesiune",
        "userlogout": "Închide sesiunea",
        "notloggedin": "Nu sunteți autentificat",
        "userlogin-noaccount": "Nu aveți cont încă?",
        "userlogin-joinproject": "Înscrieți-vă la {{SITENAME}}",
-       "nologin": "Nu aveți cont încă? $1.",
-       "nologinlink": "Creați-vă un cont de utilizator acum",
        "createaccount": "Creare cont",
-       "gotaccount": "Aveți deja un cont de utilizator? '''$1'''.",
-       "gotaccountlink": "Autentificați-vă",
-       "userlogin-resetlink": "Ați uitat datele de autentificare?",
        "userlogin-resetpassword-link": "V-ați uitat parola?",
        "userlogin-helplink2": "Ajutor la autentificare",
        "userlogin-loggedin": "Sunteți deja {{GENDER:$1|autentificat|autentificată}} ca {{GENDER:$1|$1}}.\nUtilizați formularul de mai jos pentru a vă autentifica cu alt nume de utilizator.",
        "createaccountmail": "Utilizează o parolă temporară aleasă la întâmplare și o trimite la adresa de e-mail indicată",
        "createaccountmail-help": "Poate fi folosit pentru a crea un cont pentru o altă persoană fără a cunoaște parola.",
        "createacct-realname": "Nume real (opțional)",
-       "createaccountreason": "Motiv:",
        "createacct-reason": "Motiv",
        "createacct-reason-ph": "De ce creați un alt cont",
        "createacct-reason-help": "Mesaj care apare în jurnalul de creare a conturilor",
        "action-deleterevision": "ștergeți această versiune",
        "action-deletelogentry": "ștergeți înregistrări din jurnal",
        "action-deletedhistory": "vizualizați istoricul șters a unei pagini",
+       "action-deletedtext": "vizualizați textul versiunii șterse",
        "action-browsearchive": "căutați pagini șterse",
        "action-undelete": "recuperați această pagină",
        "action-suppressrevision": "revizuiți și să restaurați versiuni ascunse",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|a actualizat}} etichetele pentru versiunea $4 a paginii $3 ({{PLURAL:$7|adăugat}} $6; {{PLURAL:$9|șters}} $8)",
        "logentry-tag-update-logentry": "$1 {{GENDER:$2|a actualizat}} etichetele intrării din jurnal $5 a paginii $3 ({{PLURAL:$7|adăugat}} $6; {{PLURAL:$9|șters}} $8)",
        "rightsnone": "(niciunul)",
-       "revdelete-summary": "descrierea modificărilor",
        "feedback-adding": "Se adaugă părerea pe pagină...",
        "feedback-back": "Înapoi",
        "feedback-bugcheck": "Minunat! Trebuie doar să verificați dacă nu cumva problema a fost [$1 deja înregistrată].",
        "feedback-thanks": "Mulțumim! Comentariile dumneavoastră au fost publicate pe pagina „[ $2  $1 ]”.",
        "feedback-thanks-title": "Mulțumim!",
        "feedback-useragent": "Agent utilizator:",
-       "searchsuggest-search": "Căutare",
+       "searchsuggest-search": "Căutare în {{SITENAME}}",
        "searchsuggest-containing": "conținând...",
        "api-error-badtoken": "Eroare internă: jeton greșit.",
        "api-error-emptypage": "Crearea paginilor noi, goale nu este permisă.",
index 30836c6..aa8b00a 100644 (file)
        "rcfilters-filter-editsbyother-label": "Правки других участников",
        "rcfilters-filter-editsbyother-description": "Правки, сделанные другими участниками (не вами).",
        "rcfilters-filtergroup-userExpLevel": "Уровня опыта (только для зарегистрированных участников)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Новички",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Менее 10 правок и 4 дней работы.",
-       "rcfilters-filter-userExpLevel-learner-label": "Учащиеся",
-       "rcfilters-filter-userExpLevel-learner-description": "Больше правок и дней работы, чем у «Новичков», но меньше, чем у «Опытных пользователей».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Опытные пользователи",
-       "rcfilters-filter-userExpLevel-experienced-description": "Более 30 дней активности и 500 правок.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Новички",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Менее 10 правок и 4 дней работы.",
+       "rcfilters-filter-user-experience-level-learner-label": "Учащиеся",
+       "rcfilters-filter-user-experience-level-learner-description": "Больше правок и дней работы, чем у «Новичков», но меньше, чем у «Опытных пользователей».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Опытные пользователи",
+       "rcfilters-filter-user-experience-level-experienced-description": "Более 30 дней активности и 500 правок.",
        "rcfilters-filtergroup-automated": "Автоматизированные вклады",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-bots-description": "Правки, сделанные с помощью автоматизированных инструментов.",
        "rcfilters-filter-humans-label": "Человек (не бот)",
        "rcfilters-filter-humans-description": "Правки, внесённые редакторами.",
+       "rcfilters-filter-patrolled-label": "Отпатрулировано",
+       "rcfilters-filter-patrolled-description": "Правки, помеченные как отпатрулированные.",
        "rcfilters-filtergroup-significance": "Значение",
        "rcfilters-filter-minor-label": "Малые правки",
        "rcfilters-filter-minor-description": "Правки, которые автор пометил как малые.",
index a6af6a9..0931c4a 100644 (file)
@@ -23,7 +23,8 @@
                        "Matma Rex",
                        "Aursani",
                        "V6rg",
-                       "C.R."
+                       "C.R.",
+                       "Anomie"
                ]
        },
        "tog-underline": "Unnerline airtins:",
        "login": "Log in",
        "login-security": "Verify yer identity",
        "nav-login-createaccount": "Log in / cræft aen accoont",
-       "userlogin": "Cræft aen accoont or log in",
-       "userloginnocreate": "Log in.",
        "logout": "Log oot",
        "userlogout": "Log oot",
        "notloggedin": "Naw loggit in",
        "userlogin-noaccount": "Dinna hae aen accoont?",
        "userlogin-joinproject": "Jyn {{SITENAME}}",
-       "nologin": "Dinna hae aen accoont? $1.",
-       "nologinlink": "Cræft aen accoont",
        "createaccount": "Creaut accoont",
-       "gotaccount": "Awreadie hae aen accoont? $1.",
-       "gotaccountlink": "Log in",
-       "userlogin-resetlink": "Fergotten yer login details?",
        "userlogin-resetpassword-link": "Fergot yer password?",
        "userlogin-helplink2": "Heelp wi loggin in",
        "userlogin-loggedin": "Ye'r awreadie loggit in as {{GENDER:$1|$1}}.\nUise the form ablow tae log in as anither uiser.",
        "createacct-another-email-ph": "Enter wab-mail address",
        "createaccountmail": "Uise ae temporarie random passwaird n send it til the speceefied wab-mail address",
        "createacct-realname": "Real name (optional).",
-       "createaccountreason": "Raison:",
        "createacct-reason": "Raison",
        "createacct-reason-ph": "Why ar ye creating anither accoont",
        "createacct-reason-help": "Message shawn in the accoont creation log",
        "logentry-managetags-delete": "$1 {{GENDER:$2|deletit}} the tag \"$4\" (remuived frae $5 {{PLURAL:$5|reveesion or log entry|reveesions an/or log entries}})",
        "log-name-tag": "Tag log",
        "rightsnone": "(nane)",
-       "revdelete-summary": "eedit the ootline",
        "feedback-adding": "Eikin feedback til page...",
        "feedback-bugcheck": "Wunnerfu! Just check that it's na awreadie yin o the [$1 knawn bugs].",
        "feedback-bugnew": "Ah checkt. Report ae new bug",
index 0b2b10c..404083e 100644 (file)
@@ -12,7 +12,8 @@
                        "Trondtr",
                        "לערי ריינהארט",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Pyscowicz"
                ]
        },
        "tog-underline": "Liŋkkaid vuolláisárgun",
        "oct": "golg",
        "nov": "skáb",
        "dec": "juov",
+       "january-date": "ođđajagimánnu $1",
+       "february-date": "guovvamánnu $1",
+       "march-date": "njukčamánnu $1",
+       "april-date": "cuoŋománnu $1",
+       "may-date": "miessemánnu $1",
+       "june-date": "geassemánnu $1",
+       "july-date": "suoidnemánnu $1",
+       "august-date": "borgemánnu $1",
+       "september-date": "čakčamánnu $1",
+       "october-date": "golggotmánnu $1",
+       "november-date": "skábmamánnu $1",
+       "december-date": "juovlamánnu $1",
        "pagecategories": "{{PLURAL:$1|Luohkká|Luohkát}}",
        "category_header": "Siiddut, mat gullet luohkkái $1",
        "subcategories": "Vuolleluohkát",
        "searcharticle": "Mana",
        "history": "Siiddu historjá",
        "history_short": "Historjá",
+       "history_small": "historjá",
        "updatedmarker": "beaiváduvvon du ovddit fitnama maŋŋá",
        "printableversion": "Prentenveršuvdna",
        "permalink": "Bissovaš liŋka",
        "yourdomainname": "Fierbmenamma",
        "login": "Čálligoađe sisa",
        "nav-login-createaccount": "Daga ođđa geavaheaddjidovddaldaga dahje čálligoađe sisa",
-       "userlogin": "Logge sisa dahje ráhkat dovddaldaga",
        "logout": "Čálligoađe olggos",
        "userlogout": "Logge olggos",
        "notloggedin": "It leat čálligoahttan sisa",
-       "nologin": "Jus dus ii vel leat geavaheaddjidovddaldat, sáhtát '''$1''' dakkára.",
-       "nologinlink": "ráhkadit",
        "createaccount": "Ráhkat dovddaldaga",
-       "gotaccount": "Jus dus lea jo geavaheaddjidovddaldat, sáhtát '''$1'''.",
-       "gotaccountlink": "Logge sisa",
        "createaccountmail": "e-poasttain",
        "badretype": "Suollemassánit maid čállet eai leat seammalaganat.",
        "userexists": "Geavaheaddjidovddaldat, man evttohit, lea jo anus. Leage buorre ja vállje nubbi geavaheaddjidovddaldaga.",
        "prefs-misc": "Eará",
        "saveprefs": "Vurke ásahusaid",
        "prefs-editing": "Rievdadeapmi",
-       "rows": "Gurgadasa:",
-       "columns": "Kolumnat",
        "searchresultshead": "Ohcan",
        "recentchangescount": "Siiddui mearri varas rievdadusain",
        "savedprefs": "Du ásahusid vurken lihkosmuvai.",
index 3d67150..b5b80bc 100644 (file)
@@ -13,7 +13,8 @@
                        "Matma Rex",
                        "Srdjan m",
                        "Conquistador",
-                       "Xð"
+                       "Xð",
+                       "Сербијана"
                ]
        },
        "tog-underline": "Podvuci linkove:",
        "category_header": "Stranice u kategoriji \"$1\"",
        "subcategories": "Potkategorije",
        "category-media-header": "Medijske datoteke u kategoriji \"$1\"",
-       "category-empty": "''Ova kategorija trenutno ne sadrži članke ni medijske datoteke.''",
+       "category-empty": "<em>Ova kategorija trenutno ne sadrži članke ni medijske datoteke.</em>",
        "hidden-categories": "{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}}",
        "hidden-category-category": "Sakrivene kategorije",
        "category-subcat-count": "{{PLURAL:$2|Ova kategorija ima sljedeću $1 potkategoriju.|Ova kategorija ima {{PLURAL:$1|sljedeće potkategorije|sljedećih $1 potkategorija}}, od $2 ukupno.}}",
index 09daac9..006d23e 100644 (file)
        "searcharticle": "Ísť na",
        "history": "História stránky",
        "history_short": "História",
+       "history_small": "história",
        "updatedmarker": "aktualizované od mojej poslednej návštevy",
        "printableversion": "Verzia na tlač",
        "permalink": "Trvalý odkaz",
        "views": "Zobrazenia",
        "toolbox": "Nástroje",
        "tool-link-userrights": "Zmeniť používateľské skupiny {{GENDER:$1|tohoto použivateľa|tejto používateľky}}",
+       "tool-link-userrights-readonly": "Zobraziť {{GENDER:$1|používateľské}} skupiny",
        "tool-link-emailuser": "Poslať e-mail {{GENDER:$1|tomuto používateľovi|tejto používateľke}}",
        "userpage": "Zobraziť stránku používateľa",
        "projectpage": "Zobraziť projektovú stránku",
        "passwordreset-emailelement": "Používateľské meno: \n$1\n\nDočasné heslo:\n$2",
        "passwordreset-emailsentemail": "Pokiaľ je toto e-mailová adresa zaregistrovaná k vášmu účtu, bude na ňu zaslaný e-mail pre získanie nového hesla.",
        "passwordreset-emailsentusername": "Pokiaľ je príslušná mailová adresa zaregistrovaná, bude na ňu zaslaný e-mail s novým heslom.",
+       "passwordreset-nocaller": "Musí byť uvedený volajúci",
+       "passwordreset-nosuchcaller": "Volajúci neexistuje: $1",
+       "passwordreset-ignored": "Žiadosť o nové heslo nebola spracovaná. Možno nie je nakonfigurovaný žiaden poskytovateľ?",
+       "passwordreset-invalidemail": "Neplatná e-mailová adresa",
+       "passwordreset-nodata": "Nebolo zadané používateľské meno ani e-mailová adresa",
        "changeemail": "Zmeniť alebo odstrániť e-mailovú adresu",
        "changeemail-header": "Vyplňte tento formulár, ak chcete zmeniť svoju emailovú adresu. Ak chcete odstrániť priradenie akejkoľvek emailovej adresy k vášmu účtu, nechajte pri odosielaní formulára emailovú adresu nevyplnenú",
        "changeemail-no-info": "Na prístup k tejto stránke musíte byť prihlásený.",
        "blockedtitle": "Používateľ je zablokovaný",
        "blockedtext": "'''Vaše používateľské meno alebo IP adresa bola zablokovaná.'''\n\nZablokoval vás správca $1. Udáva tento dôvod:<br />''$2''\n\n* Blokovanie začalo: $8\n* Blokovanie vyprší: $6\n* Kto mal byť zablokovaný: $7\n\nMôžete kontaktovať $1 alebo s jedného z ďalších [[{{MediaWiki:Grouppage-sysop}}|správcov]] a prediskutovať blokovanie.\nUvedomte si, že nemôžete použiť funkciu „{{int:Emailuser}}“, pokiaľ nemáte registrovanú platnú e-mailovú adresu vo svojich [[Special:Preferences|nastaveniach]].\nVaša IP adresa je $3 a ID blokovania je #$5.\nProsím, uveďte oba tieto údaje do každej správy, ktorú posielate.",
        "autoblockedtext": "Vaša IP adresa bola automaticky zablokovaná, pretože ju používa iný používateľ, ktorého zablokoval $1.\nUdaný dôvod zablokovania:\n\n:''$2''\n\n* Blokovanie začalo: $8\n* Blokovanie vyprší: $6\n* Blokovanie sa týka: $7\n\nAk potrebujete informácie o blokovaní, môžete kontaktovať $1 alebo niektorého iného\n[[{{MediaWiki:Grouppage-sysop}}|správcu]].\n\nPozn.: Nemôžete použiť funkciu „{{int:emailuser}}“, ak ste si vo svojich\n[[Special:Preferences|používateľských nastaveniach]] nezaregistrovali platnú emailovú adresu.\n\nVaša aktuálna IP adresa je $3. ID vášho blokovania je $5.\nProsím, uveďte tieto podrobnosti v akýchkoľvek otázkach, ktoré sa opýtate.",
+       "systemblockedtext": "Vaša IP adresa bola automaticky zablokovaná.\nUdaný dôvod zablokovania:\n\n:<em>$2</em>\n\n* Blokovanie začalo: $8\n* Blokovanie vyprší: $6\n* Blokovanie sa týka: $7\n\nVaša aktuálna IP adresa je $3.\nProsím, uveďte tieto podrobnosti v akýchkoľvek otázkach, ktoré sa opýtate.",
        "blockednoreason": "nebol uvedený dôvod",
        "whitelistedittext": "Aby ste mohli upravovať stránky, musíte sa $1",
        "confirmedittext": "Pred úpravami stránok musíte potvrdiť vašu emailovú adresu. Prosím, nastavte a overte svoju emailovú adresu v [[Special:Preferences|používateľských nastaveniach]].",
        "rev-deleted-comment": "(zhrnutie úprav odstránené)",
        "rev-deleted-user": "(používateľské meno odstránené)",
        "rev-deleted-event": "(činnosť odstránená zo záznamu)",
-       "rev-deleted-user-contribs": "[používateľské meno alebo IP adresa odstránená - úprava skrytá pred prispievateľmi]",
+       "rev-deleted-user-contribs": "[používateľské meno alebo IP adresa odstránené â\80\93 úprava skrytá pred prispievateľmi]",
        "rev-deleted-text-permission": "Táto revízia stránky bola '''zmazaná'''.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
        "rev-suppressed-text-permission": "Táto revízia stránky bola <strong>potlačená</strong>. Podrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
        "rev-deleted-text-unhide": "Táto revízia stránky bola <strong>zmazaná</strong>.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].\nAko správca máte stále možnosť [$1 zobraziť túto revíziu] ak chcete.",
        "search-interwiki-caption": "Sesterské projekty",
        "search-interwiki-default": "Výsledky z $1:",
        "search-interwiki-more": "(viac)",
+       "search-interwiki-more-results": "ďalšie výsledky",
        "search-relatedarticle": "Súvisiace",
        "searchrelated": "súvisiace",
        "searchall": "všetko",
        "powersearch-remember": "Zapamätať si výber pre budúce vyhľadávania",
        "search-external": "Externé vyhľadávanie",
        "searchdisabled": "Prepáčte! Fulltextové vyhľadávanie bolo dočasne vypnuté z dôvodu preťaženia. Zatiaľ môžete použiť hľadanie pomocou Google, ktoré však nemusí byť aktuálne.",
-       "search-error": "Pri hľadaní sa vyskytla chyba:$1",
+       "search-error": "Pri hľadaní došlo k chybe: $1",
+       "search-warning": "Pri hľadaní došlo k varovaniu: $1",
        "preferences": "Nastavenia",
        "mypreferences": "Nastavenia",
        "prefs-edits": "Počet úprav:",
        "timezoneregion-indian": "Indický oceán",
        "timezoneregion-pacific": "Tichý oceán",
        "allowemail": "Povoliť prijímanie e-mailov od iných používateľov",
-       "prefs-searchoptions": "Hľadanie",
+       "prefs-searchoptions": "Vyhľadávanie",
        "prefs-namespaces": "Menné priestory",
        "default": "predvolený",
        "prefs-files": "Súbory",
        "prefs-custom-js": "Vlastný JS",
        "prefs-common-css-js": "Spoločné CSS/JS pre všetky témy:",
        "prefs-reset-intro": "Túto stránku môžete použiť na vrátenie predvolených hodnôt vašich nastavení.\nTúto operáciu nemožno vrátiť.",
-       "prefs-emailconfirm-label": "Overenie e-emailu:",
+       "prefs-emailconfirm-label": "Overenie e-mailu:",
        "youremail": "Váš e-mail²",
        "username": "{{GENDER:$1|Používateľské meno}}:",
        "prefs-memberingroups": "{{GENDER:$2|Člen|Členovia}} {{PLURAL:$1|skupiny|skupín}}:",
+       "group-membership-link-with-expiry": "$1 (do $2)",
        "prefs-registration": "Čas registrácie:",
        "yourrealname": "Skutočné meno *:",
        "yourlanguage": "Jazyk:",
        "userrights-user-editname": "Zadajte meno používateľa:",
        "editusergroup": "Upraviť skupiny {{GENDER:$1|používateľa|používateľky}}",
        "editinguser": "Zmena práv používateľa '''[[User:$1|$1]]''' $2",
+       "viewinguserrights": "Prehliadanie práv {{GENDER:$1|používateľa|používateľky}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Upraviť skupiny používateľa",
+       "userrights-viewusergroup": "Zobraziť {{GENDER:$1|používateľské}} skupiny",
        "saveusergroups": "Uložiť skupiny {{GENDER:$1|používateľa|používateľky}}",
        "userrights-groupsmember": "{{GENDER:$2|Člen|Členka}} {{PLURAL:$1|skupiny|skupín}}:",
        "userrights-groupsmember-auto": "Implicitne {{GENDER:$2|člen|členka}} {{PLURAL:$1|skupiny|skupín}}:",
        "userrights-conflict": "Konflikt zmien práv používateľov! Prosím, skontrolujte ho a potvrďte svoje zmeny.",
        "group": "Skupina:",
        "group-user": "Používatelia",
-       "group-autoconfirmed": "zaregistrovaní používatelia",
+       "group-autoconfirmed": "Automaticky schválení používatelia",
        "group-bot": "Boti",
        "group-sysop": "Správcovia",
        "group-bureaucrat": "Byrokrati",
        "group-bureaucrat-member": "{{GENDER:$1|byrokrat|byrokratka|byrokrat}}",
        "group-suppress-member": "{{GENDER:$1|dozorca|dozorkyňa|dozorca}}",
        "grouppage-user": "{{ns:project}}:Používatelia",
-       "grouppage-autoconfirmed": "{{ns:project}}:Zaregistrovaní používatelia",
+       "grouppage-autoconfirmed": "{{ns:project}}:Automaticky schválení používatelia",
        "grouppage-bot": "{{ns:project}}:Boti",
        "grouppage-sysop": "{{ns:project}}:Správcovia",
        "grouppage-bureaucrat": "{{ns:project}}:Byrokrati",
        "right-reupload-shared": "Nahrávať lokálne súbory, ktoré majú prednosť pred zdieľaným úložiskom",
        "right-upload_by_url": "Nahrávať súbor z URL adresy",
        "right-purge": "Čistiť vyrovnávaciu pamäť stránky bez potvrdzovacej stránky",
-       "right-autoconfirmed": "Neovplyvnený obmedzeniami na základe IP adresy",
+       "right-autoconfirmed": "Neovplyvnený rýchlostnými limitmi na základe IP adresy",
        "right-bot": "Byť považovaný za automatický proces",
        "right-nominornewtalk": "Pri drobných úpravách diskusnej stránky nevypisovať hlásenie o nových správach",
        "right-apihighlimits": "Používať vyššie limity v požiadavkách API",
        "action-writeapi": "použiť API na zápis",
        "action-delete": "zmazať túto stránku",
        "action-deleterevision": "zmazať túto revíziu",
+       "action-deletelogentry": "mazať záznamy",
        "action-deletedhistory": "zobraziť históriu zmazaných revízií tejto stránky",
+       "action-deletedtext": "zobraziť si zmazané texty revízií",
        "action-browsearchive": "vyhľadávať zmazané stránky",
        "action-undelete": "obnoviť túto stránku",
        "action-suppressrevision": "skontrolovať a obnoviť túto skrytú revíziu",
        "action-userrights-interwiki": "upravovať práva používateľov na iných wiki",
        "action-siteadmin": "zamykať alebo odomykať databázu",
        "action-sendemail": "posielať e-maily",
+       "action-editmyoptions": "meniť svoje používateľské nastavenia",
        "action-editmywatchlist": "upraviť zoznam sledovaných stránok",
        "action-viewmywatchlist": "zobraziť zoznam sledovaných stránok",
        "action-viewmyprivateinfo": "zobraziť vaše súkromné údaje",
        "upload_directory_read_only": "Webový server nemôže zapisovať do adresára pre nahrávanie ($1).",
        "uploaderror": "Chyba pri nahrávaní",
        "upload-recreate-warning": "'''Upozornenie: Súbor s týmto názvom bol zmazaný alebo presunutý.'''\n\nĎalšie informácie si môžete pozrieť v zázname zmazaní a presunutí:",
-       "uploadtext": "Tento formulár použite na nahrávanie súborov.\nUž nahrané súbory môžete zobraziť alebo hľadať v [[Special:FileList|zozname nahraných súborov]]. Nahrávania a mazania sa tiež zaznamenávajú v [[Special:Log/upload|zázname nahrávaní]].\n\nNa začlenenie obrázka do stránky použite odkaz v tvare\n\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Súbor.jpg]]</nowiki>''' - použije sa plná verzia obrázka\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Súbor.png|alternatívny text]]</nowiki>''' - vykreslí sa v šírke 200 pixelov s okrajom, na ľavom okraji, s popisom v „alt” texte.\n* '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Súbor.ogg]]</nowiki>''' - priamy odkaz na súbor (nezobrazí obrázok na stránke)",
+       "uploadtext": "Tento formulár použite na nahrávanie súborov.\nUž nahrané súbory môžete zobraziť alebo hľadať v [[Special:FileList|zozname nahraných súborov]]. Nahrávania a mazania sa tiež zaznamenávajú v [[Special:Log/upload|zázname nahrávaní]].\n\nPre vloženie obrázka do stránky použite odkaz v tvare:\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Súbor.jpg]]</nowiki>''' – vloží sa originál obrázka,\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Súbor.png|náhľad|Popis]]</nowiki>''' – vloží sa zmenšený náhľad s rámikom a popisom,\n* '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Súbor.ogg]]</nowiki>''' – vloží sa odkaz na súbor (bez zobrazenia obrázku na stránke)",
        "upload-permitted": "{{PLURAL:$2|Povolený formát súboru|Povolené typy súborov}}: $1.",
        "upload-preferred": "{{PLURAL:$2|Uprednostňovaný formát súboru|Uprednostňované typy súborov}}: $1.",
        "upload-prohibited": "{{PLURAL:$2|Zakázaný formát súboru|Zakázané formáty súborov}}: $1.",
        "protect-locked-dblock": "Nie je možné zmeniť úroveň ochrany z dôvodu aktívneho zámku databázy.\nTu sú aktuálne nastavenia stránky '''$1''':",
        "protect-locked-access": "Váš účet nemá oprávnenie meniť úroveň ochrany stránky.\nTu sú aktuálne nastavenia stránky '''$1''':",
        "protect-cascadeon": "Táto stránka je momentálne zamknutá, lebo je vložená v {{PLURAL:$1|nasledovnej stránke, ktorá má|nasledovných stránkach, ktoré majú}} zapnutú kaskádovú ochranu. Zmeny úrovne ochrany tejto stránky neovplyvnia kaskádovú ochranu.",
-       "protect-default": "Povoliť všetkých používateľov",
-       "protect-fallback": "Povoliť iba používateľov s oprávnením „$1“",
-       "protect-level-autoconfirmed": "Povoliť iba používateľov s potvrdeným emailom",
-       "protect-level-sysop": "Povoliť iba správcov",
+       "protect-default": "Povoliť všetkým",
+       "protect-fallback": "Povoliť iba používateľom s oprávnením „$1“",
+       "protect-level-autoconfirmed": "Povoliť iba automaticky schváleným používateľom",
+       "protect-level-sysop": "Povoliť iba správcom",
        "protect-summary-cascade": "kaskáda",
        "protect-expiring": "vyprší o $1 (UTC)",
        "protect-expiring-local": "vyprší $1",
        "protect-expiry-indefinite": "na neurčito",
-       "protect-cascade": "Kaskádové zamknutie - chrániť všetky stránky použité na tejto stránke.",
+       "protect-cascade": "Zamknúť aj stránky použité na tejto stránke (kaskádový zámok)",
        "protect-cantedit": "Nemôžete zmeniť úrovne ochrany tejto stránky, pretože nemáte oprávnenie ju upravovať.",
        "protect-othertime": "Iný čas:",
        "protect-othertime-op": "iný čas",
        "proxyblockreason": "Vaša IP adresa bola zablokovaná, pretože je otvorená proxy. Prosím kontaktujte vášho internetového poskytovateľa alebo technickú podporu a informujte ich o tomto vážnom bezpečnostnom probléme.",
        "sorbsreason": "Vaša IP adresa je vedená ako nezabezpečený proxy server v DNSBL.",
        "sorbs_create_account_reason": "Vaša IP adresa je vedená ako nezabezpečený proxy server v databáze DNSBL, ktorú používa {{SITENAME}}. Nemôžete si vytvoriť účet.",
+       "softblockrangesreason": "Anonymné príspevky z vašej IP adresy ($1) nie sú povolené. Prosím prihláste sa.",
        "xffblockreason": "IP adresa prítomná v hlavičke X-Forwarded-For patriaca buď vám alebo proxy serveru, ktorý používate, je zablokovaná. Pôvodný dôvod zablokovania bol: $1",
        "cant-see-hidden-user": "Používateľ, ktorého sa pokúšate zablokovať už bol zablokovaný a skrytý. Pretože nemáte právo hideuser, nemôžete vidieť ani upravovať blokovanie používateľa.",
        "ipbblocked": "Nemôžete zablokovať ani odblokovať iných používateľov, pretože ste sami zablokovaní",
        "cant-move-to-user-page": "Nemáte oprávnenie presunúť stránku na stránku používateľa (iba na podstránku používateľa).",
        "cant-move-category-page": "Nemáte oprávnenie presúvať stránky kategórií.",
        "cant-move-to-category-page": "Nemáte oprávnenie presunúť stránku na stránku kategórie.",
+       "cant-move-subpages": "Nemáte oprávnenie presúvať podstránky.",
+       "namespace-nosubpages": "V mennom priestore „$1“ nie sú podstránky povolené.",
        "newtitle": "Nový názov:",
        "move-watch": "Sledovať túto stránku",
        "movepagebtn": "Presunúť stránku",
        "pageinfo-length": "Dĺžka stránky (v bajtoch)",
        "pageinfo-article-id": "ID stránky",
        "pageinfo-language": "Jazyk obsahu stránky",
+       "pageinfo-language-change": "zmeniť",
        "pageinfo-content-model": "Model obsahu stránky",
        "pageinfo-content-model-change": "zmeniť",
        "pageinfo-robot-policy": "Indexovanie robotmi",
        "newimages-legend": "Filter",
        "newimages-label": "Názov súboru (alebo jeho časť):",
        "newimages-showbots": "Zobraziť súbory nahrané botmi",
+       "newimages-hidepatrolled": "Skryť preverené nahratia súborov",
        "noimages": "Niet čo zobraziť.",
+       "gallery-slideshow-toggle": "Prepnúť náhľady",
        "ilsubmit": "Hľadať",
        "bydate": "podľa dátumu",
        "sp-newimages-showfrom": "Zobraziť nové súbory počínajúc $2, $1",
        "confirmemail_needlogin": "Musíte sa $1 na potvrdenie vašej emailovaj adresy.",
        "confirmemail_success": "Vaša e-mailová adresa bola potvrdená. Môžete sa prihlásiť a využívať wiki.",
        "confirmemail_loggedin": "Vaša e-mailová adresa bola potvrdená.",
-       "confirmemail_subject": "{{SITENAME}} - potvrdenie e-mailovej adresy",
+       "confirmemail_subject": "{{SITENAME}}  potvrdenie e-mailovej adresy",
        "confirmemail_body": "Niekto, pravdepodobne vy z IP adresy $1, zaregistroval účet\n„$2“ s touto e-mailovou adresou na {{GRAMMAR:lokál|{{SITENAME}}}}.\n\nPre potvrdenie, že tento účet skutočne patrí vám a pre aktivovanie\ne-mailových funkcií na {{GRAMMAR:lokál|{{SITENAME}}}}, otvorte tento odkaz vo vašom prehliadači:\n\n$3\n\nAk ste to *neboli* vy, otvorte tento odkaz,\nčím zrušíte potvrdenie emailovej adresy:\n\n$5\n\nTento potvrdzovací kód vyprší $4.",
        "confirmemail_body_changed": "Niekto, pravdepodobne vy, z IP adresy $1,\nzmenil e-mailovú adresu účtu „$2“ na túto adresu na {{GRAMMAR:lokál|{{SITENAME}}}}.\n\nAby ste potvrdili, že tento účet skutočne patrí vám a znova\naktivovali funkcie emailu na{{GRAMMAR:lokál|{{SITENAME}}}},\notvorte nasledovný odkaz v prehliadači:\n\n$3\n\nAk vám účet *nepatrí*, otvorte namiesto toho tento odkaz,\nktorý zruší potvrdenie emailovej adresy:\n\n$5\n\nPlatnosť tohoto potvrdzovacieho kódu vyprší $4.",
        "confirmemail_body_set": "Niekto, pravdepodobne vy, z IP adresy $1\nnastavil e-mailovú adresu účtu „$2“ na túto adresu na {{GRAMMAR:genitív|{{SITENAME}}}}.\n\nAk chcete potvrdiť, že tento účet skutočne patrí vám a aktivovať\ne-mailové funkcie na {{GRAMMAR:genitív|{{SITENAME}}}}, otvorte tento odkaz vo vašom prehliadači:\n\n$3\n\nAk účet nie je *nepatrí* patrí k vám, nasledujte tento odkaz,\nktorý zruší potvrdenie e-mailovej adresy:\n\n$5\n\nPlatnosť tohto potvrdzovacieho kódu vyprší $4.",
        "pagelang-language": "Jazyk",
        "pagelang-use-default": "Použiť predvolený jazyk",
        "pagelang-select-lang": "Vybrať jazyk",
+       "pagelang-reason": "Dôvod",
        "pagelang-submit": "Odoslať",
        "right-pagelang": "Zmeniť jazyk stránky",
        "action-pagelang": "meniť jazyk stránky",
index 34fef33..d8a1174 100644 (file)
        "group-bot": "Boti",
        "group-sysop": "Administratorji",
        "group-bureaucrat": "Birokrati",
-       "group-suppress": "Zatiralci",
+       "group-suppress": "Ukinjevalci",
        "group-all": "(vsi)",
        "group-user-member": "{{GENDER:$1|uporabnik|uporabnica}}",
        "group-autoconfirmed-member": "{{GENDER:$1|samodejno potrjen uporabnik|samodejno potrjena uporabnica}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator|administratorka}}",
        "group-bureaucrat-member": "{{GENDER:$1|birokrat|birokratinja}}",
-       "group-suppress-member": "{{GENDER:$1|zatiralec|zatiralka}}",
+       "group-suppress-member": "{{GENDER:$1|ukinjevalec|ukinjevalka}}",
        "grouppage-user": "{{ns:project}}:Uporabniki",
        "grouppage-autoconfirmed": "{{ns:project}}:Samodejno potrjeni uporabniki",
        "grouppage-bot": "{{ns:project}}:Boti",
        "grouppage-sysop": "{{ns:project}}:Administratorji",
        "grouppage-bureaucrat": "{{ns:project}}:Birokrati",
-       "grouppage-suppress": "{{ns:project}}:Zatiralci",
+       "grouppage-suppress": "{{ns:project}}:Ukinjevalci",
        "right-read": "Branje strani",
        "right-edit": "Urejanje strani",
        "right-createpage": "Ustvarjanje strani (ki niso pogovorne)",
        "rcfilters-filter-registered-description": "Prijavljeni uredniki.",
        "rcfilters-filter-unregistered-label": "Neregistriran",
        "rcfilters-filter-unregistered-description": "Uredniki, ki niso prijavljeni.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Filter »Neregistrirani« ni dejaven, saj njegov učinek {{PLURAL:$2|izniči naslednji filter|izničita naslednja filtra|izničijo naslednji filtri}} Izkušnje, ki {{PLURAL:$2|najde|najdeta|najdejo}} samo registrirane uporabnike: $1",
        "rcfilters-filtergroup-authorship": "Uredi avtorstvo",
        "rcfilters-filter-editsbyself-label": "Vaša lastna urejanja",
        "rcfilters-filter-editsbyself-description": "Vaša urejanja.",
        "rcfilters-filter-editsbyother-label": "Urejanja drugih",
        "rcfilters-filter-editsbyother-description": "Urejanja, ki so jih naredili drugi uporabniki (ne vi).",
        "rcfilters-filtergroup-userExpLevel": "Stopnja izkušenosti (samo za registrirane uporabnike)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Novinci",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Manj kot 10 urejanj in 4 dni dejavnosti.",
-       "rcfilters-filter-userExpLevel-learner-label": "Učenci",
-       "rcfilters-filter-userExpLevel-learner-description": "Več dni dejavnosti in urejanj kot »Novinci«, vendar manj kot »Izkušeni uporabniki«.",
-       "rcfilters-filter-userExpLevel-experienced-label": "Izkušeni uporabniki",
-       "rcfilters-filter-userExpLevel-experienced-description": "Več kot 30 dni dejavnosti in 500 urejanj.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filter ni dejaven, saj najde samo registrirane uporabnike in zato filter »Neregistrirani« izniči njegov učinek.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter »Neregistrirani« je v sporu z enim ali več filtrov Izkušnje. Filtri Izkušnje najdejo samo registrirane uporabnike. Filtri v sporu so zgoraj označeni kot nedejavni.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Novinci",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Manj kot 10 urejanj in 4 dni dejavnosti.",
+       "rcfilters-filter-user-experience-level-learner-label": "Učenci",
+       "rcfilters-filter-user-experience-level-learner-description": "Več dni dejavnosti in urejanj kot »Novinci«, vendar manj kot »Izkušeni uporabniki«.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Izkušeni uporabniki",
+       "rcfilters-filter-user-experience-level-experienced-description": "Več kot 30 dni dejavnosti in 500 urejanj.",
        "rcfilters-filtergroup-automated": "Samodejni prispevki",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Urejanja, narejena s samodejnimi orodji.",
        "rcfilters-filter-humans-label": "Človek (ne bot)",
        "rcfilters-filter-humans-description": "Urejanja, ki so jih izvedli uredniki ljudje.",
+       "rcfilters-filtergroup-reviewstatus": "Stanje pregleda",
+       "rcfilters-filter-patrolled-label": "Nadzorovano",
+       "rcfilters-filter-patrolled-description": "Urejanja, ki so označena kot nadzorovana.",
+       "rcfilters-filter-unpatrolled-label": "Nenadzorovano",
+       "rcfilters-filter-unpatrolled-description": "Urejanja, ki niso označena kot nadzorovana.",
        "rcfilters-filtergroup-significance": "Pomembnost",
        "rcfilters-filter-minor-label": "Manjše urejanje",
        "rcfilters-filter-minor-description": "Urejanja, ki jih je avtor označil kot manjša.",
index 94a0000..03b7a0d 100644 (file)
        "permalink": "Трајна веза",
        "print": "Штампај",
        "view": "Погледај",
-       "view-foreign": "Види на $1",
+       "view-foreign": "Види на пројекту $1",
        "edit": "Уреди",
        "edit-local": "Уреди локални опис",
        "create": "Направи",
        "longpageerror": "'''Грешка: текст који сте унели је величине {{PLURAL:$1|један килобајт|$1 килобајта|$1 килобајта}}, што је веће од {{PLURAL:$2|дозвољеног једног килобајта|дозвољена $2 килобајта|дозвољених $2 килобајта}}.'''\nСтраница не може бити сачувана.",
        "readonlywarning": "<strong>Упозорење: база података је закључана ради одржавања, тако да тренутно нећете моћи да сачувате измене.</strong>\nМожда бисте желели сачувати текст за касније у некој текстуалној датотеци.\n\nСистемски администратор је навео следеће објашњење: $1",
        "protectedpagewarning": "<strong>Упозорење: ова страница је заштићена, тако да само администратори могу да је мењају.</strong>\nПоследњи запис у дневнику је приказан испод:",
-       "semiprotectedpagewarning": "'''Напомена:''' ова страница је заштићена, тако да само регистровани корисници могу да је уређују.\nПоследњи запис у дневнику је приказан испод:",
+       "semiprotectedpagewarning": "<strong>Напомена:</strong> Ова страница је заштићена, тако да само регистровани корисници могу да је уређују.\nПоследњи запис у дневнику приказан је испод као референца:",
        "cascadeprotectedwarning": "<strong>Упозорење:</strong> ова страница је заштићена тако да је могу уређивати само администратори, јер је она укључена у {{PLURAL:$1|следећу страницу која је|следеће странице које су}} заштићене „преносивом“ заштитом:",
        "titleprotectedwarning": "<strong>Упозорење: ову страницу могу направити само корисници [[Special:ListGroupRights|с одређеним правима]].</strong>\nИспод су наведени последњи записи у дневнику:",
        "templatesused": "{{PLURAL:$1|Шаблон|Шаблони}} на овој страници:",
        "right-createtalk": "прављење страница за разговор",
        "right-createaccount": "отварање нових корисничких налога",
        "right-minoredit": "означавање измена мањим",
-       "right-move": "премештање страница",
+       "right-move": "Ð\9fремештање страница",
        "right-move-subpages": "премештање страница с њиховим подстраницама",
        "right-move-rootuserpages": "премештање основних корисничких страница",
-       "right-move-categorypages": "премештање категорија",
-       "right-movefile": "премештање датотека",
+       "right-move-categorypages": "Ð\9fремештање категорија",
+       "right-movefile": "Ð\9fремештање датотека",
        "right-suppressredirect": "премештање страница без остављања преусмерења",
        "right-upload": "Отпремање датотека",
        "right-reupload": "замењивање постојећих датотека",
        "rcfilters-restore-default-filters": "Враћање подразумеваних филтера",
        "rcfilters-clear-all-filters": "Уклони све филтере",
        "rcfilters-empty-filter": "Нема активних филтера. Сви доприноси су приказани.",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Новајлије",
-       "rcfilters-filter-userExpLevel-experienced-label": "Искусни корисници",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Новајлије",
+       "rcfilters-filter-user-experience-level-experienced-label": "Искусни корисници",
        "rcnotefrom": "Испод {{PLURAL:$5|је измена|су измене}} од <strong>$3, $4</strong> (до <strong>$1</strong> приказано).",
        "rclistfrom": "Прикажи нове измене почев од $2 $3",
        "rcshowhideminor": "$1 мање измене",
        "filehist-revert": "врати",
        "filehist-current": "тренутно",
        "filehist-datetime": "Датум/време",
-       "filehist-thumb": "Ð\9cиниÑ\98аÑ\82Ñ\83Ñ\80а",
+       "filehist-thumb": "УмаÑ\9aени Ð¿Ñ\80иказ",
        "filehist-thumbtext": "Умањени приказ за издање од $1",
-       "filehist-nothumb": "Нема минијатуре",
+       "filehist-nothumb": "Нема умањеног приказа",
        "filehist-user": "Корисник",
        "filehist-dimensions": "Димензије",
        "filehist-filesize": "Величина датотеке",
        "wlshowhidecategorization": "категоризацију страница",
        "watchlist-options": "Поставке списка надгледања",
        "watching": "Надгледање…",
-       "unwatching": "Ð\9fÑ\80екидаÑ\9aе Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aаâ\80¦",
+       "unwatching": "УклаÑ\9aаÑ\9aе Ñ\81а Ñ\81пиÑ\81ка Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа...",
        "watcherrortext": "Дошло је до грешке при промени поставки вашег списка надгледања за „$1“.",
        "enotif_reset": "Означи све странице као посећене",
        "enotif_impersonal_salutation": "{{SITENAME}} корисник",
        "deletepage": "Обриши страницу",
        "confirm": "Потврди",
        "excontent": "садржај је био: „$1“",
-       "excontentauthor": "садржај је био: „$1“, а једини уредник [[Special:Contributions/$2|$2]] ([[User talk:$2|разговор]])",
+       "excontentauthor": "садржај је био: „$1“, а једини уредник „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|разговор]])",
        "exbeforeblank": "садржај пре брисања је био: „$1“",
        "delete-confirm": "Брисање странице „$1“",
        "delete-legend": "Обриши",
        "logentry-contentmodel-change": "$1 је {{GENDER:$2|променио|променила}} модел садржаја странице $3 из „$4“ у „$5“",
        "logentry-contentmodel-change-revertlink": "врати",
        "logentry-contentmodel-change-revert": "врати",
-       "protectlogpage": "Дневник закључавања",
+       "protectlogpage": "Дневник заштите",
        "protectlogtext": "Испод је списак заштићених страница.\nПогледајте [[Special:ProtectedPages|списак заштићених страница]] за више детаља.",
        "protectedarticle": "је заштитио „[[$1]]“",
        "modifiedarticleprotection": "промењен степен заштите за „[[$1]]“",
        "protect-locked-dblock": "Степени заштите се не могу мењати јер је активна база података закључана.\nОво су поставке странице '''$1''':",
        "protect-locked-access": "Немате овлашћења за мењање степена заштите странице.\nОво су тренутне поставке странице '''$1''':",
        "protect-cascadeon": "Ова страница је тренутно заштићена јер се налази на {{PLURAL:$1|страници која има|страницама које имају}} преносиву заштиту.\nМожете да промените степен заштите, али то неће утицати на преносиву заштиту.",
-       "protect-default": "Допуштено свима",
+       "protect-default": "Допуштено свим корисницима",
        "protect-fallback": "Дозвољено само корисницима са дозволом „$1“",
        "protect-level-autoconfirmed": "Допуштено само аутоматски потврђеним корисницима",
        "protect-level-sysop": "Допуштено само администраторима",
        "minimum-size": "Најмања величина",
        "maximum-size": "Највећа величина:",
        "pagesize": "(бајтови)",
-       "restriction-edit": "уређивање",
-       "restriction-move": "премештање",
+       "restriction-edit": "Уређивање",
+       "restriction-move": "Ð\9fремештање",
        "restriction-create": "прављење",
        "restriction-upload": "отпремање",
        "restriction-level-sysop": "потпуно заштићено",
        "ipblocklist-no-results": "Тражена ИП адреса или корисничко име није блокирано.",
        "blocklink": "блокирај",
        "unblocklink": "деблокирај",
-       "change-blocklink": "пÑ\80омени Ð±Ð»Ð¾ÐºÐ¸Ñ\80аÑ\9aе",
+       "change-blocklink": "пÑ\80омени Ð±Ð»Ð¾ÐºÐ°Ð´Ñ\83",
        "contribslink": "доприноси",
        "emaillink": "пошаљи имејл",
        "autoblocker": "Аутоматски сте блокирани јер делите ИП адресу с корисником/цом [[User:$1|$1]].\nРазлог блокирања: „$2“",
        "delete_and_move_confirm": "Да, обриши страницу",
        "delete_and_move_reason": "Обрисано да се ослободи место за премештање из „[[$1]]“",
        "selfmove": "Изворни и одредишни наслови су истоветни;\nне могу да преместим страницу преко саме себе.",
-       "immobile-source-namespace": "Ð\9dе Ð¼Ð¾Ð³Ñ\83 Ð´Ð° Ð¿Ñ\80емеÑ\81Ñ\82им Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð¸Ð¼ÐµÐ½Ñ\81ком Ð¿Ñ\80оÑ\81Ñ\82оÑ\80Ñ\83 â\80\9e$1â\80\9c",
-       "immobile-target-namespace": "Ð\9dе Ð¼Ð¾Ð³Ñ\83 Ð´Ð° Ð¿Ñ\80емеÑ\81Ñ\82им Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð¸Ð¼ÐµÐ½Ñ\81ком Ð¿Ñ\80оÑ\81Ñ\82оÑ\80Ñ\83 â\80\9e$1â\80\9c",
+       "immobile-source-namespace": "Ð\9dе Ð¼Ð¾Ð³Ñ\83 Ð¿Ñ\80емеÑ\81Ñ\82иÑ\82и Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð¸Ð¼ÐµÐ½Ñ\81ки Ð¿Ñ\80оÑ\81Ñ\82оÑ\80 â\80\9e$1â\80\9c.",
+       "immobile-target-namespace": "Ð\9dе Ð¼Ð¾Ð³Ñ\83 Ð¿Ñ\80емеÑ\81Ñ\82иÑ\82и Ñ\81Ñ\82Ñ\80аниÑ\86е Ñ\83 Ð¸Ð¼ÐµÐ½Ñ\81ки Ð¿Ñ\80оÑ\81Ñ\82оÑ\80 â\80\9e$1â\80\9c.",
        "immobile-target-namespace-iw": "Међувики веза није исправно одредиште за премештање странице.",
        "immobile-source-page": "Ова страница се не може преместити.",
        "immobile-target-page": "Не могу да преместим на жељени наслов.",
        "fix-double-redirects": "Ажурирајте сва преусмерења која воде до првобитног наслова",
        "move-leave-redirect": "Остави преусмерење",
        "protectedpagemovewarning": "'''Упозорење:''' ова страница је заштићена, тако да само корисници с администраторским овлашћењима могу да је преместе.\nЗа више информација, последњи запис у дневнику измена је приказан испод:",
-       "semiprotectedpagemovewarning": "'''Напомена:''' ова страница је заштићена, тако да само регистровани корисници могу да је преместе.\nЗа више информација, последњи запис у дневнику измена је приказан испод:",
+       "semiprotectedpagemovewarning": "<strong>Напомена:</strong> Ова страница је заштићена, тако да само регистровани корисници могу да је преместе.\nПоследњи запис у дневнику измена приказан је испод као референца:",
        "move-over-sharedrepo": "[[:$1]] се налази на дељеном складишту. Ако преместите датотеку на овај наслов, то ће заменити дељену датотеку.",
        "file-exists-sharedrepo": "Наведени назив датотеке се већ користи у дељеном складишту.\nИзаберите други назив.",
        "export": "Извоз страница",
        "group-bot.js": "/* Јаваскрипт постављен овде ће се учитати само за ботове */",
        "group-sysop.js": "/* Јаваскрипт постављен овде ће се учитати само за системске операторе */",
        "group-bureaucrat.js": "/* Јаваскрипт постављен овде ће се учитати само за бирократе */",
-       "anonymous": "Ð\90нонимни {{PLURAL:$1|коÑ\80иÑ\81ник|коÑ\80иÑ\81ниÑ\86и}} Ð½Ð° Ð¿Ñ\80оÑ\98екÑ\82Ñ\83 {{SITENAME}}",
+       "anonymous": "Ð\90нонимни {{PLURAL:$1|коÑ\80иÑ\81ник|коÑ\80иÑ\81ниÑ\86и}} Ð¿Ñ\80оÑ\98екÑ\82а {{SITENAME}}",
        "siteuser": "{{SITENAME}} корисник $1",
        "anonuser": "{{SITENAME}} анониман корисник $1",
        "lastmodifiedatby": "Ову страницу је последњи пут {{GENDER:$4|изменио|изменила|изменио}} $3, $1 у $2.",
        "exif-scenecapturetype": "Врста снимања сцена",
        "exif-gaincontrol": "Контрола сцене",
        "exif-contrast": "Контраст",
-       "exif-saturation": "Засићење",
+       "exif-saturation": "Засићеност",
        "exif-sharpness": "Оштрина",
        "exif-devicesettingdescription": "Опис поставки уређаја",
        "exif-subjectdistancerange": "Опсег удаљености објекта",
        "exif-contrast-1": "Меко",
        "exif-contrast-2": "Тврдо",
        "exif-saturation-0": "Нормално",
-       "exif-saturation-1": "Ð\9dиÑ\81ко Ð·Ð°Ñ\81иÑ\9bеÑ\9aе",
-       "exif-saturation-2": "Ð\92иÑ\81око Ð·Ð°Ñ\81иÑ\9bеÑ\9aе",
+       "exif-saturation-1": "Ð\9dиÑ\81ка Ð·Ð°Ñ\81иÑ\9bеноÑ\81Ñ\82",
+       "exif-saturation-2": "Ð\92иÑ\81ока Ð·Ð°Ñ\81иÑ\9bеноÑ\81Ñ\82",
        "exif-sharpness-0": "Нормално",
        "exif-sharpness-1": "Меко",
        "exif-sharpness-2": "Тврдо",
        "log-action-filter-protect-protect": "закључавање",
        "log-action-filter-protect-modify": "измена закључавања",
        "log-action-filter-protect-unprotect": "уклањање закључавања",
-       "log-action-filter-protect-move_prot": "премештање заштите",
+       "log-action-filter-protect-move_prot": "Ð\9fремештање заштите",
        "log-action-filter-rights-rights": "ручно",
        "log-action-filter-rights-autopromote": "аутоматски",
        "log-action-filter-upload-upload": "Ново отпремање",
index a69b504..0f3ceeb 100644 (file)
        "category_header": "Stranice u kategoriji „$1“",
        "subcategories": "Potkategorije",
        "category-media-header": "Datoteke u kategoriji „$1“",
-       "category-empty": "<div style=\"margin:2em 1em 0 1em; padding:0.5em; border:1px solid #AAA; text-align:center;\">''Ova kategorija trenutno ne sadrži stranice ili datoteke.''</div>",
+       "category-empty": "<em>Ova kategorija trenutno ne sadrži stranice ili datoteke.</em>",
        "hidden-categories": "{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}}",
        "hidden-category-category": "Skrivene kategorije",
        "category-subcat-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sledeću potkategoriju.|Ova kategorija ima {{PLURAL:$1|1=sledeću potkategoriju|sledeće $1 potkategorije|sledećih $1 potkategorija}}, od ukupno $2.}}",
        "permalink": "Trajna veza",
        "print": "Štampaj",
        "view": "Pogledaj",
-       "view-foreign": "Vidi na $1",
+       "view-foreign": "Vidi na projektu $1",
        "edit": "Uredi",
        "edit-local": "Uredi lokalni opis",
        "create": "Napravi",
        "readonly_lag": "Baza podataka je automatski zaključana da bi se sekundarni serveri baze podataka uskladili s glavnim.",
        "internalerror": "Unutrašnja greška",
        "internalerror_info": "Unutrašnja greška: $1",
+       "internalerror-fatal-exception": "Kritični izuzetak tipa $1",
        "filecopyerror": "Ne mogu da umnožim datoteku „$1“ u „$2“.",
        "filerenameerror": "Ne mogu da preimenujem datoteku „$1“ u „$2“.",
        "filedeleteerror": "Ne mogu da obrišem datoteku „$1“.",
        "cannotdelete": "Ne mogu da obrišem stranicu ili datoteku „$1“.\nVerovatno ju je neko drugi obrisao.",
        "cannotdelete-title": "Ne mogu da obrišem stranicu „$1“",
        "delete-hook-aborted": "Brisanje je prekinula kuka.\nNije dato nikakvo obrazloženje.",
+       "no-null-revision": "Nije moguće napraviti \"null\" reviziju za stranicu $1",
        "badtitle": "Neispravan naslov",
        "badtitletext": "Naslov stranice je neispravan, prazan ili je međujezički ili međuviki naslov pogrešno povezan.\nMožda sadrži znakove koji se ne mogu koristiti u naslovima.",
+       "title-invalid-empty": "Zatraženi naslov strane je prazan ili sadrži samo razmak.",
+       "title-invalid-utf8": "Zatraženi naslov stranice sadrži nevažeći UTF-8 niz.",
+       "title-invalid-interwiki": "Zatraženi naslov stranice sadrži \"interwiki\" vezu koja ne može biti upotrebljenja za naslove.",
+       "title-invalid-talk-namespace": "Zatraženi naslov strane se odnosi na stranicu za razgovor koja ne može da postoji.",
        "title-invalid-characters": "Traženi naslov ima nevažeće karaktere: „$1“.",
        "perfcached": "Sledeći podaci su keširani i mogu biti zastareli. Keš sadrži najviše {{PLURAL:$1|jedan rezultat|$1 rezultata|$1 rezultata}}.",
        "perfcachedts": "Sledeći podaci su keširani i poslednji put su ažurirani $2 u $3. U kešu {{PLURAL:$4|1=je dostupan najviše jedan rezultat|su dostupna najviše $4 rezultata|je dostupno najviše $4 rezultata}}.",
        "longpageerror": "'''Greška: tekst koji ste uneli je veličine {{PLURAL:$1|jedan kilobajt|$1 kilobajta|$1 kilobajta}}, što je veće od {{PLURAL:$2|dozvoljenog jednog kilobajta|dozvoljena $2 kilobajta|dozvoljenih $2 kilobajta}}.'''\nStranica ne može biti sačuvana.",
        "readonlywarning": "<strong>Upozorenje: baza podataka je zaključana radi održavanja, tako da trenutno nećete moći da sačuvate izmene.</strong>\nMožda biste želeli sačuvati tekst za kasnije u nekoj tekstualnoj datoteci.\n\nSistemski administrator je naveo sledeće objašnjenje: $1",
        "protectedpagewarning": "<strong>Upozorenje: ova stranica je zaštićena, tako da samo administratori mogu da je menjaju.</strong>\nPoslednji zapis u dnevniku je prikazan ispod:",
-       "semiprotectedpagewarning": "'''Napomena:''' ova stranica je zaštićena, tako da samo registrovani korisnici mogu da je uređuju.\nPoslednji zapis u dnevniku je prikazan ispod:",
+       "semiprotectedpagewarning": "<strong>Napomena:</strong> Ova stranica je zaštićena, tako da samo registrovani korisnici mogu da je uređuju.\nPoslednji zapis u dnevniku prikazan je ispod kao referenca:",
        "cascadeprotectedwarning": "<strong>Upozorenje:</strong> ova stranica je zaštićena tako da je mogu uređivati samo administratori, jer je ona uključena u {{PLURAL:$1|sledeću stranicu koja je|sledeće stranice koje su}} zaštićene „prenosivom“ zaštitom:",
        "titleprotectedwarning": "<strong>Upozorenje: ovu stranicu mogu napraviti samo korisnici [[Special:ListGroupRights|s određenim pravima]].</strong>\nIspod su navedeni poslednji zapisi u dnevniku:",
        "templatesused": "{{PLURAL:$1|Šablon|Šabloni}} na ovoj stranici:",
        "right-createtalk": "pravljenje stranica za razgovor",
        "right-createaccount": "otvaranje novih korisničkih naloga",
        "right-minoredit": "označavanje izmena manjim",
-       "right-move": "premeštanje stranica",
+       "right-move": "Premeštanje stranica",
        "right-move-subpages": "premeštanje stranica s njihovim podstranicama",
        "right-move-rootuserpages": "premeštanje osnovnih korisničkih stranica",
-       "right-move-categorypages": "premeštanje kategorija",
-       "right-movefile": "premeštanje datoteka",
+       "right-move-categorypages": "Premeštanje kategorija",
+       "right-movefile": "Premeštanje datoteka",
        "right-suppressredirect": "premeštanje stranica bez ostavljanja preusmerenja",
        "right-upload": "Otpremanje datoteka",
        "right-reupload": "zamenjivanje postojećih datoteka",
        "filehist-revert": "vrati",
        "filehist-current": "trenutno",
        "filehist-datetime": "Datum/vreme",
-       "filehist-thumb": "Minijatura",
+       "filehist-thumb": "Umanjeni prikaz",
        "filehist-thumbtext": "Umanjeni prikaz za izdanje od $1",
-       "filehist-nothumb": "Nema minijature",
+       "filehist-nothumb": "Nema umanjenog prikaza",
        "filehist-user": "Korisnik",
        "filehist-dimensions": "Dimenzije",
        "filehist-filesize": "Veličina datoteke",
        "wlshowhidecategorization": "kategorizaciju stranica",
        "watchlist-options": "Postavke spiska nadgledanja",
        "watching": "Nadgledanje…",
-       "unwatching": "Prekidanje nadgledanja…",
+       "unwatching": "Uklanjanje sa spiska nadgledanja...",
        "watcherrortext": "Došlo je do greške pri promeni postavki vašeg spiska nadgledanja za „$1“.",
        "enotif_reset": "Označi sve stranice kao posećene",
        "enotif_impersonal_salutation": "{{SITENAME}} korisnik",
        "logentry-contentmodel-change": "$1 je {{GENDER:$2|promenio|promenila}} model sadržaja stranice $3 iz „$4“ u „$5“",
        "logentry-contentmodel-change-revertlink": "vrati",
        "logentry-contentmodel-change-revert": "vrati",
-       "protectlogpage": "Dnevnik zaključavanja",
+       "protectlogpage": "Dnevnik zaštite",
        "protectlogtext": "Ispod je spisak zaštićenih stranica.\nPogledajte [[Special:ProtectedPages|spisak zaštićenih stranica]] za više detalja.",
        "protectedarticle": "je zaštitio „[[$1]]“",
        "modifiedarticleprotection": "promenjen stepen zaštite za „[[$1]]“",
        "protect-locked-dblock": "Stepeni zaštite se ne mogu menjati jer je aktivna baza podataka zaključana.\nOvo su postavke stranice '''$1''':",
        "protect-locked-access": "Nemate ovlašćenja za menjanje stepena zaštite stranice.\nOvo su trenutne postavke stranice '''$1''':",
        "protect-cascadeon": "Ova stranica je trenutno zaštićena jer se nalazi na {{PLURAL:$1|stranici koja ima|stranicama koje imaju}} prenosivu zaštitu.\nMožete da promenite stepen zaštite, ali to neće uticati na prenosivu zaštitu.",
-       "protect-default": "Dopušteno svima",
+       "protect-default": "Dopušteno svim korisnicima",
        "protect-fallback": "Dozvoljeno samo korisnicima sa dozvolom „$1“",
        "protect-level-autoconfirmed": "Dopušteno samo automatski potvrđenim korisnicima",
        "protect-level-sysop": "Dopušteno samo administratorima",
        "minimum-size": "Najmanja veličina",
        "maximum-size": "Najveća veličina:",
        "pagesize": "(bajtovi)",
-       "restriction-edit": "uređivanje",
-       "restriction-move": "premeštanje",
+       "restriction-edit": "Uređivanje",
+       "restriction-move": "Premeštanje",
        "restriction-create": "pravljenje",
        "restriction-upload": "otpremanje",
        "restriction-level-sysop": "potpuno zaštićeno",
        "ipblocklist-no-results": "Tražena IP adresa ili korisničko ime nije blokirano.",
        "blocklink": "blokiraj",
        "unblocklink": "deblokiraj",
-       "change-blocklink": "promeni blokiranje",
+       "change-blocklink": "promeni blokadu",
        "contribslink": "doprinosi",
        "emaillink": "pošalji imejl",
        "autoblocker": "Automatski ste blokirani jer delite IP adresu s korisnikom/com [[User:$1|$1]].\nRazlog blokiranja: „$2“",
        "delete_and_move_confirm": "Da, obriši stranicu",
        "delete_and_move_reason": "Obrisano da se oslobodi mesto za premeštanje iz „[[$1]]“",
        "selfmove": "Izvorni i odredišni naslovi su istovetni;\nne mogu da premestim stranicu preko same sebe.",
-       "immobile-source-namespace": "Ne mogu da premestim stranice u imenskom prostoru „$1“",
-       "immobile-target-namespace": "Ne mogu da premestim stranice u imenskom prostoru „$1“",
+       "immobile-source-namespace": "Ne mogu premestiti stranice u imenski prostor „$1“.",
+       "immobile-target-namespace": "Ne mogu premestiti stranice u imenski prostor „$1“.",
        "immobile-target-namespace-iw": "Međuviki veza nije ispravno odredište za premeštanje stranice.",
        "immobile-source-page": "Ova stranica se ne može premestiti.",
        "immobile-target-page": "Ne mogu da premestim na željeni naslov.",
        "fix-double-redirects": "Ažurirajte sva preusmerenja koja vode do prvobitnog naslova",
        "move-leave-redirect": "Ostavi preusmerenje",
        "protectedpagemovewarning": "'''Upozorenje:''' ova stranica je zaštićena, tako da samo korisnici s administratorskim ovlašćenjima mogu da je premeste.\nZa više informacija, poslednji zapis u dnevniku izmena je prikazan ispod:",
-       "semiprotectedpagemovewarning": "'''Napomena:''' ova stranica je zaštićena, tako da samo registrovani korisnici mogu da je premeste.\nZa više informacija, poslednji zapis u dnevniku izmena je prikazan ispod:",
+       "semiprotectedpagemovewarning": "<strong>Napomena:</strong> Ova stranica je zaštićena, tako da samo registrovani korisnici mogu da je premeste.\nPoslednji zapis u dnevniku izmena prikazan je ispod kao referenca:",
        "move-over-sharedrepo": "[[:$1]] se nalazi na deljenom skladištu. Ako premestite datoteku na ovaj naslov, to će zameniti deljenu datoteku.",
        "file-exists-sharedrepo": "Navedeni naziv datoteke se već koristi u deljenom skladištu.\nIzaberite drugi naziv.",
        "export": "Izvoz stranica",
        "group-bot.js": "/* Javaskript postavljen ovde će se učitati samo za botove */",
        "group-sysop.js": "/* Javaskript postavljen ovde će se učitati samo za sistemske operatore */",
        "group-bureaucrat.js": "/* Javaskript postavljen ovde će se učitati samo za birokrate */",
-       "anonymous": "Anonimni {{PLURAL:$1|korisnik|korisnici}} na projektu {{SITENAME}}",
+       "anonymous": "Anonimni {{PLURAL:$1|korisnik|korisnici}} projekta {{SITENAME}}",
        "siteuser": "{{SITENAME}} korisnik $1",
        "anonuser": "{{SITENAME}} anoniman korisnik $1",
        "lastmodifiedatby": "Ovu stranicu je poslednji put {{GENDER:$4|izmenio|izmenila|izmenio}} $3, $1 u $2.",
        "exif-scenecapturetype": "Vrsta snimanja scena",
        "exif-gaincontrol": "Kontrola scene",
        "exif-contrast": "Kontrast",
-       "exif-saturation": "Zasićenje",
+       "exif-saturation": "Zasićenost",
        "exif-sharpness": "Oštrina",
        "exif-devicesettingdescription": "Opis postavki uređaja",
        "exif-subjectdistancerange": "Opseg udaljenosti objekta",
        "exif-contrast-1": "Meko",
        "exif-contrast-2": "Tvrdo",
        "exif-saturation-0": "Normalno",
-       "exif-saturation-1": "Nisko zasićenje",
-       "exif-saturation-2": "Visoko zasićenje",
+       "exif-saturation-1": "Niska zasićenost",
+       "exif-saturation-2": "Visoka zasićenost",
        "exif-sharpness-0": "Normalno",
        "exif-sharpness-1": "Meko",
        "exif-sharpness-2": "Tvrdo",
        "randomrootpage": "Slučajna korenska stranica",
        "log-action-filter-all": "Sve",
        "log-action-filter-move-move_redir": "Premeštanje sa prepisivanjem preusmerenja",
+       "log-action-filter-protect-move_prot": "Premeštanje zaštite",
        "log-action-filter-upload-upload": "Novo otpremanje",
        "authmanager-email-label": "Imejl",
        "authmanager-email-help": "Imejl adresa",
index 30496d4..78fce1e 100644 (file)
        "rcfilters-filter-registered-description": "Користувачі, що увійшли в систему.",
        "rcfilters-filter-unregistered-label": "Незареєстровані",
        "rcfilters-filter-unregistered-description": "Користувачі, які не ввійшли в систему.",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Фільтр «Незареєстровані» неактивний, оскільки його вплив нівелюється {{PLURAL:$2|таким фільтром|такими фільтрами}} досвіду, {{PLURAL:$2|який знаходить|які знаходять}} лише зареєстрованих користувачів: $1",
        "rcfilters-filtergroup-authorship": "Авторство редагувань",
        "rcfilters-filter-editsbyself-label": "Ваші власні редагування",
        "rcfilters-filter-editsbyself-description": "Редагування, зроблені Вами.",
        "rcfilters-filter-editsbyother-label": "Редагування, зроблені іншими",
        "rcfilters-filter-editsbyother-description": "Редагування, виконані іншими користувачами (не Вами).",
        "rcfilters-filtergroup-userExpLevel": "Рівень досвіду (тільки для зареєстрованих користувачів)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "Новачки",
-       "rcfilters-filter-userExpLevel-newcomer-description": "Менше ніж 10 редагувань і 4 дні активності.",
-       "rcfilters-filter-userExpLevel-learner-label": "Учні",
-       "rcfilters-filter-userExpLevel-learner-description": "Більше днів активності та більше редагувань, ніж у «новачків», але менше, ніж у «досвідчених користувачів».",
-       "rcfilters-filter-userExpLevel-experienced-label": "Досвідчені користувачі",
-       "rcfilters-filter-userExpLevel-experienced-description": "Більше 30 днів активності і понад 500 редагувань.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Цей фільтр неактивний, оскільки він знаходить лише зареєстрованих користувачів, тож фільтр «Незареєстровані» нівелює його вплив.",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтр «Незареєстровані» перебуває в конфлікті з одним або більше фільтрів досвіду. Фільтри досвіду знаходять лише зареєстрованих користувачів. Конфліктні фільтри позначені вище як неактивні.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Новачки",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Менше ніж 10 редагувань і 4 дні активності.",
+       "rcfilters-filter-user-experience-level-learner-label": "Учні",
+       "rcfilters-filter-user-experience-level-learner-description": "Більше днів активності та більше редагувань, ніж у «новачків», але менше, ніж у «досвідчених користувачів».",
+       "rcfilters-filter-user-experience-level-experienced-label": "Досвідчені користувачі",
+       "rcfilters-filter-user-experience-level-experienced-description": "Більше 30 днів активності і понад 500 редагувань.",
        "rcfilters-filtergroup-automated": "Автоматизовані редагування",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-bots-description": "Редагування, зроблені з допомогою автоматизованих засобів.",
        "rcfilters-filter-humans-label": "Людина (не бот)",
        "rcfilters-filter-humans-description": "Редагування, зроблені людиною.",
+       "rcfilters-filtergroup-reviewstatus": "Статус перевірки",
+       "rcfilters-filter-patrolled-label": "Перевірені",
+       "rcfilters-filter-patrolled-description": "Редагування, позначені як перевірені",
+       "rcfilters-filter-unpatrolled-label": "Неперевірені",
+       "rcfilters-filter-unpatrolled-description": "Редагування, не позначені як перевірені",
        "rcfilters-filtergroup-significance": "Важливість",
        "rcfilters-filter-minor-label": "Незначні редагування",
        "rcfilters-filter-minor-description": "Редагування, позначені авторами як незначні.",
index 7d0fcc5..6486ad3 100644 (file)
        "talk": "討論",
        "views": "去睇",
        "toolbox": "架撐",
+       "tool-link-userrights": "改{{GENDER:$1|用戶}}組",
+       "tool-link-userrights-readonly": "睇{{GENDER:$1|用戶}}組",
        "tool-link-emailuser": "電郵畀呢個{{GENDER:$1|用戶}}",
        "userpage": "去睇用戶頁",
        "projectpage": "去睇專題頁",
        "createacct-email-ph": "入電郵地址",
        "createacct-another-email-ph": "入電郵地址",
        "createaccountmail": "將臨時密碼送到指定電郵地址",
+       "createaccountmail-help": "可以用嚟幫另一個人開戶,唔會睇到佢個密碼。",
        "createacct-realname": "真名(可以唔填)",
        "createacct-reason": "原因",
        "createacct-reason-ph": "開過個戶口嘅原因",
+       "createacct-reason-help": "喺開戶日誌度顯示呢條訊息",
        "createacct-submit": "開戶口",
        "createacct-another-submit": "開戶口",
        "createacct-continue-submit": "繼續開戶",
        "nocookiesnew": "已經開咗戶口,但你未登入。 {{SITENAME}} 要用 cookies 嚟登入。你已經熄咗佢。請你開咗再試。",
        "nocookieslogin": "{{SITENAME}} 登入要開 cookies。熄咗佢。請你開咗再試。",
        "nocookiesfornew": "呢位用戶嘅戶開未開,我哋唔能夠確認佢嘅來源。\n請肯定你已經開咗 cookies,重新載入再試。",
+       "createacct-loginerror": "開戶成功但自動簽到失敗。煩請[[Special:UserLogin|手動簽到]]。",
        "noname": "你未指定一個有效嘅用戶名。",
        "loginsuccesstitle": "簽到成功",
        "loginsuccess": "'''「$1」登入咗{{SITENAME}}。'''",
        "wrongpasswordempty": "你都未入密碼,唔該再試多次啦。",
        "passwordtooshort": "你嘅密碼最少要有$1個半形字元。",
        "passwordtoolong": "密碼唔可以長過{{PLURAL:$1|1個字元|$1個字元}}。",
+       "passwordtoopopular": "呢個密碼太常見,唔俾用。唔該揀個獨特啲嘅密碼。",
        "password-name-match": "你嘅密碼一定要同你嘅用戶名唔一樣。",
        "password-login-forbidden": "呢個用戶名同密碼嘅利用係被禁止嘅。",
        "mailmypassword": "重設密碼",
        "botpasswords-label-cancel": "取消",
        "botpasswords-label-delete": "刪除",
        "botpasswords-label-resetpassword": "改過個個密碼",
+       "botpasswords-created-title": "生成咗機械人密碼",
+       "botpasswords-updated-title": "改咗機械人密碿",
+       "botpasswords-deleted-title": "鏟咗機械人密碼",
+       "botpasswords-restriction-failed": "機械人密碼限制令到呢次簽到失敗。",
+       "botpasswords-invalid-name": "呢個用戶名無機械人密碼分隔字(「$1」)",
        "resetpass_forbidden": "唔可以更改密碼",
+       "resetpass_forbidden-reason": "改唔到密碼:$1",
        "resetpass-no-info": "你一定要登入咗去直接入來呢一版。",
        "resetpass-submit-loggedin": "改密碼",
        "resetpass-submit-cancel": "取消",
        "selfredirect": "<strong>警告:</strong> 你個跳轉彈返去自己度。\n你可能設錯咗跳轉目標,或者改錯咗版。\n如果你再撳多「{{int:savearticle}}」一下,就會照幫你開呢個跳轉。",
        "missingcommenttext": "請輸入一個註解。",
        "missingcommentheader": "<strong>提醒:</strong>你響呢個註解度並無提供一個主題/標題。如果你再撳一次「{{int:savearticle}}」,你嘅編輯就會無題。",
-       "summary-preview": "摘要預覽:",
-       "subject-preview": "題預覽:",
+       "summary-preview": "編輯摘要預覽:",
+       "subject-preview": "題預覽:",
        "previewerrortext": "預覽你嘅修改嗰陣出錯。",
        "blockedtitle": "用戶已經封鎖",
        "blockedtext": "你嘅用戶名或者 IP 位址已經被 $1 封咗。\n\n呢次封鎖係由$1所封嘅。當中嘅原因係''$2''。\n\n* 呢次封鎖嘅開始時間係:$8\n* 呢次封鎖嘅到期時間係:$6\n* 對於被封鎖者:$7\n\n你可以聯絡 $1 或者其他嘅[[{{MediaWiki:Grouppage-sysop}}|管理員]],討論呢次封鎖。\n除非你已經響你嘅[[Special:Preferences|戶口喜好設定]]入面設定咗有效嘅電郵地址,否則你係唔可以用「電郵呢個用戶」嘅功能。當設定咗一個有效嘅電郵地址之後,呢個功能係唔會封鎖嘅。\n\n你現時嘅 IP 位址係 $3 ,而個封鎖 ID 係 #$5。 請你喺你嘅查詢都註明以上封鎖嘅資料。",
        "rcfilters-filter-editsbyother-label": "其他人改嘅嘢",
        "rcfilters-filter-editsbyother-description": "其他人(唔係你)改嘅嘢",
        "rcfilters-filtergroup-userExpLevel": "經驗級別(只限簽咗到嘅用戶)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "新手",
-       "rcfilters-filter-userExpLevel-newcomer-description": "少過4日、10次編輯",
-       "rcfilters-filter-userExpLevel-learner-label": "學徒",
-       "rcfilters-filter-userExpLevel-learner-description": "編輯數同經驗多過「新手」但少過「老手」。",
-       "rcfilters-filter-userExpLevel-experienced-label": "老手",
-       "rcfilters-filter-userExpLevel-experienced-description": "超過30日同埋500次編輯",
+       "rcfilters-filter-user-experience-level-newcomer-label": "新手",
+       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯",
+       "rcfilters-filter-user-experience-level-learner-label": "學徒",
+       "rcfilters-filter-user-experience-level-learner-description": "編輯數同經驗多過「新手」但少過「老手」。",
+       "rcfilters-filter-user-experience-level-experienced-label": "老手",
+       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯",
        "rcfilters-filtergroup-automated": "自動貢獻",
        "rcfilters-filter-bots-label": "機械人",
        "rcfilters-filter-bots-description": "用自動工具做嘅貢獻",
        "whatlinkshere-hideredirs": "$1跳轉",
        "whatlinkshere-hidetrans": "$1嵌入",
        "whatlinkshere-hidelinks": "$1連結",
-       "whatlinkshere-hideimages": "$1檔案連結",
+       "whatlinkshere-hideimages": "$1檔案連結",
        "whatlinkshere-filters": "過濾器",
        "whatlinkshere-submit": "去",
        "autoblockid": "自動封鎖 #$1",
        "special-characters-title-emdash": "長破折號",
        "special-characters-title-minus": "減號",
        "mw-widgets-titleinput-description-new-page": "冇呢頁",
-       "mw-widgets-titleinput-description-redirect": "跳轉到$1"
+       "mw-widgets-titleinput-description-redirect": "跳轉到$1",
+       "authmanager-authplugin-setpass-failed-title": "改唔到密碼"
 }
index a61fce9..afd63c2 100644 (file)
        "rcfilters-filter-registered-description": "登录编辑者。",
        "rcfilters-filter-unregistered-label": "未注册",
        "rcfilters-filter-unregistered-description": "未登录的编辑者。",
+       "rcfilters-filter-unregistered-conflicts-user-experience-level": "“未注册”过滤器未被激活,因为其影响已被以下体验{{PLURAL:$2|过滤器}}消除,而它只{{PLURAL:$2|找到}}注册用户:$1",
        "rcfilters-filtergroup-authorship": "编辑的作者",
        "rcfilters-filter-editsbyself-label": "您自己的编辑",
        "rcfilters-filter-editsbyself-description": "由您编辑。",
        "rcfilters-filter-editsbyother-label": "由其他人编辑",
        "rcfilters-filter-editsbyother-description": "由其他用户(而不是您)创建的编辑。",
        "rcfilters-filtergroup-userExpLevel": "体验水平(仅限注册用户)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "新手",
-       "rcfilters-filter-userExpLevel-newcomer-description": "少于10次编辑和4天活跃。",
-       "rcfilters-filter-userExpLevel-learner-label": "初学者",
-       "rcfilters-filter-userExpLevel-learner-description": "比“新手”拥有更多活跃天数和编辑数,但少于“有经验的用户”。",
-       "rcfilters-filter-userExpLevel-experienced-label": "有经验的用户",
-       "rcfilters-filter-userExpLevel-experienced-description": "超过30天活跃和500次编辑。",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "此过滤器未被激活,因为它只找到注册用户,而因此“未注册”过滤器正在消除其影响。",
+       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "“未注册”过滤器与一个或更多体验过滤器相冲突。体验过滤器只寻找注册用户。冲突过滤器已在上方标记为未激活。",
+       "rcfilters-filter-user-experience-level-newcomer-label": "新手",
+       "rcfilters-filter-user-experience-level-newcomer-description": "少于10次编辑和4天活跃。",
+       "rcfilters-filter-user-experience-level-learner-label": "初学者",
+       "rcfilters-filter-user-experience-level-learner-description": "比“新手”拥有更多活跃天数和编辑数,但少于“有经验的用户”。",
+       "rcfilters-filter-user-experience-level-experienced-label": "有经验的用户",
+       "rcfilters-filter-user-experience-level-experienced-description": "超过30天活跃和500次编辑。",
        "rcfilters-filtergroup-automated": "自动化贡献者",
        "rcfilters-filter-bots-label": "机器人",
        "rcfilters-filter-bots-description": "由自动化工具做出的编辑。",
        "rcfilters-filter-humans-label": "人类(不是机器人)",
        "rcfilters-filter-humans-description": "由人类编辑者做出的编辑。",
+       "rcfilters-filtergroup-reviewstatus": "巡查状态",
+       "rcfilters-filter-patrolled-label": "已巡查",
+       "rcfilters-filter-patrolled-description": "已将编辑标记为已巡查。",
+       "rcfilters-filter-unpatrolled-label": "未巡查",
+       "rcfilters-filter-unpatrolled-description": "编辑未标记为已巡查。",
        "rcfilters-filtergroup-significance": "意义",
        "rcfilters-filter-minor-label": "小编辑",
        "rcfilters-filter-minor-description": "作者已标记为小编辑的编辑。",
        "version-skin-colheader-name": "皮肤",
        "version-ext-colheader-version": "版本",
        "version-ext-colheader-license": "许可协议",
-       "version-ext-colheader-description": "说明",
+       "version-ext-colheader-description": "描述",
        "version-ext-colheader-credits": "作者",
        "version-license-title": "$1的许可协议",
        "version-license-not-found": "未找到此扩展相关的详细授权信息。",
index 6ed30ab..c141307 100644 (file)
        "rcfilters-filter-editsbyother-label": "其他人的編輯",
        "rcfilters-filter-editsbyother-description": "由其他使用者而非您所作的編輯。",
        "rcfilters-filtergroup-userExpLevel": "經驗等級 (僅限已註冊的使用者)",
-       "rcfilters-filter-userExpLevel-newcomer-label": "新手",
-       "rcfilters-filter-userExpLevel-newcomer-description": "編輯數低於 10 次,活躍低於 4 天。",
-       "rcfilters-filter-userExpLevel-learner-label": "初學者",
-       "rcfilters-filter-userExpLevel-learner-description": "活躍的天數以及編輯數比「新手」多,但比「有經驗的使用者」少。",
-       "rcfilters-filter-userExpLevel-experienced-label": "有經驗的使用者",
-       "rcfilters-filter-userExpLevel-experienced-description": "活躍超過 30 天,編輯超過 500 次。",
+       "rcfilters-filter-user-experience-level-newcomer-label": "新手",
+       "rcfilters-filter-user-experience-level-newcomer-description": "編輯數低於 10 次,活躍低於 4 天。",
+       "rcfilters-filter-user-experience-level-learner-label": "初學者",
+       "rcfilters-filter-user-experience-level-learner-description": "活躍的天數以及編輯數比「新手」多,但比「有經驗的使用者」少。",
+       "rcfilters-filter-user-experience-level-experienced-label": "有經驗的使用者",
+       "rcfilters-filter-user-experience-level-experienced-description": "活躍超過 30 天,編輯超過 500 次。",
        "rcfilters-filter-bots-label": "機器人",
        "rcfilters-filter-humans-label": "人類(非機器人)",
        "rcfilters-filter-humans-description": "由人類編者做出的編輯",
diff --git a/maintenance/archives/patch-image-user-index-2.sql b/maintenance/archives/patch-image-user-index-2.sql
new file mode 100644 (file)
index 0000000..8b19d82
--- /dev/null
@@ -0,0 +1 @@
+CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
index a74d7bd..b44930f 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- image-user-index.sql
 --
--- Add user/timestamp index to current image versions
+-- Add user_text/timestamp index to current image versions
 --
 
 ALTER TABLE /*$wgDBprefix*/image
diff --git a/maintenance/mssql/archives/patch-alter-table-oldimage.sql b/maintenance/mssql/archives/patch-alter-table-oldimage.sql
new file mode 100644 (file)
index 0000000..fb31d6a
--- /dev/null
@@ -0,0 +1 @@
+DROP INDEX /*i*/oi_name_archive_name ON /*_*/oldimage;
index 78f0671..3babb39 100644 (file)
@@ -661,7 +661,6 @@ CREATE TABLE /*_*/oldimage (
 
 CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
-CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name);
 CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
 
 
index 17a8d2e..6279a34 100644 (file)
@@ -68,6 +68,7 @@ class CLIParser extends Maintenance {
                        false,
                        true
                );
+               $this->addOption( 'tidy', 'Tidy the output' );
                $this->addArg( 'file', 'File containing wikitext (Default: stdin)', false );
        }
 
@@ -127,10 +128,14 @@ class CLIParser extends Maintenance {
         * @return ParserOutput
         */
        protected function parse( $wikitext ) {
+               $options = new ParserOptions;
+               if ( $this->getOption( 'tidy' ) ) {
+                       $options->setTidy( true );
+               }
                return $this->parser->parse(
                        $wikitext,
                        $this->getTitle(),
-                       new ParserOptions()
+                       $options
                );
        }
 }
index 44922a4..8f59690 100644 (file)
@@ -899,6 +899,8 @@ CREATE TABLE /*_*/image (
   img_sha1 varbinary(32) NOT NULL default ''
 ) /*$wgDBTableOptions*/;
 
+-- Used by Special:Newimages and ApiQueryAllImages
+CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
 CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
 -- Used by Special:ListFiles for sort-by-size
 CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
index 0c3d27d..119d167 100644 (file)
@@ -1807,42 +1807,6 @@ return [
                        'rcfilters-filterlist-title',
                        'rcfilters-filterlist-feedbacklink',
                        'rcfilters-filterlist-noresults',
-                       'rcfilters-filtergroup-registration',
-                       'rcfilters-filter-registered-label',
-                       'rcfilters-filter-registered-description',
-                       'rcfilters-filter-unregistered-label',
-                       'rcfilters-filter-unregistered-description',
-                       'rcfilters-filtergroup-authorship',
-                       'rcfilters-filter-editsbyself-label',
-                       'rcfilters-filter-editsbyself-description',
-                       'rcfilters-filter-editsbyother-label',
-                       'rcfilters-filter-editsbyother-description',
-                       'rcfilters-filtergroup-userExpLevel',
-                       'rcfilters-filter-userExpLevel-newcomer-label',
-                       'rcfilters-filter-userExpLevel-newcomer-description',
-                       'rcfilters-filter-userExpLevel-learner-label',
-                       'rcfilters-filter-userExpLevel-learner-description',
-                       'rcfilters-filter-userExpLevel-experienced-label',
-                       'rcfilters-filter-userExpLevel-experienced-description',
-                       'rcfilters-filtergroup-automated',
-                       'rcfilters-filter-bots-label',
-                       'rcfilters-filter-bots-description',
-                       'rcfilters-filter-humans-label',
-                       'rcfilters-filter-humans-description',
-                       'rcfilters-filtergroup-significance',
-                       'rcfilters-filter-minor-label',
-                       'rcfilters-filter-minor-description',
-                       'rcfilters-filter-major-label',
-                       'rcfilters-filter-major-description',
-                       'rcfilters-filtergroup-changetype',
-                       'rcfilters-filter-pageedits-label',
-                       'rcfilters-filter-pageedits-description',
-                       'rcfilters-filter-newpages-label',
-                       'rcfilters-filter-newpages-description',
-                       'rcfilters-filter-categorization-label',
-                       'rcfilters-filter-categorization-description',
-                       'rcfilters-filter-logactions-label',
-                       'rcfilters-filter-logactions-description',
                        'rcfilters-highlightbutton-title',
                        'rcfilters-highlightmenu-title',
                        'rcfilters-highlightmenu-help',
@@ -2051,7 +2015,7 @@ return [
                ],
                'targets' => [ 'desktop', 'mobile' ],
                'messages' => [
-                       'search-interwiki-more',
+                       'search-interwiki-more-results',
                        'searchprofile-images'
                ],
        ],
index b339371..5dfdede 100644 (file)
@@ -25,8 +25,7 @@
        var config = mw.config.get( [ 'wgAction', 'wgCurRevisionId' ] ),
                // This should match EditPage::POST_EDIT_COOKIE_KEY_PREFIX:
                cookieKey = 'PostEditRevision' + config.wgCurRevisionId,
-               cookieVal = mw.cookie.get( cookieKey ),
-               $div, id;
+               cookieVal, $div, id;
 
        function removeConfirmation() {
                $div.remove();
 
        mw.hook( 'postEdit' ).add( showConfirmation );
 
-       if ( config.wgAction === 'view' && cookieVal ) {
-               mw.config.set( 'wgPostEdit', true );
-
-               mw.hook( 'postEdit' ).fire( {
-                       // The following messages can be used here:
-                       // postedit-confirmation-saved
-                       // postedit-confirmation-created
-                       // postedit-confirmation-restored
-                       message: mw.msg(
-                               'postedit-confirmation-' + cookieVal,
-                               mw.user
-                       )
-               } );
-               mw.cookie.set( cookieKey, null );
+       // Only when viewing wiki pages, that exist
+       // (E.g. not on special pages or non-view actions)
+       if ( config.wgCurRevisionId && config.wgAction === 'view' ) {
+               cookieVal = mw.cookie.get( cookieKey );
+               if ( cookieVal ) {
+                       mw.config.set( 'wgPostEdit', true );
+
+                       mw.hook( 'postEdit' ).fire( {
+                               // The following messages can be used here:
+                               // postedit-confirmation-saved
+                               // postedit-confirmation-created
+                               // postedit-confirmation-restored
+                               message: mw.msg(
+                                       'postedit-confirmation-' + cookieVal,
+                                       mw.user
+                               )
+                       } );
+
+                       mw.cookie.set( cookieKey, null );
+               }
        }
 
 }( mediaWiki, jQuery ) );
index 5be3656..3bb7716 100644 (file)
         * Set filters and preserve a group relationship based on
         * the definition given by an object
         *
-        * @param {Object} filters Filter group definition
+        * @param {Array} filters Filter group definition
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function ( filters ) {
                var i, filterItem, selectedFilterNames,
                this.clearItems();
                this.groups = {};
 
-               $.each( filters, function ( group, data ) {
+               filters.forEach( function ( data ) {
+                       var group = data.name;
+
                        if ( !model.groups[ group ] ) {
                                model.groups[ group ] = new mw.rcfilters.dm.FilterGroup( group, {
                                        type: data.type,
-                                       title: data.title,
+                                       title: mw.msg( data.title ),
                                        separator: data.separator,
                                        fullCoverage: !!data.fullCoverage
                                } );
 
                        selectedFilterNames = [];
                        for ( i = 0; i < data.filters.length; i++ ) {
+                               data.filters[ i ].subset = data.filters[ i ].subset || [];
+                               data.filters[ i ].subset = data.filters[ i ].subset.map( function ( el ) {
+                                       return el.filter;
+                               } );
+
                                filterItem = new mw.rcfilters.dm.FilterItem( data.filters[ i ].name, model.groups[ group ], {
                                        group: group,
-                                       label: data.filters[ i ].label,
-                                       description: data.filters[ i ].description,
+                                       label: mw.msg( data.filters[ i ].label ),
+                                       description: mw.msg( data.filters[ i ].description ),
                                        subset: data.filters[ i ].subset,
-                                       cssClass: data.filters[ i ].class
+                                       cssClass: data.filters[ i ].cssClass
                                } );
 
                                // For convenience, we should store each filter's "supersets" -- these are
                                        }
                                }
 
-                               if ( values.length === 0 || values.length === filterItems.length ) {
+                               if ( values.length === filterItems.length ) {
                                        result[ group ] = 'all';
                                } else {
                                        result[ group ] = values.join( model.getSeparator() );
index e562057..f8008b6 100644 (file)
@@ -17,7 +17,7 @@
        /**
         * Initialize the filter and parameter states
         *
-        * @param {Object} filterStructure Filter definition and structure for the model
+        * @param {Array} filterStructure Filter definition and structure for the model
         */
        mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) {
                // Initialize the model
index a0b785d..4a586e4 100644 (file)
                        new mw.rcfilters.ui.ChangesListWrapperWidget(
                                filtersModel, changesListModel, $( '.mw-changeslist, .mw-changeslist-empty' ) );
 
-                       controller.initialize( {
-                               registration: {
-                                       title: mw.msg( 'rcfilters-filtergroup-registration' ),
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               {
-                                                       name: 'hideliu',
-                                                       label: mw.msg( 'rcfilters-filter-registered-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-registered-description' ),
-                                                       'class': 'mw-changeslist-liu'
-                                               },
-                                               {
-                                                       name: 'hideanons',
-                                                       label: mw.msg( 'rcfilters-filter-unregistered-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-unregistered-description' ),
-                                                       'class': 'mw-changeslist-anon'
-                                               }
-                                       ]
-                               },
-                               userExpLevel: {
-                                       title: mw.msg( 'rcfilters-filtergroup-userExpLevel' ),
-                                       // Type 'string_options' means that the group is evaluated by
-                                       // string values separated by comma; for example, param=opt1,opt2
-                                       // If all options are selected they are replaced by the term "all".
-                                       // The filters are the values for the parameter defined by the group.
-                                       // ** In this case, the parameter name is the group name. **
-                                       type: 'string_options',
-                                       separator: ',',
-                                       fullCoverage: false,
-                                       filters: [
-                                               {
-                                                       name: 'newcomer',
-                                                       label: mw.msg( 'rcfilters-filter-userExpLevel-newcomer-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-userExpLevel-newcomer-description' ),
-                                                       conflicts: [ 'hideanons' ],
-                                                       'class': 'mw-changeslist-user-newcomer'
-                                               },
-                                               {
-                                                       name: 'learner',
-                                                       label: mw.msg( 'rcfilters-filter-userExpLevel-learner-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-userExpLevel-learner-description' ),
-                                                       conflicts: [ 'hideanons' ],
-                                                       'class': 'mw-changeslist-user-learner'
-                                               },
-                                               {
-                                                       name: 'experienced',
-                                                       label: mw.msg( 'rcfilters-filter-userExpLevel-experienced-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-userExpLevel-experienced-description' ),
-                                                       conflicts: [ 'hideanons' ],
-                                                       'class': 'mw-changeslist-user-experienced'
-                                               }
-                                       ]
-                               },
-                               authorship: {
-                                       title: mw.msg( 'rcfilters-filtergroup-authorship' ),
-                                       // Type 'send_unselected_if_any' means that the controller will go over
-                                       // all unselected filters in the group and use their parameters
-                                       // as truthy in the query string.
-                                       // This is to handle the "negative" filters. We are showing users
-                                       // a positive message ("Show xxx") but the filters themselves are
-                                       // based on "hide YYY". The purpose of this is to correctly map
-                                       // the functionality to the UI, whether we are dealing with 2
-                                       // parameters in the group or more.
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               {
-                                                       name: 'hidemyself',
-                                                       label: mw.msg( 'rcfilters-filter-editsbyself-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-editsbyself-description' ),
-                                                       'class': 'mw-changeslist-self'
-                                               },
-                                               {
-                                                       name: 'hidebyothers',
-                                                       label: mw.msg( 'rcfilters-filter-editsbyother-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-editsbyother-description' ),
-                                                       'class': 'mw-changeslist-others'
-                                               }
-                                       ]
-                               },
-                               automated: {
-                                       title: mw.msg( 'rcfilters-filtergroup-automated' ),
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               {
-                                                       name: 'hidebots',
-                                                       label: mw.msg( 'rcfilters-filter-bots-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-bots-description' ),
-                                                       'default': true,
-                                                       'class': 'mw-changeslist-bot'
-                                               },
-                                               {
-                                                       name: 'hidehumans',
-                                                       label: mw.msg( 'rcfilters-filter-humans-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-humans-description' ),
-                                                       'default': false,
-                                                       'class': 'mw-changeslist-human'
-                                               }
-                                       ]
-                               },
-                               significance: {
-                                       title: mw.msg( 'rcfilters-filtergroup-significance' ),
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               {
-                                                       name: 'hideminor',
-                                                       label: mw.msg( 'rcfilters-filter-minor-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-minor-description' ),
-                                                       'class': 'mw-changeslist-minor'
-                                               },
-                                               {
-                                                       name: 'hidemajor',
-                                                       label: mw.msg( 'rcfilters-filter-major-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-major-description' ),
-                                                       'class': 'mw-changeslist-major'
-                                               }
-                                       ]
-                               },
-                               changetype: {
-                                       title: mw.msg( 'rcfilters-filtergroup-changetype' ),
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               {
-                                                       name: 'hidepageedits',
-                                                       label: mw.msg( 'rcfilters-filter-pageedits-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-pageedits-description' ),
-                                                       'default': false,
-                                                       'class': 'mw-changeslist-src-mw-edit'
-
-                                               },
-                                               {
-                                                       name: 'hidenewpages',
-                                                       label: mw.msg( 'rcfilters-filter-newpages-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-newpages-description' ),
-                                                       'default': false,
-                                                       'class': 'mw-changeslist-src-mw-new'
-                                               },
-                                               {
-                                                       name: 'hidecategorization',
-                                                       label: mw.msg( 'rcfilters-filter-categorization-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-categorization-description' ),
-                                                       'default': true,
-                                                       'class': 'mw-changeslist-src-mw-categorize'
-                                               },
-                                               {
-                                                       name: 'hidelog',
-                                                       label: mw.msg( 'rcfilters-filter-logactions-label' ),
-                                                       description: mw.msg( 'rcfilters-filter-logactions-description' ),
-                                                       'default': false,
-                                                       'class': 'mw-changeslist-src-mw-log'
-                                               }
-                                       ]
-                               }
-                       } );
+                       controller.initialize( mw.config.get( 'wgStructuredChangeFilters' ) );
 
                        // eslint-disable-next-line no-new
                        new mw.rcfilters.ui.FormWrapperWidget(
index 3810ac1..2a93e58 100644 (file)
@@ -3,7 +3,7 @@
        var api = new mw.Api(),
                pageUrl = new mw.Uri(),
                imagesText = new mw.Message( mw.messages, 'searchprofile-images' ),
-               moreResultsText = new mw.Message( mw.messages, 'search-interwiki-more' );
+               moreResultsText = new mw.Message( mw.messages, 'search-interwiki-more-results' );
 
        function itemTemplate( results ) {
 
@@ -28,7 +28,7 @@
 
        function itemWrapperTemplate( pageQuery, itemTemplateOutput ) {
 
-               return '<li class="iw-resultset iw-resultset--image" data-iw-resultsset-pos="0">' +
+               return '<li class="iw-resultset iw-resultset--image" data-iw-resultset-pos="0">' +
                                '<div class="iw-result__header">' +
                                        '<span class="iw-result__icon iw-result__icon--image"></span>' +
                                        '<strong>' + imagesText.escaped() + '</strong>' +
@@ -67,7 +67,7 @@
                }
 
                results.sort( function( a, b ) {
-                       return b.index - a.index;
+                       return a.index - b.index;
                } );
 
                multimediaWidgetTemplate = itemWrapperTemplate( pageUrl.query.search, itemTemplate( results ) );
index 5504883..0b0f912 100644 (file)
@@ -1,11 +1,6 @@
 /* interwiki search results */
 /*==========================*/
 
-#mw-interwiki-results {
-       float: right;
-       width: 30%;
-}
-
 .iw-headline {
        font-weight: bold;
        font-size: 1rem;
@@ -76,7 +71,6 @@ to resemble a traditional dictionary definition */
 }
 
 .iw-result__title a.extiw {
-       color: #252525;
        font-weight: bold;
 }
 
@@ -183,44 +177,20 @@ to resemble a traditional dictionary definition */
        text-align: right;
 }
 
-/* no results
-span the interwiki results across the bottom of the page.
-*/
+/* desktop only */
 
-.mw-search-nonefound ~ #mw-search-interwiki {
-       width: 100%;
-}
+@media only screen and ( min-width: @deviceWidthTablet ) {
 
-.mw-search-nonefound ~ #mw-search-interwiki .iw-resultset {
-       width: 30%;
-       max-width: 300px;
-       margin-left: 0.5em;
-       margin-right: 0.5em;
-}
-
-/* mobile */
-/* stylelint-disable declaration-no-important */
-@media only screen and ( max-width: 768px ) {
        #mw-interwiki-results {
-               width: 100%;
-       }
-       .mw-search-results {
-               max-width: none !important;
+               width: 30%;
+               display: inline-block; /* used to align iw sidebar with the top of the main search results*/
+               margin-left: 10%;
        }
-       .iw-resultset {
-               width: 45% !important;
-               margin-left: 0.5em !important;
-               margin-right: 0.5em !important;
+       .searchresults .mw-search-createlink,
+       .searchresults .mw-search-nonefound,
+       .searchresults .mw-search-results {
+               float: left;
+               width: 60%;
        }
 
 }
-
-@media only screen and ( max-width: 600px ) {
-       .iw-resultset {
-               width: 100% !important;
-               margin-left: 0 !important;
-               margin-right: 0 !important;
-               max-width: none !important;
-       }
-}
-/* stylelint-enable declaration-no-important */
index 89f5132..72ede97 100644 (file)
@@ -32,9 +32,9 @@
 
 .mw-search-results {
        margin: 0;
-       float: left;
-       max-width: 60%;
+       max-width: 38em;
 }
+
 .mw-search-visualclear {
        clear: both;
 }
index 0a73bef..7880b55 100755 (executable)
@@ -30,7 +30,6 @@
                var $form = config.$input ? config.$input.closest( 'form' ) : $();
 
                config = $.extend( {
-                       type: 'search',
                        icon: 'search',
                        maxLength: undefined,
                        performSearchOnClick: true,
                                )
                        } );
                }.bind( this ) );
+
+               this.$element.addClass( 'oo-ui-textInputWidget-type-search' );
+               this.updateSearchIndicator();
+               this.connect( this, {
+                       disable: 'onDisable'
+               } );
        };
 
        /* Setup */
 
        /* Methods */
 
+       /**
+        * @inheritdoc
+        * @protected
+        */
+       mw.widgets.SearchInputWidget.prototype.getInputElement = function () {
+               return $( '<input>' ).attr( 'type', 'search' );
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SearchInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+               if ( e.which === OO.ui.MouseButtons.LEFT ) {
+                       // Clear the text field
+                       this.setValue( '' );
+                       this.$input[ 0 ].focus();
+                       return false;
+               }
+       };
+
+       /**
+        * Update the 'clear' indicator displayed on type: 'search' text
+        * fields, hiding it when the field is already empty or when it's not
+        * editable.
+        */
+       mw.widgets.SearchInputWidget.prototype.updateSearchIndicator = function () {
+               if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
+                       this.setIndicator( null );
+               } else {
+                       this.setIndicator( 'clear' );
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SearchInputWidget.prototype.onChange = function () {
+               mw.widgets.SearchInputWidget.parent.prototype.onChange.call( this );
+               this.updateSearchIndicator();
+       };
+
+       /**
+        * Handle disable events.
+        *
+        * @param {boolean} disabled Element is disabled
+        * @private
+        */
+       mw.widgets.SearchInputWidget.prototype.onDisable = function () {
+               this.updateSearchIndicator();
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SearchInputWidget.prototype.setReadOnly = function ( state ) {
+               mw.widgets.SearchInputWidget.parent.prototype.setReadOnly.call( this, state );
+               this.updateSearchIndicator();
+               return this;
+       };
+
        /**
         * @inheritdoc mw.widgets.TitleWidget
         */
index 33f146b..c2cee7e 100644 (file)
                /* eslint-enable no-bitwise */
        }
 
-       // <https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set>
-       StringSet = window.Set || ( function () {
-               /**
-                * @private
-                * @class
-                */
-               function StringSet() {
-                       this.set = {};
-               }
-               StringSet.prototype.add = function ( value ) {
-                       this.set[ value ] = true;
-               };
-               StringSet.prototype.has = function ( value ) {
-                       return hasOwn.call( this.set, value );
-               };
-               return StringSet;
-       }() );
+       function defineFallbacks() {
+               // <https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set>
+               StringSet = window.Set || ( function () {
+                       /**
+                        * @private
+                        * @class
+                        */
+                       function StringSet() {
+                               this.set = {};
+                       }
+                       StringSet.prototype.add = function ( value ) {
+                               this.set[ value ] = true;
+                       };
+                       StringSet.prototype.has = function ( value ) {
+                               return hasOwn.call( this.set, value );
+                       };
+                       return StringSet;
+               }() );
+       }
 
        /**
         * Create an object that can be read from or written to via methods that allow
                }
        };
 
+       defineFallbacks();
+
        /* eslint-disable no-console */
        log = ( function () {
                // Also update the restoration of methods in mediawiki.log.js
         * @class mw
         */
        mw = {
+               redefineFallbacksForTest: function () {
+                       if ( !window.QUnit ) {
+                               throw new Error( 'Reset not allowed outside unit tests' );
+                       }
+                       defineFallbacks();
+               },
 
                /**
                 * Get the current time, measured in milliseconds since January 1, 1970 (UTC).
index b67c9ab..ebd3c53 100644 (file)
@@ -24,6 +24,7 @@
 global $wgAutoloadClasses;
 $testDir = __DIR__ . "/..";
 
+// @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
 $wgAutoloadClasses += [
 
        # tests/common
@@ -130,6 +131,7 @@ $wgAutoloadClasses += [
 
        # tests/phpunit/includes/specialpage
        'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
+       'AbstractChangesListSpecialPageTestCase' => "$testDir/phpunit/includes/specialpage/AbstractChangesListSpecialPageTestCase.php",
 
        # tests/phpunit/includes/specials
        'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
@@ -167,3 +169,4 @@ $wgAutoloadClasses += [
        'ParserTestFileSuite' => "$testDir/phpunit/suites/ParserTestFileSuite.php",
        'ParserTestTopLevelSuite' => "$testDir/phpunit/suites/ParserTestTopLevelSuite.php",
 ];
+// @codingStandardsIgnoreEnd
\ No newline at end of file
index 469f45a..2bd9086 100644 (file)
@@ -51,6 +51,43 @@ class TemplateParserTest extends MediaWikiTestCase {
                                false,
                                'UnexpectedValueException'
                        ],
+                       [
+                               "\000../foobar",
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
+                       [
+                               '/',
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
+                       [
+                               // Allegedly this can strip ext in windows.
+                               'baz<',
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
+                       [
+                               '\\foo',
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
+                       [
+                               'C:\bar',
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
+                       [
+                               "foo\000bar",
+                               [],
+                               false,
+                               'UnexpectedValueException'
+                       ],
                        [
                                'nonexistenttemplate',
                                [],
diff --git a/tests/phpunit/includes/changes/ChangesListBooleanFilterGroupTest.php b/tests/phpunit/includes/changes/ChangesListBooleanFilterGroupTest.php
new file mode 100644 (file)
index 0000000..0db3a49
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * @covers ChangesListBooleanFilterGroup
+ */
+class ChangesListBooleanFilterGroupTest extends MediaWikiTestCase {
+       public function testIsFullCoverage() {
+               $hideGroupDefault = TestingAccessWrapper::newFromObject(
+                       new ChangesListBooleanFilterGroup( [
+                               'name' => 'groupName',
+                               'priority' => 1,
+                               'filters' => [],
+                       ] )
+               );
+
+               $this->assertSame(
+                       true,
+                       $hideGroupDefault->isFullCoverage
+               );
+       }
+
+       public function testAutoPriorities() {
+               $group = new ChangesListBooleanFilterGroup( [
+                       'name' => 'groupName',
+                       'priority' => 1,
+                       'filters' => [
+                               [ 'name' => 'hidefoo', 'default' => false, ],
+                               [ 'name' => 'hidebar', 'default' => false, ],
+                               [ 'name' => 'hidebaz', 'default' => false, ],
+                       ],
+               ] );
+
+               $filters = $group->getFilters();
+               $this->assertEquals(
+                       [
+                               -2,
+                               -3,
+                               -4,
+                       ],
+                       array_map(
+                               function ( $f ) {
+                                       return $f->getPriority();
+                               },
+                               array_values( $filters )
+                       )
+               );
+       }
+
+       public function testGetJsData() {
+               $definition = [
+                       'name' => 'some-group',
+                       'title' => 'some-group-title',
+                       'priority' => 1,
+                       'filters' => [
+                               [
+                                       'name' => 'hidefoo',
+                                       'label' => 'foo-label',
+                                       'description' => 'foo-description',
+                                       'default' => true,
+                                       'showHide' => 'showhidefoo',
+                                       'priority' => 2,
+                               ],
+                               [
+                                       'name' => 'hidebar',
+                                       'label' => 'bar-label',
+                                       'description' => 'bar-description',
+                                       'default' => false,
+                                       'priority' => 4,
+                               ]
+                       ],
+               ];
+
+               $group = new ChangesListBooleanFilterGroup( $definition );
+
+               $specialPage = $this->getMockBuilder( 'ChangesListSpecialPage' )
+                       ->setConstructorArgs( [
+                               'ChangesListSpecialPage',
+                               '',
+                       ] )
+                       ->getMockForAbstractClass();
+
+               $this->assertArrayEquals(
+                       [
+                               'name' => 'some-group',
+                               'title' => 'some-group-title',
+                               'type' => ChangesListBooleanFilterGroup::TYPE,
+                               'priority' => 1,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidebar',
+                                               'label' => 'bar-label',
+                                               'description' => 'bar-description',
+                                               'default' => false,
+                                               'priority' => 4,
+                                               'cssClass' => null,
+                                               'conflicts' => [],
+                                               'subset' => [],
+                                       ],
+                                       [
+                                               'name' => 'hidefoo',
+                                               'label' => 'foo-label',
+                                               'description' => 'foo-description',
+                                               'default' => true,
+                                               'priority' => 2,
+                                               'cssClass' => null,
+                                               'conflicts' => [],
+                                               'subset' => [],
+                                       ],
+                               ],
+                               'conflicts' => [],
+                               'fullCoverage' => true,
+                               'messageKeys' => [
+                                       'some-group-title',
+                                       'bar-label',
+                                       'bar-description',
+                                       'foo-label',
+                                       'foo-description',
+                               ],
+                       ],
+
+                       $group->getJsData( $specialPage ),
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+       }
+}
diff --git a/tests/phpunit/includes/changes/ChangesListBooleanFilterTest.php b/tests/phpunit/includes/changes/ChangesListBooleanFilterTest.php
new file mode 100644 (file)
index 0000000..c715988
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+
+/**
+ * @covers ChangesListBooleanFilter
+ */
+class ChangesListBooleanFilterTest extends MediaWikiTestCase {
+       public function testGetJsData() {
+               $group = new ChangesListBooleanFilterGroup( [
+                       'name' => 'group',
+                       'priority' => 2,
+                       'filters' => [],
+               ] );
+
+               $definition = [
+                       'group' => $group,
+                       'label' => 'main-label',
+                       'description' => 'main-description',
+                       'default' => 1,
+                       'priority' => 1,
+               ];
+
+               $fooFilter = new ChangesListBooleanFilter(
+                       $definition + [ 'name' => 'hidefoo' ]
+               );
+
+               $barFilter = new ChangesListBooleanFilter(
+                       $definition + [ 'name' => 'hidebar' ]
+               );
+
+               $bazFilter = new ChangesListBooleanFilter(
+                       $definition + [ 'name' => 'hidebaz' ]
+               );
+
+               $fooFilter->conflictsWith(
+                       $barFilter,
+                       'foo-bar-global-conflict',
+                       'foo-conflicts-bar',
+                       'bar-conflicts-foo'
+               );
+
+               $fooFilter->setAsSupersetOf( $bazFilter, 'foo-superset-of-baz' );
+
+               $fooData = $fooFilter->getJsData();
+               $this->assertArrayEquals(
+                       [
+                               'name' => 'hidefoo',
+                               'label' => 'main-label',
+                               'description' => 'main-description',
+                               'default' => 1,
+                               'priority' => 1,
+                               'cssClass' => null,
+                               'conflicts' => [
+                                       [
+                                               'group' => 'group',
+                                               'filter' => 'hidebar',
+                                               'globalDescription' => 'foo-bar-global-conflict',
+                                               'contextDescription' => 'foo-conflicts-bar',
+                                       ]
+                               ],
+                               'subset' => [
+                                       [
+                                               'group' => 'group',
+                                               'filter' => 'hidebaz',
+                                       ],
+
+                               ],
+                               'messageKeys' => [
+                                       'main-label',
+                                       'main-description',
+                                       'foo-bar-global-conflict',
+                                       'foo-conflicts-bar',
+                               ],
+                       ],
+                       $fooData,
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+
+               $barData = $barFilter->getJsData();
+               $this->assertArrayEquals(
+                       [
+                               'name' => 'hidebar',
+                               'label' => 'main-label',
+                               'description' => 'main-description',
+                               'default' => 1,
+                               'priority' => 1,
+                               'cssClass' => null,
+                               'conflicts' => [
+                                       [
+                                               'group' => 'group',
+                                               'filter' => 'hidefoo',
+                                               'globalDescription' => 'foo-bar-global-conflict',
+                                               'contextDescription' => 'bar-conflicts-foo',
+                                       ]
+                               ],
+                               'subset' => [],
+                               'messageKeys' => [
+                                       'main-label',
+                                       'main-description',
+                                       'foo-bar-global-conflict',
+                                       'bar-conflicts-foo',
+                               ],
+                       ],
+                       $barData,
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+       }
+
+       /**
+        * @expectedException MWException
+        * @expectedExceptionMessage Supersets can only be defined for filters in the same group
+        */
+       public function testSetAsSupersetOf() {
+               $groupA = new ChangesListBooleanFilterGroup( [
+                       'name' => 'groupA',
+                       'priority' => 2,
+                       'filters' => [
+                               [
+                                       'name' => 'foo',
+                                       'default' => false,
+                               ],
+                               [
+                                       'name' => 'bar',
+                                       'default' => false,
+                               ]
+                       ],
+               ] );
+
+               $groupB = new ChangesListBooleanFilterGroup( [
+                       'name' => 'groupB',
+                       'priority' => 3,
+                       'filters' => [
+                               [
+                                       'name' => 'baz',
+                                       'default' => true,
+                               ],
+                       ],
+               ] );
+
+               $foo = TestingAccessWrapper::newFromObject( $groupA->getFilter( 'foo' ) );
+
+               $bar = $groupA->getFilter( 'bar' );
+
+               $baz = $groupB->getFilter( 'baz' );
+
+               $foo->setAsSupersetOf( $bar );
+               $this->assertArrayEquals( [
+                               [
+                                       'group' => 'groupA',
+                                       'filter' => 'bar',
+                               ],
+                       ],
+                       $foo->subsetFilters,
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+
+               $foo->setAsSupersetOf( $baz, 'some-message' );
+       }
+
+       public function testIsFeatureAvailableOnStructuredUi() {
+               $specialPage = $this->getMockBuilder( 'ChangesListSpecialPage' )
+                       ->setConstructorArgs( [
+                                       'ChangesListSpecialPage',
+                                       '',
+                               ] )
+                       ->getMockForAbstractClass();
+
+               $groupA = new ChangesListBooleanFilterGroup( [
+                       'name' => 'groupA',
+                       'priority' => 1,
+                       'filters' => [],
+               ] );
+
+               $foo = new ChangesListBooleanFilter( [
+                       'name' => 'hidefoo',
+                       'group' => $groupA,
+                       'label' => 'foo-label',
+                       'description' => 'foo-description',
+                       'default' => true,
+                       'showHide' => 'showhidefoo',
+                       'priority' => 2,
+               ] );
+
+               $this->assertEquals(
+                       true,
+                       $foo->isFeatureAvailableOnStructuredUi( $specialPage ),
+                       'Same filter appears on both'
+               );
+
+               // Should only be legacy ones that haven't been ported yet
+               $bar = new ChangesListBooleanFilter( [
+                       'name' => 'hidebar',
+                       'default' => true,
+                       'group' => $groupA,
+                       'showHide' => 'showhidebar',
+                       'priority' => 2,
+               ] );
+
+               $this->assertEquals(
+                       false,
+                       $bar->isFeatureAvailableOnStructuredUi( $specialPage ),
+                       'Only on unstructured UI'
+               );
+
+               $baz = new ChangesListBooleanFilter( [
+                       'name' => 'hidebaz',
+                       'default' => true,
+                       'group' => $groupA,
+                       'showHide' => 'showhidebaz',
+                       'isReplacedInStructuredUi' => true,
+                       'priority' => 2,
+               ] );
+
+               $this->assertEquals(
+                       true,
+                       $baz->isFeatureAvailableOnStructuredUi( $specialPage ),
+                       'Legacy filter does not appear directly in new UI, but equivalent ' .
+                               'does and is marked with isReplacedInStructuredUi'
+               );
+       }
+}
diff --git a/tests/phpunit/includes/changes/ChangesListStringOptionsFilterGroupTest.php b/tests/phpunit/includes/changes/ChangesListStringOptionsFilterGroupTest.php
new file mode 100644 (file)
index 0000000..019e257
--- /dev/null
@@ -0,0 +1,302 @@
+<?php
+
+/**
+ * @covers ChangesListStringOptionsFilterGroup
+ */
+class ChangesListStringOptionsFilterGroupTest extends MediaWikiTestCase {
+       /**
+        * @expectedException MWException
+        */
+       public function testIsFullCoverage() {
+               $falseGroup = TestingAccessWrapper::newFromObject(
+                       new ChangesListStringOptionsFilterGroup( [
+                               'name' => 'group',
+                               'filters' => [],
+                               'isFullCoverage' => false,
+                               'queryCallable' => function () {
+                               }
+                       ] )
+               );
+
+               $this->assertSame(
+                       false,
+                       $falseGroup->isFullCoverage
+               );
+
+               // Should throw due to missing isFullCoverage
+               $undefinedFullCoverageGroup = new ChangesListStringOptionsFilterGroup( [
+                       'name' => 'othergroup',
+                       'filters' => [],
+               ] );
+       }
+
+       /**
+        * @param array $filterDefinitions Array of filter definitions
+        * @param array $expectedValues Array of values callback should receive
+        * @param string $input Value in URL
+        *
+        * @dataProvider provideModifyQuery
+        */
+       public function testModifyQuery( $filterDefinitions, $expectedValues, $input ) {
+               $self = $this;
+
+               $queryCallable = function (
+                       $className,
+                       $ctx,
+                       $dbr,
+                       &$tables,
+                       &$fields,
+                       &$conds,
+                       &$query_options,
+                       &$join_conds,
+                       $actualSelectedValues
+               ) use ( $self, $expectedValues ) {
+                       $self->assertSame(
+                               $expectedValues,
+                               $actualSelectedValues
+                       );
+               };
+
+               $groupDefinition = [
+                       'name' => 'group',
+                       'default' => '',
+                       'isFullCoverage' => true,
+                       'filters' => $filterDefinitions,
+                       'queryCallable' => $queryCallable,
+               ];
+
+               $this->modifyQueryHelper( $groupDefinition, $input );
+       }
+
+       public function provideModifyQuery() {
+               $mixedFilters = [
+                       [
+                               'name' => 'foo',
+                       ],
+                       [
+                               'name' => 'bar',
+                               'isAllowedCallable' => function () {
+                                       return false;
+                               },
+                       ],
+                       [
+                               'name' => 'baz',
+                       ],
+                       [
+                               'name' => 'goo'
+                       ],
+               ];
+
+               return [
+                       [
+                               $mixedFilters,
+                               [ 'baz', 'foo', ],
+                               'foo;bar;BaZ;invalid',
+                       ],
+
+                       [
+                               $mixedFilters,
+                               [ 'baz', 'foo', 'goo' ],
+                               'all',
+                       ],
+               ];
+       }
+
+       /**
+        * @param array $filterDefinitions Array of filter definitions
+        * @param string $input Value in URL
+        * @param string $message Message thrown by exception
+        *
+        * @dataProvider provideNoOpModifyQuery
+        */
+       public function testNoOpModifyQuery( $filterDefinitions, $input, $message ) {
+               $noFiltersAllowedCallable = function (
+                       $className,
+                       $ctx,
+                       $dbr,
+                       &$tables,
+                       &$fields,
+                       &$conds,
+                       &$query_options,
+                       &$join_conds,
+                       $actualSelectedValues
+               ) use ( $message ) {
+                       throw new MWException( $message );
+               };
+
+               $groupDefinition = [
+                       'name' => 'group',
+                       'default' => '',
+                       'isFullCoverage' => true,
+                       'filters' => $filterDefinitions,
+                       'queryCallable' => $noFiltersAllowedCallable,
+               ];
+
+               $this->modifyQueryHelper( $groupDefinition, $input );
+
+               $this->assertTrue(
+                       true,
+                       'Test successfully completed without calling queryCallable'
+               );
+       }
+
+       public function provideNoOpModifyQuery() {
+               $isAllowedFalse = [
+                       'isAllowedCallable' => function () {
+                               return false;
+                       },
+               ];
+
+               $allDisallowedFilters = [
+                       [
+                               'name' => 'disallowed1',
+                       ] + $isAllowedFalse,
+
+                       [
+                               'name' => 'disallowed2',
+                       ] + $isAllowedFalse,
+
+                       [
+                               'name' => 'disallowed3',
+                       ] + $isAllowedFalse,
+               ];
+
+               $normalFilters = [
+                       [
+                               'name' => 'foo',
+                       ],
+                       [
+                               'name' => 'bar',
+                       ]
+               ];
+
+               return [
+                       [
+                               $allDisallowedFilters,
+                               'disallowed1;disallowed3',
+                               'The queryCallable should not be called if no filters are allowed',
+                       ],
+
+                       [
+                               $normalFilters,
+                               '',
+                               'The queryCallable should not be called if no filters are selected',
+                       ],
+
+                       [
+                               $normalFilters,
+                               'invalid1',
+                               'The queryCallable should not be called if no valid filters are selected',
+                       ],
+               ];
+       }
+
+       protected function getSpecialPage() {
+               return $this->getMockBuilder( 'ChangesListSpecialPage' )
+                       ->setConstructorArgs( [
+                                       'ChangesListSpecialPage',
+                                       '',
+                               ] )
+                       ->getMockForAbstractClass();
+       }
+
+       /**
+        * @param array $groupDefinition Group definition
+        * @param string $input Value in URL
+        *
+        * @dataProvider provideModifyQuery
+        */
+       protected function modifyQueryHelper( $groupDefinition, $input ) {
+               $ctx = $this->getMock( 'IContextSource' );
+               $dbr = $this->getMock( 'IDatabase' );
+               $tables = $fields = $conds = $query_options = $join_conds = [];
+
+               $group = new ChangesListStringOptionsFilterGroup( $groupDefinition );
+
+               $specialPage = $this->getSpecialPage();
+
+               $group->modifyQuery(
+                       $dbr,
+                       $specialPage,
+                       $tables,
+                       $fields,
+                       $conds,
+                       $query_options,
+                       $join_conds,
+                       $input
+               );
+       }
+
+       public function testGetJsData() {
+               $definition = [
+                       'name' => 'some-group',
+                       'title' => 'some-group-title',
+                       'default' => 'foo',
+                       'priority' => 1,
+                       'isFullCoverage' => false,
+                       'queryCallable' => function () {
+                       },
+                       'filters' => [
+                               [
+                                       'name' => 'foo',
+                                       'label' => 'foo-label',
+                                       'description' => 'foo-description',
+                                       'priority' => 2,
+                               ],
+                               [
+                                       'name' => 'bar',
+                                       'label' => 'bar-label',
+                                       'description' => 'bar-description',
+                                       'priority' => 4,
+                               ]
+                       ],
+               ];
+
+               $group = new ChangesListStringOptionsFilterGroup( $definition );
+
+               $specialPage = $this->getSpecialPage();
+
+               $this->assertArrayEquals(
+                       [
+                               'name' => 'some-group',
+                               'title' => 'some-group-title',
+                               'type' => ChangesListStringOptionsFilterGroup::TYPE,
+                               'default' => 'foo',
+                               'priority' => 1,
+                               'fullCoverage' => false,
+                               'filters' => [
+                                       [
+                                               'name' => 'bar',
+                                               'label' => 'bar-label',
+                                               'description' => 'bar-description',
+                                               'priority' => 4,
+                                               'cssClass' => null,
+                                               'conflicts' => [],
+                                               'subset' => [],
+                                       ],
+                                       [
+                                               'name' => 'foo',
+                                               'label' => 'foo-label',
+                                               'description' => 'foo-description',
+                                               'priority' => 2,
+                                               'cssClass' => null,
+                                               'conflicts' => [],
+                                               'subset' => [],
+                                       ],
+                               ],
+                               'conflicts' => [],
+                               'separator' => ';',
+                               'messageKeys' => [
+                                       'some-group-title',
+                                       'bar-label',
+                                       'bar-description',
+                                       'foo-label',
+                                       'foo-description',
+                               ],
+                       ],
+                       $group->getJsData( $specialPage ),
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+       }
+}
index a5a1b7a..6b12229 100644 (file)
@@ -46,7 +46,7 @@ class SearchIndexFieldTest extends MediaWikiTestCase {
                $this->assertFalse( $field1->merge( $field2 ) );
 
                $field1->setMergeCallback(
-                       function ( $this, $that ) {
+                       function ( $a, $b ) {
                                return "test";
                        }
                );
diff --git a/tests/phpunit/includes/specialpage/AbstractChangesListSpecialPageTestCase.php b/tests/phpunit/includes/specialpage/AbstractChangesListSpecialPageTestCase.php
new file mode 100644 (file)
index 0000000..621d6a2
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Abstract base class for shared logic when testing ChangesListSpecialPage
+ * and subclasses
+ *
+ * @group Database
+ */
+abstract class AbstractChangesListSpecialPageTestCase extends MediaWikiTestCase {
+       // Must be initialized by subclass
+       /**
+        * @var ChangesListSpecialPage
+        */
+       protected $changesListSpecialPage;
+
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( 'wgRCWatchCategoryMembership', true );
+       }
+
+       /**
+        * @dataProvider provideParseParameters
+        */
+       public function testParseParameters( $params, $expected ) {
+               $this->changesListSpecialPage->registerFilters();
+
+               $opts = new FormOptions();
+               foreach ( $expected as $key => $value ) {
+                       // Register it as null so sets aren't rejected.
+                       $opts->add(
+                               $key,
+                               null,
+                               FormOptions::guessType( $expected )
+                       );
+               }
+
+               $this->changesListSpecialPage->parseParameters(
+                       $params,
+                       $opts
+               );
+
+               $this->assertArrayEquals(
+                       $expected,
+                       $opts->getAllValues(),
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+       }
+}
diff --git a/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php b/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
new file mode 100644 (file)
index 0000000..c292e97
--- /dev/null
@@ -0,0 +1,804 @@
+<?php
+/**
+ * Test class for ChangesListSpecialPage class
+ *
+ * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
+ *
+ * @author Antoine Musso
+ * @author Stephane Bisson
+ * @author Matthew Flaschen
+ * @group Database
+ *
+ * @covers ChangesListSpecialPage
+ */
+class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase {
+       protected function setUp() {
+               parent::setUp();
+
+               # setup the rc object
+               $this->changesListSpecialPage = $this->getPage();
+       }
+
+       protected function getPage() {
+               return TestingAccessWrapper::newFromObject(
+                       $this->getMockForAbstractClass(
+                               'ChangesListSpecialPage',
+                               [
+                                       'ChangesListSpecialPage',
+                                       ''
+                               ]
+                       )
+               );
+       }
+
+       /** helper to test SpecialRecentchanges::buildMainQueryConds() */
+       private function assertConditions(
+               $expected,
+               $requestOptions = null,
+               $message = '',
+               $user = null
+       ) {
+               $context = new RequestContext;
+               $context->setRequest( new FauxRequest( $requestOptions ) );
+               if ( $user ) {
+                       $context->setUser( $user );
+               }
+
+               $this->changesListSpecialPage->setContext( $context );
+               $formOptions = $this->changesListSpecialPage->setup( null );
+
+               #  Filter out rc_timestamp conditions which depends on the test runtime
+               # This condition is not needed as of march 2, 2011 -- hashar
+               # @todo FIXME: Find a way to generate the correct rc_timestamp
+
+               $tables = [];
+               $fields = [];
+               $queryConditions = [];
+               $query_options = [];
+               $join_conds = [];
+
+               call_user_func_array(
+                       [ $this->changesListSpecialPage, 'buildQuery' ],
+                       [
+                               &$tables,
+                               &$fields,
+                               &$queryConditions,
+                               &$query_options,
+                               &$join_conds,
+                               $formOptions
+                       ]
+               );
+
+               $queryConditions = array_filter(
+                       $queryConditions,
+                       'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
+               );
+
+               $this->assertEquals(
+                       self::normalizeCondition( $expected ),
+                       self::normalizeCondition( $queryConditions ),
+                       $message
+               );
+       }
+
+       private static function normalizeCondition( $conds ) {
+               $normalized = array_map(
+                       function ( $k, $v ) {
+                               return is_numeric( $k ) ? $v : "$k = $v";
+                       },
+                       array_keys( $conds ),
+                       $conds
+               );
+               sort( $normalized );
+               return $normalized;
+       }
+
+       /** return false if condition begin with 'rc_timestamp ' */
+       private static function filterOutRcTimestampCondition( $var ) {
+               return ( false === strpos( $var, 'rc_timestamp ' ) );
+       }
+
+       public function testRcNsFilter() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_namespace = '0'",
+                       ],
+                       [
+                               'namespace' => NS_MAIN,
+                       ],
+                       "rc conditions with no options (aka default setting)"
+               );
+       }
+
+       public function testRcNsFilterInversion() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_namespace != '0'",
+                       ],
+                       [
+                               'namespace' => NS_MAIN,
+                               'invert' => 1,
+                       ],
+                       "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * T4429
+        * @dataProvider provideNamespacesAssociations
+        */
+       public function testRcNsFilterAssociation( $ns1, $ns2 ) {
+               $this->assertConditions(
+                       [ # expected
+                               "(rc_namespace = '$ns1' OR rc_namespace = '$ns2')",
+                       ],
+                       [
+                               'namespace' => $ns1,
+                               'associated' => 1,
+                       ],
+                       "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * T4429
+        * @dataProvider provideNamespacesAssociations
+        */
+       public function testRcNsFilterAssociationWithInversion( $ns1, $ns2 ) {
+               $this->assertConditions(
+                       [ # expected
+                               "(rc_namespace != '$ns1' AND rc_namespace != '$ns2')",
+                       ],
+                       [
+                               'namespace' => $ns1,
+                               'associated' => 1,
+                               'invert' => 1,
+                       ],
+                       "rc conditions with namespace inverted"
+               );
+       }
+
+       /**
+        * Provides associated namespaces to test recent changes
+        * namespaces association filtering.
+        */
+       public static function provideNamespacesAssociations() {
+               return [ # (NS => Associated_NS)
+                       [ NS_MAIN, NS_TALK ],
+                       [ NS_TALK, NS_MAIN ],
+               ];
+       }
+
+       public function testRcHidemyselfFilter() {
+               $user = $this->getTestUser()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_user != '{$user->getId()}'",
+                       ],
+                       [
+                               'hidemyself' => 1,
+                       ],
+                       "rc conditions: hidemyself=1 (logged in)",
+                       $user
+               );
+
+               $user = User::newFromName( '10.11.12.13', false );
+               $this->assertConditions(
+                       [ # expected
+                               "rc_user_text != '10.11.12.13'",
+                       ],
+                       [
+                               'hidemyself' => 1,
+                       ],
+                       "rc conditions: hidemyself=1 (anon)",
+                       $user
+               );
+       }
+
+       public function testRcHidebyothersFilter() {
+               $user = $this->getTestUser()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_user = '{$user->getId()}'",
+                       ],
+                       [
+                               'hidebyothers' => 1,
+                       ],
+                       "rc conditions: hidebyothers=1 (logged in)",
+                       $user
+               );
+
+               $user = User::newFromName( '10.11.12.13', false );
+               $this->assertConditions(
+                       [ # expected
+                               "rc_user_text = '10.11.12.13'",
+                       ],
+                       [
+                               'hidebyothers' => 1,
+                       ],
+                       "rc conditions: hidebyothers=1 (anon)",
+                       $user
+               );
+       }
+
+       public function testRcHidemyselfHidebyothersFilter() {
+               $user = $this->getTestUser()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_user != '{$user->getId()}'",
+                               "rc_user = '{$user->getId()}'",
+                       ],
+                       [
+                               'hidemyself' => 1,
+                               'hidebyothers' => 1,
+                       ],
+                       "rc conditions: hidemyself=1 hidebyothers=1 (logged in)",
+                       $user
+               );
+       }
+
+       public function testRcHidepageedits() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_type != '0'",
+                       ],
+                       [
+                               'hidepageedits' => 1,
+                       ],
+                       "rc conditions: hidepageedits=1"
+               );
+       }
+
+       public function testRcHidenewpages() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_type != '1'",
+                       ],
+                       [
+                               'hidenewpages' => 1,
+                       ],
+                       "rc conditions: hidenewpages=1"
+               );
+       }
+
+       public function testRcHidelog() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_type != '3'",
+                       ],
+                       [
+                               'hidelog' => 1,
+                       ],
+                       "rc conditions: hidelog=1"
+               );
+       }
+
+       public function testRcHidehumans() {
+               $this->assertConditions(
+                       [ # expected
+                               'rc_bot' => 1,
+                       ],
+                       [
+                               'hidebots' => 0,
+                               'hidehumans' => 1,
+                       ],
+                       "rc conditions: hidebots=0 hidehumans=1"
+               );
+       }
+
+       public function testRcHidepatrolledDisabledFilter() {
+               $user = $this->getTestUser()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                       ],
+                       [
+                               'hidepatrolled' => 1,
+                       ],
+                       "rc conditions: hidepatrolled=1 (user not allowed)",
+                       $user
+               );
+       }
+
+       public function testRcHideunpatrolledDisabledFilter() {
+               $user = $this->getTestUser()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                       ],
+                       [
+                               'hideunpatrolled' => 1,
+                       ],
+                       "rc conditions: hideunpatrolled=1 (user not allowed)",
+                       $user
+               );
+       }
+       public function testRcHidepatrolledFilter() {
+               $user = $this->getTestSysop()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_patrolled = 0",
+                       ],
+                       [
+                               'hidepatrolled' => 1,
+                       ],
+                       "rc conditions: hidepatrolled=1",
+                       $user
+               );
+       }
+
+       public function testRcHideunpatrolledFilter() {
+               $user = $this->getTestSysop()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_patrolled = 1",
+                       ],
+                       [
+                               'hideunpatrolled' => 1,
+                       ],
+                       "rc conditions: hideunpatrolled=1",
+                       $user
+               );
+       }
+
+       public function testRcHideminorFilter() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_minor = 0",
+                       ],
+                       [
+                               'hideminor' => 1,
+                       ],
+                       "rc conditions: hideminor=1"
+               );
+       }
+
+       public function testRcHidemajorFilter() {
+               $this->assertConditions(
+                       [ # expected
+                               "rc_minor = 1",
+                       ],
+                       [
+                               'hidemajor' => 1,
+                       ],
+                       "rc conditions: hidemajor=1"
+               );
+       }
+
+       public function testRcHidepatrolledHideunpatrolledFilter() {
+               $user = $this->getTestSysop()->getUser();
+               $this->assertConditions(
+                       [ # expected
+                               "rc_patrolled = 0",
+                               "rc_patrolled = 1",
+                       ],
+                       [
+                               'hidepatrolled' => 1,
+                               'hideunpatrolled' => 1,
+                       ],
+                       "rc conditions: hidepatrolled=1 hideunpatrolled=1",
+                       $user
+               );
+       }
+
+       public function testHideCategorization() {
+               $this->assertConditions(
+                       [
+                               # expected
+                               "rc_type != '6'"
+                       ],
+                       [
+                               'hidecategorization' => 1
+                       ],
+                       "rc conditions: hidecategorization=1"
+               );
+       }
+
+       public function testFilterUserExpLevel() {
+               $this->setMwGlobals( [
+                       'wgLearnerEdits' => 10,
+                       'wgLearnerMemberSince' => 4,
+                       'wgExperiencedUserEdits' => 500,
+                       'wgExperiencedUserMemberSince' => 30,
+               ] );
+
+               $this->createUsers( [
+                       'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
+                       'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
+                       'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
+                       'Learner1' => [ 'edits' => 15, 'days' => 10 ],
+                       'Learner2' => [ 'edits' => 450, 'days' => 20 ],
+                       'Learner3' => [ 'edits' => 460, 'days' => 33 ],
+                       'Learner4' => [ 'edits' => 525, 'days' => 28 ],
+                       'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
+               ] );
+
+               // newcomers only
+               $this->assertArrayEquals(
+                       [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
+                       $this->fetchUsers( [ 'newcomer' ] )
+               );
+
+               // newcomers and learner
+               $this->assertArrayEquals(
+                       [
+                               'Newcomer1', 'Newcomer2', 'Newcomer3',
+                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
+                       ],
+                       $this->fetchUsers( [ 'newcomer', 'learner' ] )
+               );
+
+               // newcomers and more learner
+               $this->assertArrayEquals(
+                       [
+                               'Newcomer1', 'Newcomer2', 'Newcomer3',
+                               'Experienced1',
+                       ],
+                       $this->fetchUsers( [ 'newcomer', 'experienced' ] )
+               );
+
+               // learner only
+               $this->assertArrayEquals(
+                       [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
+                       $this->fetchUsers( [ 'learner' ] )
+               );
+
+               // more experienced only
+               $this->assertArrayEquals(
+                       [ 'Experienced1' ],
+                       $this->fetchUsers( [ 'experienced' ] )
+               );
+
+               // learner and more experienced
+               $this->assertArrayEquals(
+                       [
+                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
+                               'Experienced1',
+                       ],
+                       $this->fetchUsers( [ 'learner', 'experienced' ] ),
+                       'Learner and more experienced'
+               );
+
+               // newcomers, learner, and more experienced
+               // TOOD: Fix test.  This needs to test that anons are excluded,
+               // and right now the join fails.
+               /* $this->assertArrayEquals( */
+               /*      [ */
+               /*              'Newcomer1', 'Newcomer2', 'Newcomer3', */
+               /*              'Learner1', 'Learner2', 'Learner3', 'Learner4', */
+               /*              'Experienced1', */
+               /*      ], */
+               /*      $this->fetchUsers( [ 'newcomer', 'learner', 'experienced' ] ) */
+               /* ); */
+       }
+
+       private function createUsers( $specs ) {
+               $dbw = wfGetDB( DB_MASTER );
+               foreach ( $specs as $name => $spec ) {
+                       User::createNew(
+                               $name,
+                               [
+                                       'editcount' => $spec['edits'],
+                                       'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'] ) ),
+                                       'email' => 'ut',
+                               ]
+                       );
+               }
+       }
+
+       private function fetchUsers( $filters ) {
+               $tables = [];
+               $conds = [];
+               $fields = [];
+               $query_options = [];
+               $join_conds = [];
+
+               sort( $filters );
+
+               call_user_func_array(
+                       [ $this->changesListSpecialPage, 'filterOnUserExperienceLevel' ],
+                       [
+                               get_class( $this->changesListSpecialPage ),
+                               $this->changesListSpecialPage->getContext(),
+                               $this->changesListSpecialPage->getDB(),
+                               &$tables,
+                               &$fields,
+                               &$conds,
+                               &$query_options,
+                               &$join_conds,
+                               $filters
+                       ]
+               );
+
+               $result = wfGetDB( DB_MASTER )->select(
+                       'user',
+                       'user_name',
+                       array_filter( $conds ) + [ 'user_email' => 'ut' ]
+               );
+
+               $usernames = [];
+               foreach ( $result as $row ) {
+                       $usernames[] = $row->user_name;
+               }
+
+               return $usernames;
+       }
+
+       private function daysAgo( $days ) {
+               $secondsPerDay = 86400;
+               return time() - $days * $secondsPerDay;
+       }
+
+       public function testGetFilterGroupDefinitionFromLegacyCustomFilters() {
+               $customFilters = [
+                       'hidefoo' => [
+                               'msg' => 'showhidefoo',
+                               'default' => true,
+                       ],
+
+                       'hidebar' => [
+                               'msg' => 'showhidebar',
+                               'default' => false,
+                       ],
+               ];
+
+               $this->assertEquals(
+                       [
+                               'name' => 'unstructured',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'priority' => -1,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidefoo',
+                                               'showHide' => 'showhidefoo',
+                                               'default' => true,
+                                       ],
+                                       [
+                                               'name' => 'hidebar',
+                                               'showHide' => 'showhidebar',
+                                               'default' => false,
+                                       ]
+                               ],
+                       ],
+                       $this->changesListSpecialPage->getFilterGroupDefinitionFromLegacyCustomFilters(
+                               $customFilters
+                       )
+               );
+       }
+
+       public function testGetStructuredFilterJsData() {
+               $definition = [
+                       [
+                               'name' => 'gub-group',
+                               'title' => 'gub-group-title',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidefoo',
+                                               'label' => 'foo-label',
+                                               'description' => 'foo-description',
+                                               'default' => true,
+                                               'showHide' => 'showhidefoo',
+                                               'priority' => 2,
+                                       ],
+                                       [
+                                               'name' => 'hidebar',
+                                               'label' => 'bar-label',
+                                               'description' => 'bar-description',
+                                               'default' => false,
+                                               'priority' => 4,
+                                       ]
+                               ],
+                       ],
+
+                       [
+                               'name' => 'des-group',
+                               'title' => 'des-group-title',
+                               'class' => ChangesListStringOptionsFilterGroup::class,
+                               'isFullCoverage' => true,
+                               'filters' => [
+                                       [
+                                               'name' => 'grault',
+                                               'label' => 'grault-label',
+                                               'description' => 'grault-description',
+                                       ],
+                                       [
+                                               'name' => 'garply',
+                                               'label' => 'garply-label',
+                                               'description' => 'garply-description',
+                                       ],
+                               ],
+                               'queryCallable' => function () {
+                               },
+                               'default' => ChangesListStringOptionsFilterGroup::NONE,
+                       ],
+
+                       [
+                               'name' => 'unstructured',
+                               'class' => ChangesListBooleanFilterGroup::class,
+                               'filters' => [
+                                       [
+                                               'name' => 'hidethud',
+                                               'showHide' => 'showhidethud',
+                                               'default' => true,
+                                       ],
+
+                                       [
+                                               'name' => 'hidemos',
+                                               'showHide' => 'showhidemos',
+                                               'default' => false,
+                                       ],
+                               ],
+                       ],
+
+               ];
+
+               $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
+
+               $this->assertArrayEquals(
+                       [
+                               // Filters that only display in the unstructured UI are
+                               // are not included, and neither are groups that would
+                               // be empty due to the above.
+                               'groups' => [
+                                       [
+                                               'name' => 'gub-group',
+                                               'title' => 'gub-group-title',
+                                               'type' => ChangesListBooleanFilterGroup::TYPE,
+                                               'priority' => -1,
+                                               'filters' => [
+                                                       [
+                                                               'name' => 'hidebar',
+                                                               'label' => 'bar-label',
+                                                               'description' => 'bar-description',
+                                                               'default' => false,
+                                                               'priority' => 4,
+                                                               'cssClass' => null,
+                                                               'conflicts' => [],
+                                                               'subset' => [],
+                                                       ],
+                                                       [
+                                                               'name' => 'hidefoo',
+                                                               'label' => 'foo-label',
+                                                               'description' => 'foo-description',
+                                                               'default' => true,
+                                                               'priority' => 2,
+                                                               'cssClass' => null,
+                                                               'conflicts' => [],
+                                                               'subset' => [],
+                                                       ],
+                                               ],
+                                               'fullCoverage' => true,
+                                               'conflicts' => [],
+                                       ],
+
+                                       [
+                                               'name' => 'des-group',
+                                               'title' => 'des-group-title',
+                                               'type' => ChangesListStringOptionsFilterGroup::TYPE,
+                                               'priority' => -2,
+                                               'fullCoverage' => true,
+                                               'filters' => [
+                                                       [
+                                                               'name' => 'grault',
+                                                               'label' => 'grault-label',
+                                                               'description' => 'grault-description',
+                                                               'cssClass' => null,
+                                                               'priority' => -2,
+                                                               'conflicts' => [],
+                                                               'subset' => [],
+                                                       ],
+                                                       [
+                                                               'name' => 'garply',
+                                                               'label' => 'garply-label',
+                                                               'description' => 'garply-description',
+                                                               'cssClass' => null,
+                                                               'priority' => -3,
+                                                               'conflicts' => [],
+                                                               'subset' => [],
+                                                       ],
+                                               ],
+                                               'conflicts' => [],
+                                               'separator' => ';',
+                                               'default' => ChangesListStringOptionsFilterGroup::NONE,
+                                       ],
+                               ],
+                               'messageKeys' => [
+                                       'gub-group-title',
+                                       'bar-label',
+                                       'bar-description',
+                                       'foo-label',
+                                       'foo-description',
+                                       'des-group-title',
+                                       'grault-label',
+                                       'grault-description',
+                                       'garply-label',
+                                       'garply-description',
+                               ],
+                       ],
+                       $this->changesListSpecialPage->getStructuredFilterJsData(),
+                       /** ordered= */ false,
+                       /** named= */ true
+               );
+       }
+
+       public function provideParseParameters() {
+               return [
+                       [ 'hidebots', [ 'hidebots' => true ] ],
+
+                       [ 'bots', [ 'hidebots' => false ] ],
+
+                       [ 'hideminor', [ 'hideminor' => true ] ],
+
+                       [ 'minor', [ 'hideminor' => false ] ],
+
+                       [ 'hidemajor', [ 'hidemajor' => true ] ],
+
+                       [ 'hideliu', [ 'hideliu' => true ] ],
+
+                       [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
+
+                       [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
+
+                       [ 'hideanons', [ 'hideanons' => true ] ],
+
+                       [ 'hidemyself', [ 'hidemyself' => true ] ],
+
+                       [ 'hidebyothers', [ 'hidebyothers' => true ] ],
+
+                       [ 'hidehumans', [ 'hidehumans' => true ] ],
+
+                       [ 'hidepageedits', [ 'hidepageedits' => true ] ],
+
+                       [ 'pagedits', [ 'hidepageedits' => false ] ],
+
+                       [ 'hidenewpages', [ 'hidenewpages' => true ] ],
+
+                       [ 'hidecategorization', [ 'hidecategorization' => true ] ],
+
+                       [ 'hidelog', [ 'hidelog' => true ] ],
+
+                       [
+                               'userExpLevel=learner;experienced',
+                               [
+                                       'userExpLevel' => 'learner;experienced'
+                               ],
+                       ],
+
+                       // A few random combos
+                       [
+                               'bots,hideliu,hidemyself',
+                               [
+                                       'hidebots' => false,
+                                       'hideliu' => true,
+                                       'hidemyself' => true,
+                               ],
+                       ],
+
+                       [
+                               'minor,hideanons,categorization',
+                               [
+                                       'hideminor' => false,
+                                       'hideanons' => true,
+                                       'hidecategorization' => false,
+                               ]
+                       ],
+
+                       [
+                               'hidehumans,bots,hidecategorization',
+                               [
+                                       'hidehumans' => true,
+                                       'hidebots' => false,
+                                       'hidecategorization' => true,
+                               ],
+                       ],
+
+                       [
+                               'hidemyself,userExpLevel=newcomer;learner,hideminor',
+                               [
+                                       'hidemyself' => true,
+                                       'hideminor' => true,
+                                       'userExpLevel' => 'newcomer;learner',
+                               ],
+                       ],
+               ];
+       }
+}
index 9b0fb6a..011d8a0 100644 (file)
 <?php
+
 /**
  * Test class for SpecialRecentchanges class
  *
- * Copyright © 2011, Antoine Musso
- *
- * @author Antoine Musso
  * @group Database
  *
  * @covers SpecialRecentChanges
  */
-class SpecialRecentchangesTest extends MediaWikiTestCase {
-
+class SpecialRecentchangesTest extends AbstractChangesListSpecialPageTestCase {
        protected function setUp() {
                parent::setUp();
-               $this->setMwGlobals( 'wgRCWatchCategoryMembership', true );
-       }
-
-       /**
-        * @var SpecialRecentChanges
-        */
-       protected $rc;
-
-       /** helper to test SpecialRecentchanges::buildMainQueryConds() */
-       private function assertConditions(
-               $expected,
-               $requestOptions = null,
-               $message = '',
-               $user = null
-       ) {
-               $context = new RequestContext;
-               $context->setRequest( new FauxRequest( $requestOptions ) );
-               if ( $user ) {
-                       $context->setUser( $user );
-               }
-
-               # setup the rc object
-               $this->rc = new SpecialRecentChanges();
-               $this->rc->setContext( $context );
-               $formOptions = $this->rc->setup( null );
 
-               #  Filter out rc_timestamp conditions which depends on the test runtime
-               # This condition is not needed as of march 2, 2011 -- hashar
-               # @todo FIXME: Find a way to generate the correct rc_timestamp
-               $queryConditions = array_filter(
-                       $this->rc->buildMainQueryConds( $formOptions ),
-                       'SpecialRecentchangesTest::filterOutRcTimestampCondition'
-               );
-
-               $this->assertEquals(
-                       self::normalizeCondition( $expected ),
-                       self::normalizeCondition( $queryConditions ),
-                       $message
+               # setup the CLSP object
+               $this->changesListSpecialPage = TestingAccessWrapper::newFromObject(
+                       new SpecialRecentchanges
                );
        }
 
-       private static function normalizeCondition( $conds ) {
-               $normalized = array_map(
-                       function ( $k, $v ) {
-                               return is_numeric( $k ) ? $v : "$k = $v";
-                       },
-                       array_keys( $conds ),
-                       $conds
-               );
-               sort( $normalized );
-               return $normalized;
-       }
+       public function provideParseParameters() {
+               return [
+                       [ 'limit=123', [ 'limit' => '123' ] ],
 
-       /** return false if condition begin with 'rc_timestamp ' */
-       private static function filterOutRcTimestampCondition( $var ) {
-               return ( false === strpos( $var, 'rc_timestamp ' ) );
-       }
+                       [ '234', [ 'limit' => '234' ] ],
 
-       public function testRcNsFilter() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "rc_namespace = '0'",
-                       ],
-                       [
-                               'namespace' => NS_MAIN,
-                       ],
-                       "rc conditions with no options (aka default setting)"
-               );
-       }
-
-       public function testRcNsFilterInversion() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "rc_namespace != '0'",
-                       ],
-                       [
-                               'namespace' => NS_MAIN,
-                               'invert' => 1,
-                       ],
-                       "rc conditions with namespace inverted"
-               );
-       }
-
-       /**
-        * T4429
-        * @dataProvider provideNamespacesAssociations
-        */
-       public function testRcNsFilterAssociation( $ns1, $ns2 ) {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "(rc_namespace = '$ns1' OR rc_namespace = '$ns2')",
-                       ],
-                       [
-                               'namespace' => $ns1,
-                               'associated' => 1,
-                       ],
-                       "rc conditions with namespace inverted"
-               );
-       }
+                       [ 'days=3', [ 'days' => '3' ] ],
 
-       /**
-        * T4429
-        * @dataProvider provideNamespacesAssociations
-        */
-       public function testRcNsFilterAssociationWithInversion( $ns1, $ns2 ) {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "(rc_namespace != '$ns1' AND rc_namespace != '$ns2')",
-                       ],
-                       [
-                               'namespace' => $ns1,
-                               'associated' => 1,
-                               'invert' => 1,
-                       ],
-                       "rc conditions with namespace inverted"
-               );
-       }
+                       [ 'namespace=5', [ 'namespace' => 5 ] ],
 
-       /**
-        * Provides associated namespaces to test recent changes
-        * namespaces association filtering.
-        */
-       public static function provideNamespacesAssociations() {
-               return [ # (NS => Associated_NS)
-                       [ NS_MAIN, NS_TALK ],
-                       [ NS_TALK, NS_MAIN ],
+                       [ 'tagfilter=foo', [ 'tagfilter' => 'foo' ] ],
                ];
        }
-
-       public function testRcHidemyselfFilter() {
-               $user = $this->getTestUser()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_user != '{$user->getId()}'",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidemyself' => 1,
-                       ],
-                       "rc conditions: hidemyself=1 (logged in)",
-                       $user
-               );
-
-               $user = User::newFromName( '10.11.12.13', false );
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_user_text != '10.11.12.13'",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidemyself' => 1,
-                       ],
-                       "rc conditions: hidemyself=1 (anon)",
-                       $user
-               );
-       }
-
-       public function testRcHidebyothersFilter() {
-               $user = $this->getTestUser()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_user = '{$user->getId()}'",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidebyothers' => 1,
-                       ],
-                       "rc conditions: hidebyothers=1 (logged in)",
-                       $user
-               );
-
-               $user = User::newFromName( '10.11.12.13', false );
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_user_text = '10.11.12.13'",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidebyothers' => 1,
-                       ],
-                       "rc conditions: hidebyothers=1 (anon)",
-                       $user
-               );
-       }
-
-       public function testRcHidemyselfHidebyothersFilter() {
-               $user = $this->getTestUser()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_user != '{$user->getId()}'",
-                               "rc_user = '{$user->getId()}'",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidemyself' => 1,
-                               'hidebyothers' => 1,
-                       ],
-                       "rc conditions: hidemyself=1 hidebyothers=1 (logged in)",
-                       $user
-               );
-       }
-
-       public function testRcHidepageedits() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "rc_type != '0'",
-                       ],
-                       [
-                               'hidepageedits' => 1,
-                       ],
-                       "rc conditions: hidepageedits=1"
-               );
-       }
-
-       public function testRcHidenewpages() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "rc_type != '1'",
-                       ],
-                       [
-                               'hidenewpages' => 1,
-                       ],
-                       "rc conditions: hidenewpages=1"
-               );
-       }
-
-       public function testRcHidelog() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                               "rc_type != '3'",
-                       ],
-                       [
-                               'hidelog' => 1,
-                       ],
-                       "rc conditions: hidelog=1"
-               );
-       }
-
-       public function testRcHidehumans() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 1,
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidebots' => 0,
-                               'hidehumans' => 1,
-                       ],
-                       "rc conditions: hidebots=0 hidehumans=1"
-               );
-       }
-
-       public function testRcHidepatrolledDisabledFilter() {
-               $user = $this->getTestUser()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidepatrolled' => 1,
-                       ],
-                       "rc conditions: hidepatrolled=1 (user not allowed)",
-                       $user
-               );
-       }
-
-       public function testRcHideunpatrolledDisabledFilter() {
-               $user = $this->getTestUser()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hideunpatrolled' => 1,
-                       ],
-                       "rc conditions: hideunpatrolled=1 (user not allowed)",
-                       $user
-               );
-       }
-       public function testRcHidepatrolledFilter() {
-               $user = $this->getTestSysop()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_patrolled = 0",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidepatrolled' => 1,
-                       ],
-                       "rc conditions: hidepatrolled=1",
-                       $user
-               );
-       }
-
-       public function testRcHideunpatrolledFilter() {
-               $user = $this->getTestSysop()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_patrolled = 1",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hideunpatrolled' => 1,
-                       ],
-                       "rc conditions: hideunpatrolled=1",
-                       $user
-               );
-       }
-
-       public function testRcHideminorFilter() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_minor = 0",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hideminor' => 1,
-                       ],
-                       "rc conditions: hideminor=1"
-               );
-       }
-
-       public function testRcHidemajorFilter() {
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_minor = 1",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidemajor' => 1,
-                       ],
-                       "rc conditions: hidemajor=1"
-               );
-       }
-
-       // This is probably going to change when we do auto-fix of
-       // filters combinations that don't make sense but for now
-       // it's the behavior therefore it's the test.
-       public function testRcHidepatrolledHideunpatrolledFilter() {
-               $user = $this->getTestSysop()->getUser();
-               $this->assertConditions(
-                       [ # expected
-                               'rc_bot' => 0,
-                               "rc_patrolled = 0",
-                               "rc_patrolled = 1",
-                               "rc_type != '6'",
-                       ],
-                       [
-                               'hidepatrolled' => 1,
-                               'hideunpatrolled' => 1,
-                       ],
-                       "rc conditions: hidepatrolled=1 hideunpatrolled=1",
-                       $user
-               );
-       }
-
-       public function testFilterUserExpLevel() {
-               $this->setMwGlobals( [
-                       'wgLearnerEdits' => 10,
-                       'wgLearnerMemberSince' => 4,
-                       'wgExperiencedUserEdits' => 500,
-                       'wgExperiencedUserMemberSince' => 30,
-               ] );
-
-               $this->createUsers( [
-                       'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
-                       'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
-                       'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
-                       'Learner1' => [ 'edits' => 15, 'days' => 10 ],
-                       'Learner2' => [ 'edits' => 450, 'days' => 20 ],
-                       'Learner3' => [ 'edits' => 460, 'days' => 33 ],
-                       'Learner4' => [ 'edits' => 525, 'days' => 28 ],
-                       'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
-               ] );
-
-               // newcomers only
-               $this->assertArrayEquals(
-                       [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'newcomer' ] )
-               );
-
-               // newcomers and learner
-               $this->assertArrayEquals(
-                       [
-                               'Newcomer1', 'Newcomer2', 'Newcomer3',
-                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
-                       ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'newcomer,learner' ] )
-               );
-
-               // newcomers and more learner
-               $this->assertArrayEquals(
-                       [
-                               'Newcomer1', 'Newcomer2', 'Newcomer3',
-                               'Experienced1',
-                       ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'newcomer,experienced' ] )
-               );
-
-               // learner only
-               $this->assertArrayEquals(
-                       [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'learner' ] )
-               );
-
-               // more experienced only
-               $this->assertArrayEquals(
-                       [ 'Experienced1' ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'experienced' ] )
-               );
-
-               // learner and more experienced
-               $this->assertArrayEquals(
-                       [
-                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
-                               'Experienced1',
-                       ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'learner,experienced' ] )
-               );
-
-               // newcomers, learner, and more experienced
-               $this->assertArrayEquals(
-                       [
-                               'Newcomer1', 'Newcomer2', 'Newcomer3',
-                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
-                               'Experienced1',
-                       ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'newcomer,learner,experienced' ] )
-               );
-
-               // 'all'
-               $this->assertArrayEquals(
-                       [
-                               'Newcomer1', 'Newcomer2', 'Newcomer3',
-                               'Learner1', 'Learner2', 'Learner3', 'Learner4',
-                               'Experienced1',
-                       ],
-                       $this->fetchUsers( [ 'userExpLevel' => 'all' ] )
-               );
-       }
-
-       private function createUsers( $specs ) {
-               $dbw = wfGetDB( DB_MASTER );
-               foreach ( $specs as $name => $spec ) {
-                       User::createNew(
-                               $name,
-                               [
-                                       'editcount' => $spec['edits'],
-                                       'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'] ) ),
-                                       'email' => 'ut',
-                               ]
-                       );
-               }
-       }
-
-       private function fetchUsers( $filters ) {
-               $specialRC = new SpecialRecentChanges();
-
-               $tables = [];
-               $conds = [];
-               $join_conds = [];
-
-               $specialRC->filterOnUserExperienceLevel(
-                       $tables,
-                       $conds,
-                       $join_conds,
-                       $filters
-               );
-
-               $result = wfGetDB( DB_MASTER )->select(
-                       'user',
-                       'user_name',
-                       array_filter( $conds ) + [ 'user_email' => 'ut' ]
-               );
-
-               $usernames = [];
-               foreach ( $result as $row ) {
-                       $usernames[] = $row->user_name;
-               }
-
-               return $usernames;
-       }
-
-       private function daysAgo( $days ) {
-               $secondsPerDay = 86400;
-               return time() - $days * $secondsPerDay;
-       }
 }
index ad0ed54..52ba360 100644 (file)
@@ -1,57 +1,67 @@
 ( function ( mw, $ ) {
-       QUnit.module( 'mediawiki.rcfilters - FiltersViewModel' );
+       QUnit.module( 'mediawiki.rcfilters - FiltersViewModel', QUnit.newMwEnvironment( {
+               messages: {
+                       'group1filter1-label': 'Group 1: Filter 1',
+                       'group1filter1-desc': 'Description of Filter 1 in Group 1',
+                       'group1filter2-label': 'Group 1: Filter 2',
+                       'group1filter2-desc': 'Description of Filter 2 in Group 1',
+                       'group2filter1-label': 'Group 2: Filter 1',
+                       'group2filter1-desc': 'Description of Filter 1 in Group 2',
+                       'group2filter2-label': 'xGroup 2: Filter 2',
+                       'group2filter2-desc': 'Description of Filter 2 in Group 2'
+               }
+       } ) );
 
        QUnit.test( 'Setting up filters', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'group1filter1',
-                                                       label: 'Group 1: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 1'
-                                               },
-                                               {
-                                                       name: 'group1filter2',
-                                                       label: 'Group 1: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'group2filter1',
-                                                       label: 'Group 2: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 2'
-                                               },
-                                               {
-                                                       name: 'group2filter2',
-                                                       label: 'Group 2: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 2'
-                                               }
-                                       ]
-                               },
-                               group3: {
-                                       title: 'Group 3',
-                                       type: 'string_options',
-                                       filters: [
-                                               {
-                                                       name: 'group3filter1',
-                                                       label: 'Group 3: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 3'
-                                               },
-                                               {
-                                                       name: 'group3filter2',
-                                                       label: 'Group 3: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 3'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'group1filter1',
+                                               label: 'Group 1: Filter 1',
+                                               description: 'Description of Filter 1 in Group 1'
+                                       },
+                                       {
+                                               name: 'group1filter2',
+                                               label: 'Group 1: Filter 2',
+                                               description: 'Description of Filter 2 in Group 1'
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'group2filter1',
+                                               label: 'Group 2: Filter 1',
+                                               description: 'Description of Filter 1 in Group 2'
+                                       },
+                                       {
+                                               name: 'group2filter2',
+                                               label: 'Group 2: Filter 2',
+                                               description: 'Description of Filter 2 in Group 2'
+                                       }
+                               ]
+                       }, {
+                               name: 'group3',
+                               title: 'Group 3',
+                               type: 'string_options',
+                               filters: [
+                                       {
+                                               name: 'group3filter1',
+                                               label: 'Group 3: Filter 1',
+                                               description: 'Description of Filter 1 in Group 3'
+                                       },
+                                       {
+                                               name: 'group3filter2',
+                                               label: 'Group 3: Filter 2',
+                                               description: 'Description of Filter 2 in Group 3'
+                                       }
+                               ]
+                       } ],
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
                model.initializeFilters( definition );
 
        QUnit.test( 'Finding matching filters', function ( assert ) {
                var matches,
-                       definition = {
-                               group1: {
-                                       title: 'Group 1 title',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'group1filter1',
-                                                       label: 'Group 1: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 1'
-                                               },
-                                               {
-                                                       name: 'group1filter2',
-                                                       label: 'Group 1: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2 title',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'group2filter1',
-                                                       label: 'Group 2: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 2'
-                                               },
-                                               {
-                                                       name: 'group2filter2',
-                                                       label: 'xGroup 2: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 2'
-                                               }
-                                       ]
-                               }
-                       },
+                       definition = [ {
+                               name: 'group1',
+                               title: 'Group 1 title',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'group1filter1',
+                                               label: 'group1filter1-label',
+                                               description: 'group1filter1-desc'
+                                       },
+                                       {
+                                               name: 'group1filter2',
+                                               label: 'group1filter2-label',
+                                               description: 'group1filter2-desc'
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2 title',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'group2filter1',
+                                               label: 'group2filter1-label',
+                                               description: 'group2filter1-desc'
+                                       },
+                                       {
+                                               name: 'group2filter2',
+                                               label: 'group2filter2-label',
+                                               description: 'group2filter2-desc'
+                                       }
+                               ]
+                       } ],
                        testCases = [
                                {
                                        query: 'group',
        } );
 
        QUnit.test( 'getParametersFromFilters', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter1',
-                                                       label: 'Group 1: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 1'
-                                               },
-                                               {
-                                                       name: 'hidefilter2',
-                                                       label: 'Group 1: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               },
-                                               {
-                                                       name: 'hidefilter3',
-                                                       label: 'Group 1: Filter 3',
-                                                       description: 'Description of Filter 3 in Group 1'
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter4',
-                                                       label: 'Group 2: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 2'
-                                               },
-                                               {
-                                                       name: 'hidefilter5',
-                                                       label: 'Group 2: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 2'
-                                               },
-                                               {
-                                                       name: 'hidefilter6',
-                                                       label: 'Group 2: Filter 3',
-                                                       description: 'Description of Filter 3 in Group 2'
-                                               }
-                                       ]
-                               },
-                               group3: {
-                                       title: 'Group 3',
-                                       type: 'string_options',
-                                       separator: ',',
-                                       filters: [
-                                               {
-                                                       name: 'filter7',
-                                                       label: 'Group 3: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 3'
-                                               },
-                                               {
-                                                       name: 'filter8',
-                                                       label: 'Group 3: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 3'
-                                               },
-                                               {
-                                                       name: 'filter9',
-                                                       label: 'Group 3: Filter 3',
-                                                       description: 'Description of Filter 3 in Group 3'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter1',
+                                               label: 'Group 1: Filter 1',
+                                               description: 'Description of Filter 1 in Group 1'
+                                       },
+                                       {
+                                               name: 'hidefilter2',
+                                               label: 'Group 1: Filter 2',
+                                               description: 'Description of Filter 2 in Group 1'
+                                       },
+                                       {
+                                               name: 'hidefilter3',
+                                               label: 'Group 1: Filter 3',
+                                               description: 'Description of Filter 3 in Group 1'
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter4',
+                                               label: 'Group 2: Filter 1',
+                                               description: 'Description of Filter 1 in Group 2'
+                                       },
+                                       {
+                                               name: 'hidefilter5',
+                                               label: 'Group 2: Filter 2',
+                                               description: 'Description of Filter 2 in Group 2'
+                                       },
+                                       {
+                                               name: 'hidefilter6',
+                                               label: 'Group 2: Filter 3',
+                                               description: 'Description of Filter 3 in Group 2'
+                                       }
+                               ]
+                       }, {
+                               name: 'group3',
+                               title: 'Group 3',
+                               type: 'string_options',
+                               separator: ',',
+                               filters: [
+                                       {
+                                               name: 'filter7',
+                                               label: 'Group 3: Filter 1',
+                                               description: 'Description of Filter 1 in Group 3'
+                                       },
+                                       {
+                                               name: 'filter8',
+                                               label: 'Group 3: Filter 2',
+                                               description: 'Description of Filter 2 in Group 3'
+                                       },
+                                       {
+                                               name: 'filter9',
+                                               label: 'Group 3: Filter 3',
+                                               description: 'Description of Filter 3 in Group 3'
+                                       }
+                               ]
+                       } ],
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
                model.initializeFilters( definition );
                                hidefilter4: 0,
                                hidefilter5: 0,
                                hidefilter6: 0,
-                               group3: 'all'
+                               group3: ''
                        },
-                       'Unselected filters return all parameters falsey or \'all\'.'
+                       'Unselected filters return all parameters falsey or \'\'.'
                );
 
                // Select 1 filter
                                hidefilter4: 0,
                                hidefilter5: 0,
                                hidefilter6: 0,
-                               group3: 'all'
+                               group3: ''
                        },
                        'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
                );
                                hidefilter4: 0,
                                hidefilter5: 0,
                                hidefilter6: 0,
-                               group3: 'all'
+                               group3: ''
                        },
                        'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
                );
                                hidefilter4: 0,
                                hidefilter5: 0,
                                hidefilter6: 0,
-                               group3: 'all'
+                               group3: ''
                        },
                        'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
                );
        } );
 
        QUnit.test( 'getFiltersFromParameters', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter1',
-                                                       label: 'Show filter 1',
-                                                       description: 'Description of Filter 1 in Group 1',
-                                                       default: true
-                                               },
-                                               {
-                                                       name: 'hidefilter2',
-                                                       label: 'Show filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               },
-                                               {
-                                                       name: 'hidefilter3',
-                                                       label: 'Show filter 3',
-                                                       description: 'Description of Filter 3 in Group 1',
-                                                       default: true
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter4',
-                                                       label: 'Show filter 4',
-                                                       description: 'Description of Filter 1 in Group 2'
-                                               },
-                                               {
-                                                       name: 'hidefilter5',
-                                                       label: 'Show filter 5',
-                                                       description: 'Description of Filter 2 in Group 2',
-                                                       default: true
-                                               },
-                                               {
-                                                       name: 'hidefilter6',
-                                                       label: 'Show filter 6',
-                                                       description: 'Description of Filter 3 in Group 2'
-                                               }
-                                       ]
-                               },
-                               group3: {
-                                       title: 'Group 3',
-                                       type: 'string_options',
-                                       separator: ',',
-                                       filters: [
-                                               {
-                                                       name: 'filter7',
-                                                       label: 'Group 3: Filter 1',
-                                                       description: 'Description of Filter 1 in Group 3'
-                                               },
-                                               {
-                                                       name: 'filter8',
-                                                       label: 'Group 3: Filter 2',
-                                                       description: 'Description of Filter 2 in Group 3',
-                                                       default: true
-                                               },
-                                               {
-                                                       name: 'filter9',
-                                                       label: 'Group 3: Filter 3',
-                                                       description: 'Description of Filter 3 in Group 3'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter1',
+                                               label: 'Show filter 1',
+                                               description: 'Description of Filter 1 in Group 1',
+                                               default: true
+                                       },
+                                       {
+                                               name: 'hidefilter2',
+                                               label: 'Show filter 2',
+                                               description: 'Description of Filter 2 in Group 1'
+                                       },
+                                       {
+                                               name: 'hidefilter3',
+                                               label: 'Show filter 3',
+                                               description: 'Description of Filter 3 in Group 1',
+                                               default: true
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter4',
+                                               label: 'Show filter 4',
+                                               description: 'Description of Filter 1 in Group 2'
+                                       },
+                                       {
+                                               name: 'hidefilter5',
+                                               label: 'Show filter 5',
+                                               description: 'Description of Filter 2 in Group 2',
+                                               default: true
+                                       },
+                                       {
+                                               name: 'hidefilter6',
+                                               label: 'Show filter 6',
+                                               description: 'Description of Filter 3 in Group 2'
+                                       }
+                               ]
+                       }, {
+
+                               name: 'group3',
+                               title: 'Group 3',
+                               type: 'string_options',
+                               separator: ',',
+                               filters: [
+                                       {
+                                               name: 'filter7',
+                                               label: 'Group 3: Filter 1',
+                                               description: 'Description of Filter 1 in Group 3'
+                                       },
+                                       {
+                                               name: 'filter8',
+                                               label: 'Group 3: Filter 2',
+                                               description: 'Description of Filter 2 in Group 3',
+                                               default: true
+                                       },
+                                       {
+                                               name: 'filter9',
+                                               label: 'Group 3: Filter 3',
+                                               description: 'Description of Filter 3 in Group 3'
+                                       }
+                               ]
+                       } ],
                        defaultFilterRepresentation = {
                                // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
                                hidefilter1: false,
        } );
 
        QUnit.test( 'sanitizeStringOptionGroup', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'string_options',
-                                       filters: [
-                                               {
-                                                       name: 'filter1',
-                                                       label: 'Show filter 1',
-                                                       description: 'Description of Filter 1 in Group 1'
-                                               },
-                                               {
-                                                       name: 'filter2',
-                                                       label: 'Show filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               },
-                                               {
-                                                       name: 'filter3',
-                                                       label: 'Show filter 3',
-                                                       description: 'Description of Filter 3 in Group 1'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'string_options',
+                               filters: [
+                                       {
+                                               name: 'filter1',
+                                               label: 'Show filter 1',
+                                               description: 'Description of Filter 1 in Group 1'
+                                       },
+                                       {
+                                               name: 'filter2',
+                                               label: 'Show filter 2',
+                                               description: 'Description of Filter 2 in Group 1'
+                                       },
+                                       {
+                                               name: 'filter3',
+                                               label: 'Show filter 3',
+                                               description: 'Description of Filter 3 in Group 1'
+                                       }
+                               ]
+                       } ],
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
                model.initializeFilters( definition );
        } );
 
        QUnit.test( 'setFiltersToDefaults', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter1',
-                                                       label: 'Show filter 1',
-                                                       description: 'Description of Filter 1 in Group 1',
-                                                       default: true
-                                               },
-                                               {
-                                                       name: 'hidefilter2',
-                                                       label: 'Show filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               },
-                                               {
-                                                       name: 'hidefilter3',
-                                                       label: 'Show filter 3',
-                                                       description: 'Description of Filter 3 in Group 1',
-                                                       default: true
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'hidefilter4',
-                                                       label: 'Show filter 4',
-                                                       description: 'Description of Filter 1 in Group 2'
-                                               },
-                                               {
-                                                       name: 'hidefilter5',
-                                                       label: 'Show filter 5',
-                                                       description: 'Description of Filter 2 in Group 2',
-                                                       default: true
-                                               },
-                                               {
-                                                       name: 'hidefilter6',
-                                                       label: 'Show filter 6',
-                                                       description: 'Description of Filter 3 in Group 2'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter1',
+                                               label: 'Show filter 1',
+                                               description: 'Description of Filter 1 in Group 1',
+                                               default: true
+                                       },
+                                       {
+                                               name: 'hidefilter2',
+                                               label: 'Show filter 2',
+                                               description: 'Description of Filter 2 in Group 1'
+                                       },
+                                       {
+                                               name: 'hidefilter3',
+                                               label: 'Show filter 3',
+                                               description: 'Description of Filter 3 in Group 1',
+                                               default: true
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'hidefilter4',
+                                               label: 'Show filter 4',
+                                               description: 'Description of Filter 1 in Group 2'
+                                       },
+                                       {
+                                               name: 'hidefilter5',
+                                               label: 'Show filter 5',
+                                               description: 'Description of Filter 2 in Group 2',
+                                               default: true
+                                       },
+                                       {
+                                               name: 'hidefilter6',
+                                               label: 'Show filter 6',
+                                               description: 'Description of Filter 3 in Group 2'
+                                       }
+                               ]
+                       } ],
                        defaultFilterRepresentation = {
                                // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
                                hidefilter1: false,
        } );
 
        QUnit.test( 'Filter interaction: subsets', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'string_options',
-                                       filters: [
-                                               {
-                                                       name: 'filter1',
-                                                       label: 'Show filter 1',
-                                                       description: 'Description of Filter 1 in Group 1',
-                                                       subset: [ 'filter2', 'filter5' ]
-                                               },
-                                               {
-                                                       name: 'filter2',
-                                                       label: 'Show filter 2',
-                                                       description: 'Description of Filter 2 in Group 1'
-                                               },
-                                               {
-                                                       name: 'filter3',
-                                                       label: 'Show filter 3',
-                                                       description: 'Description of Filter 3 in Group 1'
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'filter4',
-                                                       label: 'Show filter 4',
-                                                       description: 'Description of Filter 1 in Group 2',
-                                                       subset: [ 'filter3', 'filter5' ]
-                                               },
-                                               {
-                                                       name: 'filter5',
-                                                       label: 'Show filter 5',
-                                                       description: 'Description of Filter 2 in Group 2'
-                                               },
-                                               {
-                                                       name: 'filter6',
-                                                       label: 'Show filter 6',
-                                                       description: 'Description of Filter 3 in Group 2'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'string_options',
+                               filters: [
+                                       {
+                                               name: 'filter1',
+                                               label: 'Show filter 1',
+                                               description: 'Description of Filter 1 in Group 1',
+                                               subset: [
+                                                       {
+                                                               group: 'group1',
+                                                               filter: 'filter2'
+                                                       },
+                                                       {
+                                                               group: 'group1',
+                                                               filter: 'filter3'
+                                                       }
+                                               ]
+                                       },
+                                       {
+                                               name: 'filter2',
+                                               label: 'Show filter 2',
+                                               description: 'Description of Filter 2 in Group 1',
+                                               subset: [
+                                                       {
+                                                               group: 'group1',
+                                                               filter: 'filter3'
+                                                       }
+                                               ]
+                                       },
+                                       {
+                                               name: 'filter3',
+                                               label: 'Show filter 3',
+                                               description: 'Description of Filter 3 in Group 1'
+                                       }
+                               ]
+                       } ],
                        baseFullState = {
                                filter1: { selected: false, conflicted: false, included: false },
                                filter2: { selected: false, conflicted: false, included: false },
-                               filter3: { selected: false, conflicted: false, included: false },
-                               filter4: { selected: false, conflicted: false, included: false },
-                               filter5: { selected: false, conflicted: false, included: false },
-                               filter6: { selected: false, conflicted: false, included: false }
+                               filter3: { selected: false, conflicted: false, included: false }
                        },
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
                        $.extend( true, {}, baseFullState, {
                                filter1: { selected: true },
                                filter2: { included: true },
-                               filter5: { included: true }
+                               filter3: { included: true }
                        } ),
                        'Filters with subsets are represented in the model.'
                );
 
                // Select another filter that has a subset with the same previous filter
                model.toggleFiltersSelected( {
-                       filter4: true
+                       filter2: true
                } );
-               model.reassessFilterInteractions( model.getItemByName( 'filter4' ) );
+               model.reassessFilterInteractions( model.getItemByName( 'filter2' ) );
                assert.deepEqual(
                        model.getFullState(),
                        $.extend( true, {}, baseFullState, {
                                filter1: { selected: true },
-                               filter2: { included: true },
-                               filter3: { included: true },
-                               filter4: { selected: true },
-                               filter5: { included: true }
+                               filter2: { selected: true, included: true },
+                               filter3: { included: true }
                        } ),
                        'Filters that have multiple subsets are represented.'
                );
                assert.deepEqual(
                        model.getFullState(),
                        $.extend( true, {}, baseFullState, {
-                               filter2: { included: false },
-                               filter3: { included: true },
-                               filter4: { selected: true },
-                               filter5: { included: true }
+                               filter2: { selected: true, included: false },
+                               filter3: { included: true }
                        } ),
                        'Removing a filter only un-includes its subset if there is no other filter affecting.'
                );
 
                model.toggleFiltersSelected( {
-                       filter4: false
+                       filter2: false
                } );
-               model.reassessFilterInteractions( model.getItemByName( 'filter4' ) );
+               model.reassessFilterInteractions( model.getItemByName( 'filter2' ) );
                assert.deepEqual(
                        model.getFullState(),
                        baseFullState,
        } );
 
        QUnit.test( 'Filter interaction: full coverage', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'string_options',
-                                       fullCoverage: false,
-                                       filters: [
-                                               { name: 'filter1' },
-                                               { name: 'filter2' },
-                                               { name: 'filter3' }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       fullCoverage: true,
-                                       filters: [
-                                               { name: 'filter4' },
-                                               { name: 'filter5' },
-                                               { name: 'filter6' }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'string_options',
+                               fullCoverage: false,
+                               filters: [
+                                       { name: 'filter1', label: '1', description: '1' },
+                                       { name: 'filter2', label: '2', description: '2' },
+                                       { name: 'filter3', label: '3', description: '3' }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               fullCoverage: true,
+                               filters: [
+                                       { name: 'filter4', label: '4', description: '4' },
+                                       { name: 'filter5', label: '5', description: '5' },
+                                       { name: 'filter6', label: '6', description: '6' }
+                               ]
+                       } ],
                        model = new mw.rcfilters.dm.FiltersViewModel(),
                        isCapsuleItemMuted = function ( filterName ) {
                                var itemModel = model.getItemByName( filterName ),
        } );
 
        QUnit.test( 'Filter interaction: conflicts', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'string_options',
-                                       filters: [
-                                               {
-                                                       name: 'filter1',
-                                                       conflicts: [ 'filter2', 'filter4' ]
-                                               },
-                                               {
-                                                       name: 'filter2',
-                                                       conflicts: [ 'filter6' ]
-                                               },
-                                               {
-                                                       name: 'filter3'
-                                               }
-                                       ]
-                               },
-                               group2: {
-                                       title: 'Group 2',
-                                       type: 'send_unselected_if_any',
-                                       filters: [
-                                               {
-                                                       name: 'filter4'
-                                               },
-                                               {
-                                                       name: 'filter5',
-                                                       conflicts: [ 'filter3' ]
-                                               },
-                                               {
-                                                       name: 'filter6'
-                                               }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'string_options',
+                               filters: [
+                                       {
+                                               name: 'filter1',
+                                               label: '1',
+                                               description: '1',
+                                               conflicts: [ 'filter2', 'filter4' ]
+                                       },
+                                       {
+                                               name: 'filter2',
+                                               label: '2',
+                                               description: '2',
+                                               conflicts: [ 'filter6' ]
+                                       },
+                                       {
+                                               name: 'filter3',
+                                               label: '3',
+                                               description: '3'
+                                       }
+                               ]
+                       }, {
+                               name: 'group2',
+                               title: 'Group 2',
+                               type: 'send_unselected_if_any',
+                               filters: [
+                                       {
+                                               name: 'filter4',
+                                               label: '1',
+                                               description: '1'
+                                       },
+                                       {
+                                               name: 'filter5',
+                                               label: '5',
+                                               description: '5',
+                                               conflicts: [ 'filter3' ]
+                                       },
+                                       {
+                                               name: 'filter6',
+                                               label: '6',
+                                               description: '6'
+                                       }
+                               ]
+                       } ],
                        baseFullState = {
                                filter1: { selected: false, conflicted: false, included: false },
                                filter2: { selected: false, conflicted: false, included: false },
        } );
 
        QUnit.test( 'Filter highlights', function ( assert ) {
-               var definition = {
-                               group1: {
-                                       title: 'Group 1',
-                                       type: 'string_options',
-                                       filters: [
-                                               { name: 'filter1', class: 'class1' },
-                                               { name: 'filter2', class: 'class2' },
-                                               { name: 'filter3', class: 'class3' },
-                                               { name: 'filter4', class: 'class4' },
-                                               { name: 'filter5', class: 'class5' },
-                                               { name: 'filter6' }
-                                       ]
-                               }
-                       },
+               var definition = [ {
+                               name: 'group1',
+                               title: 'Group 1',
+                               type: 'string_options',
+                               filters: [
+                                       { name: 'filter1', cssClass: 'class1', label: '1', description: '1' },
+                                       { name: 'filter2', cssClass: 'class2', label: '2', description: '2' },
+                                       { name: 'filter3', cssClass: 'class3', label: '3', description: '3' },
+                                       { name: 'filter4', cssClass: 'class4', label: '4', description: '4' },
+                                       { name: 'filter5', cssClass: 'class5', label: '5', description: '5' },
+                                       { name: 'filter6', label: '6', description: '6' }
+                               ]
+                       } ],
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
                model.initializeFilters( definition );
index 6f9af76..477b04d 100644 (file)
@@ -5,6 +5,11 @@
                },
                teardown: function () {
                        mw.loader.store.enabled = false;
+                       // Teardown for StringSet shim test
+                       if ( this.nativeSet ) {
+                               window.Set = this.nativeSet;
+                               mw.redefineFallbacksForTest();
+                       }
                }
        } ) );
 
                } );
        } );
 
-       QUnit.test( '.using() Error: Circular dependency', function ( assert ) {
+       // Covers mw.loader#sortDependencies (with native Set if available)
+       QUnit.test( '.using() Error: Circular dependency [StringSet default]', function ( assert ) {
                var done = assert.async();
 
                mw.loader.register( [
                .always( done );
        } );
 
+       // @covers mw.loader#sortDependencies (with fallback shim)
+       QUnit.test( '.using() Error: Circular dependency [StringSet shim]', function ( assert ) {
+               var done = assert.async();
+
+               if ( !window.Set ) {
+                       assert.expect( 0 );
+                       done();
+                       return;
+               }
+
+               this.nativeSet = window.Set;
+               window.Set = undefined;
+               mw.redefineFallbacksForTest();
+
+               mw.loader.register( [
+                       [ 'test.shim.circle1', '0', [ 'test.shim.circle2' ] ],
+                       [ 'test.shim.circle2', '0', [ 'test.shim.circle3' ] ],
+                       [ 'test.shim.circle3', '0', [ 'test.shim.circle1' ] ]
+               ] );
+               mw.loader.using( 'test.shim.circle3' ).then(
+                       function done() {
+                               assert.ok( false, 'Unexpected resolution, expected error.' );
+                       },
+                       function fail( e ) {
+                               assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
+                       }
+               )
+               .always( done );
+       } );
+
        QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
                mw.loader.register( [
                        [ 'test.circleA', '0', [ 'test.circleB' ] ],