Merge "API: Update ApiTag, fix error handling"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 16 Apr 2015 23:54:59 +0000 (23:54 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 16 Apr 2015 23:54:59 +0000 (23:54 +0000)
52 files changed:
RELEASE-NOTES-1.26
autoload.php
docs/hooks.txt
includes/ChangeTags.php [deleted file]
includes/MessageBlobStore.php [deleted file]
includes/User.php
includes/WebStart.php
includes/actions/HistoryAction.php
includes/api/ApiHelp.php
includes/api/ApiMain.php
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/ksh.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/cache/MessageBlobStore.php [new file with mode: 0644]
includes/changetags/ChangeTags.php [new file with mode: 0644]
includes/profiler/SectionProfiler.php
includes/specials/SpecialVersion.php
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/cs.json
languages/i18n/cv.json
languages/i18n/de.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/ia.json
languages/i18n/khw.json
languages/i18n/ksh.json
languages/i18n/lb.json
languages/i18n/lrc.json
languages/i18n/mk.json
languages/i18n/nap.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sv.json
languages/i18n/uk.json
languages/i18n/yi.json

index 89dad11..bf9341d 100644 (file)
@@ -34,6 +34,7 @@ changes to languages because of Bugzilla reports.
 === Other changes in 1.26 ===
 * ChangeTags::tagDescription() will return false if the interface message
   for the tag is disabled.
+* Added PageHistoryPager::doBatchLookups hook.
 
 == Compatibility ==
 
index 93f8e43..38d1ac0 100644 (file)
@@ -205,7 +205,7 @@ $wgAutoloadLocalClasses = array(
        'CdbWriter' => __DIR__ . '/includes/CdbCompat.php',
        'CgzCopyTransaction' => __DIR__ . '/maintenance/storage/recompressTracked.php',
        'ChangePassword' => __DIR__ . '/maintenance/changePassword.php',
-       'ChangeTags' => __DIR__ . '/includes/ChangeTags.php',
+       'ChangeTags' => __DIR__ . '/includes/changetags/ChangeTags.php',
        'ChangeTagsList' => __DIR__ . '/includes/changetags/ChangeTagsList.php',
        'ChangeTagsLogItem' => __DIR__ . '/includes/changetags/ChangeTagsLogItem.php',
        'ChangeTagsLogList' => __DIR__ . '/includes/changetags/ChangeTagsLogList.php',
@@ -770,7 +770,7 @@ $wgAutoloadLocalClasses = array(
        'MergeLogFormatter' => __DIR__ . '/includes/logging/MergeLogFormatter.php',
        'MergeMessageFileList' => __DIR__ . '/maintenance/mergeMessageFileList.php',
        'Message' => __DIR__ . '/includes/Message.php',
-       'MessageBlobStore' => __DIR__ . '/includes/MessageBlobStore.php',
+       'MessageBlobStore' => __DIR__ . '/includes/cache/MessageBlobStore.php',
        'MessageCache' => __DIR__ . '/includes/cache/MessageCache.php',
        'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
        'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
index 877b7ed..6f59b2d 100644 (file)
@@ -2112,6 +2112,13 @@ constructed.
 $pager: the pager
 $queryInfo: the query parameters
 
+'PageHistoryPager::doBatchLookups': Called after the pager query was run, before
+any output is generated, to allow batch lookups for prefetching information
+needed for display. If the hook handler returns false, the regular behavior of
+doBatchLookups() is skipped.
+$pager: the PageHistoryPager
+$result: a ResultWrapper representing the query result
+
 'PageRenderingHash': Alter the parser cache option hash key. A parser extension
 which depends on user options should install this hook and append its values to
 the key.
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
deleted file mode 100644 (file)
index 43f957c..0000000
+++ /dev/null
@@ -1,1222 +0,0 @@
-<?php
-/**
- * Recent changes tagging.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Change tagging
- */
-
-class ChangeTags {
-       /**
-        * Can't delete tags with more than this many uses. Similar in intent to
-        * the bigdelete user right
-        * @todo Use the job queue for tag deletion to avoid this restriction
-        */
-       const MAX_DELETE_USES = 5000;
-
-       /**
-        * Creates HTML for the given tags
-        *
-        * @param string $tags Comma-separated list of tags
-        * @param string $page A label for the type of action which is being displayed,
-        *   for example: 'history', 'contributions' or 'newpages'
-        * @return array Array with two items: (html, classes)
-        *   - html: String: HTML for displaying the tags (empty string when param $tags is empty)
-        *   - classes: Array of strings: CSS classes used in the generated html, one class for each tag
-        */
-       public static function formatSummaryRow( $tags, $page ) {
-               global $wgLang;
-
-               if ( !$tags ) {
-                       return array( '', array() );
-               }
-
-               $classes = array();
-
-               $tags = explode( ',', $tags );
-               $displayTags = array();
-               foreach ( $tags as $tag ) {
-                       if ( !$tag ) {
-                               continue;
-                       }
-                       $description = self::tagDescription( $tag );
-                       if ( $description === false ) {
-                               continue;
-                       }
-                       $displayTags[] = Xml::tags(
-                               'span',
-                               array( 'class' => 'mw-tag-marker ' .
-                                                               Sanitizer::escapeClass( "mw-tag-marker-$tag" ) ),
-                               $description
-                       );
-                       $classes[] = Sanitizer::escapeClass( "mw-tag-$tag" );
-               }
-
-               if ( !$displayTags ) {
-                       return array( '', array() );
-               }
-
-               $markers = wfMessage( 'tag-list-wrapper' )
-                       ->numParams( count( $displayTags ) )
-                       ->rawParams( $wgLang->commaList( $displayTags ) )
-                       ->parse();
-               $markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
-
-               return array( $markers, $classes );
-       }
-
-       /**
-        * Get a short description for a tag.
-        *
-        * Checks if message key "mediawiki:tag-$tag" exists. If it does not,
-        * returns the HTML-escaped tag name. Uses the message if the message
-        * exists, provided it is not disabled. If the message is disabled,
-        * we consider the tag hidden, and return false.
-        *
-        * @param string $tag Tag
-        * @return string|bool Tag description or false if tag is to be hidden.
-        * @since 1.25 Returns false if tag is to be hidden.
-        */
-       public static function tagDescription( $tag ) {
-               $msg = wfMessage( "tag-$tag" );
-               if ( !$msg->exists() ) {
-                       // No such message, so return the HTML-escaped tag name.
-                       return htmlspecialchars( $tag );
-               }
-               if ( $msg->isDisabled() ) {
-                       // The message exists but is disabled, hide the tag.
-                       return false;
-               }
-
-               // Message exists and isn't disabled, use it.
-               return $msg->parse();
-       }
-
-       /**
-        * Add tags to a change given its rc_id, rev_id and/or log_id
-        *
-        * @param string|array $tags Tags to add to the change
-        * @param int|null $rc_id The rc_id of the change to add the tags to
-        * @param int|null $rev_id The rev_id of the change to add the tags to
-        * @param int|null $log_id The log_id of the change to add the tags to
-        * @param string $params Params to put in the ct_params field of table 'change_tag'
-        *
-        * @throws MWException
-        * @return bool False if no changes are made, otherwise true
-        */
-       public static function addTags( $tags, $rc_id = null, $rev_id = null,
-               $log_id = null, $params = null
-       ) {
-               $result = self::updateTags( $tags, null, $rc_id, $rev_id, $log_id, $params );
-               return (bool)$result[0];
-       }
-
-       /**
-        * Add and remove tags to/from a change given its rc_id, rev_id and/or log_id,
-        * without verifying that the tags exist or are valid. If a tag is present in
-        * both $tagsToAdd and $tagsToRemove, it will be removed.
-        *
-        * This function should only be used by extensions to manipulate tags they
-        * have registered using the ListDefinedTags hook. When dealing with user
-        * input, call updateTagsWithChecks() instead.
-        *
-        * @param string|array|null $tagsToAdd Tags to add to the change
-        * @param string|array|null $tagsToRemove Tags to remove from the change
-        * @param int|null &$rc_id The rc_id of the change to add the tags to.
-        * Pass a variable whose value is null if the rc_id is not relevant or unknown.
-        * @param int|null &$rev_id The rev_id of the change to add the tags to.
-        * Pass a variable whose value is null if the rev_id is not relevant or unknown.
-        * @param int|null &$log_id The log_id of the change to add the tags to.
-        * Pass a variable whose value is null if the log_id is not relevant or unknown.
-        * @param string $params Params to put in the ct_params field of table
-        * 'change_tag' when adding tags
-        *
-        * @throws MWException When $rc_id, $rev_id and $log_id are all null
-        * @return array Index 0 is an array of tags actually added, index 1 is an
-        * array of tags actually removed, index 2 is an array of tags present on the
-        * revision or log entry before any changes were made
-        *
-        * @since 1.25
-        */
-       public static function updateTags( $tagsToAdd, $tagsToRemove, &$rc_id = null,
-               &$rev_id = null, &$log_id = null, $params = null ) {
-
-               $tagsToAdd = array_filter( (array)$tagsToAdd ); // Make sure we're submitting all tags...
-               $tagsToRemove = array_filter( (array)$tagsToRemove );
-
-               if ( !$rc_id && !$rev_id && !$log_id ) {
-                       throw new MWException( 'At least one of: RCID, revision ID, and log ID MUST be ' .
-                               'specified when adding or removing a tag from a change!' );
-               }
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               // Might as well look for rcids and so on.
-               if ( !$rc_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
-                       if ( $log_id ) {
-                               $rc_id = $dbw->selectField(
-                                       'recentchanges',
-                                       'rc_id',
-                                       array( 'rc_logid' => $log_id ),
-                                       __METHOD__
-                               );
-                       } elseif ( $rev_id ) {
-                               $rc_id = $dbw->selectField(
-                                       'recentchanges',
-                                       'rc_id',
-                                       array( 'rc_this_oldid' => $rev_id ),
-                                       __METHOD__
-                               );
-                       }
-               } elseif ( !$log_id && !$rev_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
-                       $log_id = $dbw->selectField(
-                               'recentchanges',
-                               'rc_logid',
-                               array( 'rc_id' => $rc_id ),
-                               __METHOD__
-                       );
-                       $rev_id = $dbw->selectField(
-                               'recentchanges',
-                               'rc_this_oldid',
-                               array( 'rc_id' => $rc_id ),
-                               __METHOD__
-                       );
-               }
-
-               // update the tag_summary row
-               $prevTags = array();
-               if ( !self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $rc_id, $rev_id,
-                       $log_id, $prevTags ) ) {
-
-                       // nothing to do
-                       return array( array(), array(), $prevTags );
-               }
-
-               // insert a row into change_tag for each new tag
-               if ( count( $tagsToAdd ) ) {
-                       $tagsRows = array();
-                       foreach ( $tagsToAdd as $tag ) {
-                               // Filter so we don't insert NULLs as zero accidentally.
-                               // Keep in mind that $rc_id === null means "I don't care/know about the
-                               // rc_id, just delete $tag on this revision/log entry". It doesn't
-                               // mean "only delete tags on this revision/log WHERE rc_id IS NULL".
-                               $tagsRows[] = array_filter(
-                                       array(
-                                               'ct_tag' => $tag,
-                                               'ct_rc_id' => $rc_id,
-                                               'ct_log_id' => $log_id,
-                                               'ct_rev_id' => $rev_id,
-                                               'ct_params' => $params
-                                       )
-                               );
-                       }
-
-                       $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array( 'IGNORE' ) );
-               }
-
-               // delete from change_tag
-               if ( count( $tagsToRemove ) ) {
-                       foreach ( $tagsToRemove as $tag ) {
-                               $conds = array_filter(
-                                       array(
-                                               'ct_tag' => $tag,
-                                               'ct_rc_id' => $rc_id,
-                                               'ct_log_id' => $log_id,
-                                               'ct_rev_id' => $rev_id
-                                       )
-                               );
-                               $dbw->delete( 'change_tag', $conds, __METHOD__ );
-                       }
-               }
-
-               self::purgeTagUsageCache();
-               return array( $tagsToAdd, $tagsToRemove, $prevTags );
-       }
-
-       /**
-        * Adds or removes a given set of tags to/from the relevant row of the
-        * tag_summary table. Modifies the tagsToAdd and tagsToRemove arrays to
-        * reflect the tags that were actually added and/or removed.
-        *
-        * @param array &$tagsToAdd
-        * @param array &$tagsToRemove If a tag is present in both $tagsToAdd and
-        * $tagsToRemove, it will be removed
-        * @param int|null $rc_id Null if not known or not applicable
-        * @param int|null $rev_id Null if not known or not applicable
-        * @param int|null $log_id Null if not known or not applicable
-        * @param array &$prevTags Optionally outputs a list of the tags that were
-        * in the tag_summary row to begin with
-        * @return bool True if any modifications were made, otherwise false
-        * @since 1.25
-        */
-       protected static function updateTagSummaryRow( &$tagsToAdd, &$tagsToRemove,
-               $rc_id, $rev_id, $log_id, &$prevTags = array() ) {
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $tsConds = array_filter( array(
-                       'ts_rc_id' => $rc_id,
-                       'ts_rev_id' => $rev_id,
-                       'ts_log_id' => $log_id
-               ) );
-
-               // Can't both add and remove a tag at the same time...
-               $tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
-
-               // Update the summary row.
-               // $prevTags can be out of date on slaves, especially when addTags is called consecutively,
-               // causing loss of tags added recently in tag_summary table.
-               $prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
-               $prevTags = $prevTags ? $prevTags : '';
-               $prevTags = array_filter( explode( ',', $prevTags ) );
-
-               // add tags
-               $tagsToAdd = array_values( array_diff( $tagsToAdd, $prevTags ) );
-               $newTags = array_unique( array_merge( $prevTags, $tagsToAdd ) );
-
-               // remove tags
-               $tagsToRemove = array_values( array_intersect( $tagsToRemove, $newTags ) );
-               $newTags = array_values( array_diff( $newTags, $tagsToRemove ) );
-
-               sort( $prevTags );
-               sort( $newTags );
-               if ( $prevTags == $newTags ) {
-                       // No change.
-                       return false;
-               }
-
-               if ( !$newTags ) {
-                       // no tags left, so delete the row altogether
-                       $dbw->delete( 'tag_summary', $tsConds, __METHOD__ );
-               } else {
-                       $dbw->replace( 'tag_summary',
-                               array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
-                               array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ),
-                               __METHOD__
-                       );
-               }
-
-               return true;
-       }
-
-       /**
-        * Helper function to generate a fatal status with a 'not-allowed' type error.
-        *
-        * @param string $msgOne Message key to use in the case of one tag
-        * @param string $msgMulti Message key to use in the case of more than one tag
-        * @param array $tags Restricted tags (passed as $1 into the message, count of
-        * $tags passed as $2)
-        * @return Status
-        * @since 1.25
-        */
-       protected static function restrictedTagError( $msgOne, $msgMulti, $tags ) {
-               $lang = RequestContext::getMain()->getLanguage();
-               $count = count( $tags );
-               return Status::newFatal( ( $count > 1 ) ? $msgMulti : $msgOne,
-                       $lang->commaList( $tags ), $count );
-       }
-
-       /**
-        * Is it OK to allow the user to apply all the specified tags at the same time
-        * as they edit/make the change?
-        *
-        * @param array $tags Tags that you are interested in applying
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canAddTagsAccompanyingChange( array $tags,
-               User $user = null ) {
-
-               if ( !is_null( $user ) && !$user->isAllowed( 'applychangetags' ) ) {
-                       return Status::newFatal( 'tags-apply-no-permission' );
-               }
-
-               // to be applied, a tag has to be explicitly defined
-               // @todo Allow extensions to define tags that can be applied by users...
-               $allowedTags = self::listExplicitlyDefinedTags();
-               $disallowedTags = array_diff( $tags, $allowedTags );
-               if ( $disallowedTags ) {
-                       return self::restrictedTagError( 'tags-apply-not-allowed-one',
-                               'tags-apply-not-allowed-multi', $disallowedTags );
-               }
-
-               return Status::newGood();
-       }
-
-       /**
-        * Adds tags to a given change, checking whether it is allowed first, but
-        * without adding a log entry. Useful for cases where the tag is being added
-        * along with the action that generated the change (e.g. tagging an edit as
-        * it is being made).
-        *
-        * Extensions should not use this function, unless directly handling a user
-        * request to add a particular tag. Normally, extensions should call
-        * ChangeTags::updateTags() instead.
-        *
-        * @param array $tags Tags to apply
-        * @param int|null $rc_id The rc_id of the change to add the tags to
-        * @param int|null $rev_id The rev_id of the change to add the tags to
-        * @param int|null $log_id The log_id of the change to add the tags to
-        * @param string $params Params to put in the ct_params field of table
-        * 'change_tag' when adding tags
-        * @param User $user Who to give credit for the action
-        * @return Status
-        * @since 1.25
-        */
-       public static function addTagsAccompanyingChangeWithChecks( array $tags,
-               $rc_id, $rev_id, $log_id, $params, User $user ) {
-
-               // are we allowed to do this?
-               $result = self::canAddTagsAccompanyingChange( $tags, $user );
-               if ( !$result->isOK() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // do it!
-               self::addTags( $tagsToAdd, $rc_id, $rev_id, $log_id, $params );
-
-               return Status::newGood( true );
-       }
-
-       /**
-        * Is it OK to allow the user to adds and remove the given tags tags to/from a
-        * change?
-        *
-        * @param array $tagsToAdd Tags that you are interested in adding
-        * @param array $tagsToRemove Tags that you are interested in removing
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canUpdateTags( array $tagsToAdd, array $tagsToRemove,
-               User $user = null ) {
-
-               if ( !is_null( $user ) && !$user->isAllowed( 'changetags' ) ) {
-                       return Status::newFatal( 'tags-update-no-permission' );
-               }
-
-               // to be added, a tag has to be explicitly defined
-               // @todo Allow extensions to define tags that can be applied by users...
-               $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
-               $diff = array_diff( $tagsToAdd, $explicitlyDefinedTags );
-               if ( $diff ) {
-                       return self::restrictedTagError( 'tags-update-add-not-allowed-one',
-                               'tags-update-add-not-allowed-multi', $diff );
-               }
-
-               // to be removed, a tag has to be either explicitly defined or not defined
-               // at all
-               $definedTags = self::listDefinedTags();
-               $diff = array_diff( $tagsToRemove, $explicitlyDefinedTags );
-               if ( $diff ) {
-                       $intersect = array_intersect( $diff, $definedTags );
-                       if ( $intersect ) {
-                               return self::restrictedTagError( 'tags-update-remove-not-allowed-one',
-                                       'tags-update-remove-not-allowed-multi', $intersect );
-                       }
-               }
-
-               return Status::newGood();
-       }
-
-       /**
-        * Adds and/or removes tags to/from a given change, checking whether it is
-        * allowed first, and adding a log entry afterwards.
-        *
-        * Includes a call to ChangeTag::canUpdateTags(), so your code doesn't need
-        * to do that. However, it doesn't check whether the *_id parameters are a
-        * valid combination. That is up to you to enforce. See ApiTag::execute() for
-        * an example.
-        *
-        * @param array|null $tagsToAdd If none, pass array() or null
-        * @param array|null $tagsToRemove If none, pass array() or null
-        * @param int|null $rc_id The rc_id of the change to add the tags to
-        * @param int|null $rev_id The rev_id of the change to add the tags to
-        * @param int|null $log_id The log_id of the change to add the tags to
-        * @param string $params Params to put in the ct_params field of table
-        * 'change_tag' when adding tags
-        * @param string $reason Comment for the log
-        * @param User $user Who to give credit for the action
-        * @return Status If successful, the value of this Status object will be an
-        * object (stdClass) with the following fields:
-        *  - logId: the ID of the added log entry, or null if no log entry was added
-        *    (i.e. no operation was performed)
-        *  - addedTags: an array containing the tags that were actually added
-        *  - removedTags: an array containing the tags that were actually removed
-        * @since 1.25
-        */
-       public static function updateTagsWithChecks( $tagsToAdd, $tagsToRemove,
-               $rc_id, $rev_id, $log_id, $params, $reason, User $user ) {
-
-               if ( is_null( $tagsToAdd ) ) {
-                       $tagsToAdd = array();
-               }
-               if ( is_null( $tagsToRemove ) ) {
-                       $tagsToRemove = array();
-               }
-               if ( !$tagsToAdd && !$tagsToRemove ) {
-                       // no-op, don't bother
-                       return Status::newGood( (object)array(
-                               'logId' => null,
-                               'addedTags' => array(),
-                               'removedTags' => array(),
-                       ) );
-               }
-
-               // are we allowed to do this?
-               $result = self::canUpdateTags( $tagsToAdd, $tagsToRemove, $user );
-               if ( !$result->isOK() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // basic rate limiting
-               if ( $user->pingLimiter( 'changetag' ) ) {
-                       return Status::newFatal( 'actionthrottledtext' );
-               }
-
-               // do it!
-               list( $tagsAdded, $tagsRemoved, $initialTags ) = self::updateTags( $tagsToAdd,
-                       $tagsToRemove, $rc_id, $rev_id, $log_id, $params );
-               if ( !$tagsAdded && !$tagsRemoved ) {
-                       // no-op, don't log it
-                       return Status::newGood( (object)array(
-                               'logId' => null,
-                               'addedTags' => array(),
-                               'removedTags' => array(),
-                       ) );
-               }
-
-               // log it
-               $logEntry = new ManualLogEntry( 'tag', 'update' );
-               $logEntry->setPerformer( $user );
-               $logEntry->setComment( $reason );
-
-               // find the appropriate target page
-               if ( $rev_id ) {
-                       $rev = Revision::newFromId( $rev_id );
-                       if ( $rev ) {
-                               $title = $rev->getTitle();
-                               $logEntry->setTarget( $rev->getTitle() );
-                       }
-               } elseif ( $log_id ) {
-                       // This function is from revision deletion logic and has nothing to do with
-                       // change tags, but it appears to be the only other place in core where we
-                       // perform logged actions on log items.
-                       $logEntry->setTarget( RevDelLogList::suggestTarget( 0, array( $log_id ) ) );
-               }
-
-               if ( !$logEntry->getTarget() ) {
-                       // target is required, so we have to set something
-                       $logEntry->setTarget( SpecialPage::getTitleFor( 'Tags' ) );
-               }
-
-               $logParams = array(
-                       '4::revid' => $rev_id,
-                       '5::logid' => $log_id,
-                       '6:list:tagsAdded' => $tagsAdded,
-                       '7:number:tagsAddedCount' => count( $tagsAdded ),
-                       '8:list:tagsRemoved' => $tagsRemoved,
-                       '9:number:tagsRemovedCount' => count( $tagsRemoved ),
-                       'initialTags' => $initialTags,
-               );
-               $logEntry->setParameters( $logParams );
-               $logEntry->setRelations( array( 'Tag' => array_merge( $tagsAdded, $tagsRemoved ) ) );
-
-               $dbw = wfGetDB( DB_MASTER );
-               $logId = $logEntry->insert( $dbw );
-               // Only send this to UDP, not RC, similar to patrol events
-               $logEntry->publish( $logId, 'udp' );
-
-               return Status::newGood( (object)array(
-                       'logId' => $logId,
-                       'addedTags' => $tagsAdded,
-                       'removedTags' => $tagsRemoved,
-               ) );
-       }
-
-       /**
-        * Applies all tags-related changes to a query.
-        * Handles selecting tags, and filtering.
-        * Needs $tables to be set up properly, so we can figure out which join conditions to use.
-        *
-        * @param string|array $tables Table names, see DatabaseBase::select
-        * @param string|array $fields Fields used in query, see DatabaseBase::select
-        * @param string|array $conds Conditions used in query, see DatabaseBase::select
-        * @param array $join_conds Join conditions, see DatabaseBase::select
-        * @param array $options Options, see Database::select
-        * @param bool|string $filter_tag Tag to select on
-        *
-        * @throws MWException When unable to determine appropriate JOIN condition for tagging
-        */
-       public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
-                                                                               &$join_conds, &$options, $filter_tag = false ) {
-               global $wgRequest, $wgUseTagFilter;
-
-               if ( $filter_tag === false ) {
-                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
-               }
-
-               // Figure out which conditions can be done.
-               if ( in_array( 'recentchanges', $tables ) ) {
-                       $join_cond = 'ct_rc_id=rc_id';
-               } elseif ( in_array( 'logging', $tables ) ) {
-                       $join_cond = 'ct_log_id=log_id';
-               } elseif ( in_array( 'revision', $tables ) ) {
-                       $join_cond = 'ct_rev_id=rev_id';
-               } elseif ( in_array( 'archive', $tables ) ) {
-                       $join_cond = 'ct_rev_id=ar_rev_id';
-               } else {
-                       throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
-               }
-
-               $fields['ts_tags'] = wfGetDB( DB_SLAVE )->buildGroupConcatField(
-                       ',', 'change_tag', 'ct_tag', $join_cond
-               );
-
-               if ( $wgUseTagFilter && $filter_tag ) {
-                       // Somebody wants to filter on a tag.
-                       // Add an INNER JOIN on change_tag
-
-                       $tables[] = 'change_tag';
-                       $join_conds['change_tag'] = array( 'INNER JOIN', $join_cond );
-                       $conds['ct_tag'] = $filter_tag;
-               }
-       }
-
-       /**
-        * Build a text box to select a change tag
-        *
-        * @param string $selected Tag to select by default
-        * @param bool $fullForm
-        *        - if false, then it returns an array of (label, form).
-        *        - if true, it returns an entire form around the selector.
-        * @param Title $title Title object to send the form to.
-        *        Used when, and only when $fullForm is true.
-        * @return string|array
-        *        - if $fullForm is false: Array with
-        *        - if $fullForm is true: String, html fragment
-        */
-       public static function buildTagFilterSelector( $selected = '',
-               $fullForm = false, Title $title = null
-       ) {
-               global $wgUseTagFilter;
-
-               if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) ) {
-                       return $fullForm ? '' : array();
-               }
-
-               $data = array(
-                       Html::rawElement(
-                               'label',
-                               array( 'for' => 'tagfilter' ),
-                               wfMessage( 'tag-filter' )->parse()
-                       ),
-                       Xml::input(
-                               'tagfilter',
-                               20,
-                               $selected,
-                               array( 'class' => 'mw-tagfilter-input mw-ui-input mw-ui-input-inline', 'id' => 'tagfilter' )
-                       )
-               );
-
-               if ( !$fullForm ) {
-                       return $data;
-               }
-
-               $html = implode( '&#160;', $data );
-               $html .= "\n" .
-                       Xml::element(
-                               'input',
-                               array( 'type' => 'submit', 'value' => wfMessage( 'tag-filter-submit' )->text() )
-                       );
-               $html .= "\n" . Html::hidden( 'title', $title->getPrefixedText() );
-               $html = Xml::tags(
-                       'form',
-                       array( 'action' => $title->getLocalURL(), 'class' => 'mw-tagfilter-form', 'method' => 'get' ),
-                       $html
-               );
-
-               return $html;
-       }
-
-       /**
-        * Defines a tag in the valid_tag table, without checking that the tag name
-        * is valid.
-        * Extensions should NOT use this function; they can use the ListDefinedTags
-        * hook instead.
-        *
-        * @param string $tag Tag to create
-        * @since 1.25
-        */
-       public static function defineTag( $tag ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->replace( 'valid_tag',
-                       array( 'vt_tag' ),
-                       array( 'vt_tag' => $tag ),
-                       __METHOD__ );
-
-               // clear the memcache of defined tags
-               self::purgeTagCacheAll();
-       }
-
-       /**
-        * Removes a tag from the valid_tag table. The tag may remain in use by
-        * extensions, and may still show up as 'defined' if an extension is setting
-        * it from the ListDefinedTags hook.
-        *
-        * @param string $tag Tag to remove
-        * @since 1.25
-        */
-       public static function undefineTag( $tag ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->delete( 'valid_tag', array( 'vt_tag' => $tag ), __METHOD__ );
-
-               // clear the memcache of defined tags
-               self::purgeTagCacheAll();
-       }
-
-       /**
-        * Writes a tag action into the tag management log.
-        *
-        * @param string $action
-        * @param string $tag
-        * @param string $reason
-        * @param User $user Who to attribute the action to
-        * @param int $tagCount For deletion only, how many usages the tag had before
-        * it was deleted.
-        * @since 1.25
-        */
-       protected static function logTagManagementAction( $action, $tag, $reason,
-               User $user, $tagCount = null ) {
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $logEntry = new ManualLogEntry( 'managetags', $action );
-               $logEntry->setPerformer( $user );
-               // target page is not relevant, but it has to be set, so we just put in
-               // the title of Special:Tags
-               $logEntry->setTarget( Title::newFromText( 'Special:Tags' ) );
-               $logEntry->setComment( $reason );
-
-               $params = array( '4::tag' => $tag );
-               if ( !is_null( $tagCount ) ) {
-                       $params['5:number:count'] = $tagCount;
-               }
-               $logEntry->setParameters( $params );
-               $logEntry->setRelations( array( 'Tag' => $tag ) );
-
-               $logId = $logEntry->insert( $dbw );
-               $logEntry->publish( $logId );
-               return $logId;
-       }
-
-       /**
-        * Is it OK to allow the user to activate this tag?
-        *
-        * @param string $tag Tag that you are interested in activating
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canActivateTag( $tag, User $user = null ) {
-               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
-                       return Status::newFatal( 'tags-manage-no-permission' );
-               }
-
-               // non-existing tags cannot be activated
-               $tagUsage = self::tagUsageStatistics();
-               if ( !isset( $tagUsage[$tag] ) ) {
-                       return Status::newFatal( 'tags-activate-not-found', $tag );
-               }
-
-               // defined tags cannot be activated (a defined tag is either extension-
-               // defined, in which case the extension chooses whether or not to active it;
-               // or user-defined, in which case it is considered active)
-               $definedTags = self::listDefinedTags();
-               if ( in_array( $tag, $definedTags ) ) {
-                       return Status::newFatal( 'tags-activate-not-allowed', $tag );
-               }
-
-               return Status::newGood();
-       }
-
-       /**
-        * Activates a tag, checking whether it is allowed first, and adding a log
-        * entry afterwards.
-        *
-        * Includes a call to ChangeTag::canActivateTag(), so your code doesn't need
-        * to do that.
-        *
-        * @param string $tag
-        * @param string $reason
-        * @param User $user Who to give credit for the action
-        * @param bool $ignoreWarnings Can be used for API interaction, default false
-        * @return Status If successful, the Status contains the ID of the added log
-        * entry as its value
-        * @since 1.25
-        */
-       public static function activateTagWithChecks( $tag, $reason, User $user,
-               $ignoreWarnings = false ) {
-
-               // are we allowed to do this?
-               $result = self::canActivateTag( $tag, $user );
-               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // do it!
-               self::defineTag( $tag );
-
-               // log it
-               $logId = self::logTagManagementAction( 'activate', $tag, $reason, $user );
-               return Status::newGood( $logId );
-       }
-
-       /**
-        * Is it OK to allow the user to deactivate this tag?
-        *
-        * @param string $tag Tag that you are interested in deactivating
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canDeactivateTag( $tag, User $user = null ) {
-               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
-                       return Status::newFatal( 'tags-manage-no-permission' );
-               }
-
-               // only explicitly-defined tags can be deactivated
-               $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
-               if ( !in_array( $tag, $explicitlyDefinedTags ) ) {
-                       return Status::newFatal( 'tags-deactivate-not-allowed', $tag );
-               }
-               return Status::newGood();
-       }
-
-       /**
-        * Deactivates a tag, checking whether it is allowed first, and adding a log
-        * entry afterwards.
-        *
-        * Includes a call to ChangeTag::canDeactivateTag(), so your code doesn't need
-        * to do that.
-        *
-        * @param string $tag
-        * @param string $reason
-        * @param User $user Who to give credit for the action
-        * @param bool $ignoreWarnings Can be used for API interaction, default false
-        * @return Status If successful, the Status contains the ID of the added log
-        * entry as its value
-        * @since 1.25
-        */
-       public static function deactivateTagWithChecks( $tag, $reason, User $user,
-               $ignoreWarnings = false ) {
-
-               // are we allowed to do this?
-               $result = self::canDeactivateTag( $tag, $user );
-               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // do it!
-               self::undefineTag( $tag );
-
-               // log it
-               $logId = self::logTagManagementAction( 'deactivate', $tag, $reason, $user );
-               return Status::newGood( $logId );
-       }
-
-       /**
-        * Is it OK to allow the user to create this tag?
-        *
-        * @param string $tag Tag that you are interested in creating
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canCreateTag( $tag, User $user = null ) {
-               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
-                       return Status::newFatal( 'tags-manage-no-permission' );
-               }
-
-               // no empty tags
-               if ( $tag === '' ) {
-                       return Status::newFatal( 'tags-create-no-name' );
-               }
-
-               // tags cannot contain commas (used as a delimiter in tag_summary table) or
-               // slashes (would break tag description messages in MediaWiki namespace)
-               if ( strpos( $tag, ',' ) !== false || strpos( $tag, '/' ) !== false ) {
-                       return Status::newFatal( 'tags-create-invalid-chars' );
-               }
-
-               // could the MediaWiki namespace description messages be created?
-               $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Tag-$tag-description" );
-               if ( is_null( $title ) ) {
-                       return Status::newFatal( 'tags-create-invalid-title-chars' );
-               }
-
-               // does the tag already exist?
-               $tagUsage = self::tagUsageStatistics();
-               if ( isset( $tagUsage[$tag] ) ) {
-                       return Status::newFatal( 'tags-create-already-exists', $tag );
-               }
-
-               // check with hooks
-               $canCreateResult = Status::newGood();
-               Hooks::run( 'ChangeTagCanCreate', array( $tag, $user, &$canCreateResult ) );
-               return $canCreateResult;
-       }
-
-       /**
-        * Creates a tag by adding a row to the `valid_tag` table.
-        *
-        * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
-        * do that.
-        *
-        * @param string $tag
-        * @param string $reason
-        * @param User $user Who to give credit for the action
-        * @param bool $ignoreWarnings Can be used for API interaction, default false
-        * @return Status If successful, the Status contains the ID of the added log
-        * entry as its value
-        * @since 1.25
-        */
-       public static function createTagWithChecks( $tag, $reason, User $user,
-               $ignoreWarnings = false ) {
-
-               // are we allowed to do this?
-               $result = self::canCreateTag( $tag, $user );
-               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // do it!
-               self::defineTag( $tag );
-
-               // log it
-               $logId = self::logTagManagementAction( 'create', $tag, $reason, $user );
-               return Status::newGood( $logId );
-       }
-
-       /**
-        * Permanently removes all traces of a tag from the DB. Good for removing
-        * misspelt or temporary tags.
-        *
-        * This function should be directly called by maintenance scripts only, never
-        * by user-facing code. See deleteTagWithChecks() for functionality that can
-        * safely be exposed to users.
-        *
-        * @param string $tag Tag to remove
-        * @return Status The returned status will be good unless a hook changed it
-        * @since 1.25
-        */
-       public static function deleteTagEverywhere( $tag ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->startAtomic( __METHOD__ );
-
-               // delete from valid_tag
-               self::undefineTag( $tag );
-
-               // find out which revisions use this tag, so we can delete from tag_summary
-               $result = $dbw->select( 'change_tag',
-                       array( 'ct_rc_id', 'ct_log_id', 'ct_rev_id', 'ct_tag' ),
-                       array( 'ct_tag' => $tag ),
-                       __METHOD__ );
-               foreach ( $result as $row ) {
-                       // remove the tag from the relevant row of tag_summary
-                       $tagsToAdd = array();
-                       $tagsToRemove = array( $tag );
-                       self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $row->ct_rc_id,
-                               $row->ct_rev_id, $row->ct_log_id );
-               }
-
-               // delete from change_tag
-               $dbw->delete( 'change_tag', array( 'ct_tag' => $tag ), __METHOD__ );
-
-               $dbw->endAtomic( __METHOD__ );
-
-               // give extensions a chance
-               $status = Status::newGood();
-               Hooks::run( 'ChangeTagAfterDelete', array( $tag, &$status ) );
-               // let's not allow error results, as the actual tag deletion succeeded
-               if ( !$status->isOK() ) {
-                       wfDebug( 'ChangeTagAfterDelete error condition downgraded to warning' );
-                       $status->ok = true;
-               }
-
-               // clear the memcache of defined tags
-               self::purgeTagCacheAll();
-
-               return $status;
-       }
-
-       /**
-        * Is it OK to allow the user to delete this tag?
-        *
-        * @param string $tag Tag that you are interested in deleting
-        * @param User|null $user User whose permission you wish to check, or null if
-        * you don't care (e.g. maintenance scripts)
-        * @return Status
-        * @since 1.25
-        */
-       public static function canDeleteTag( $tag, User $user = null ) {
-               $tagUsage = self::tagUsageStatistics();
-
-               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
-                       return Status::newFatal( 'tags-manage-no-permission' );
-               }
-
-               if ( !isset( $tagUsage[$tag] ) ) {
-                       return Status::newFatal( 'tags-delete-not-found', $tag );
-               }
-
-               if ( $tagUsage[$tag] > self::MAX_DELETE_USES ) {
-                       return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
-               }
-
-               $extensionDefined = self::listExtensionDefinedTags();
-               if ( in_array( $tag, $extensionDefined ) ) {
-                       // extension-defined tags can't be deleted unless the extension
-                       // specifically allows it
-                       $status = Status::newFatal( 'tags-delete-not-allowed' );
-               } else {
-                       // user-defined tags are deletable unless otherwise specified
-                       $status = Status::newGood();
-               }
-
-               Hooks::run( 'ChangeTagCanDelete', array( $tag, $user, &$status ) );
-               return $status;
-       }
-
-       /**
-        * Deletes a tag, checking whether it is allowed first, and adding a log entry
-        * afterwards.
-        *
-        * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
-        * do that.
-        *
-        * @param string $tag
-        * @param string $reason
-        * @param User $user Who to give credit for the action
-        * @param bool $ignoreWarnings Can be used for API interaction, default false
-        * @return Status If successful, the Status contains the ID of the added log
-        * entry as its value
-        * @since 1.25
-        */
-       public static function deleteTagWithChecks( $tag, $reason, User $user,
-               $ignoreWarnings = false ) {
-
-               // are we allowed to do this?
-               $result = self::canDeleteTag( $tag, $user );
-               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
-                       $result->value = null;
-                       return $result;
-               }
-
-               // store the tag usage statistics
-               $tagUsage = self::tagUsageStatistics();
-
-               // do it!
-               $deleteResult = self::deleteTagEverywhere( $tag );
-               if ( !$deleteResult->isOK() ) {
-                       return $deleteResult;
-               }
-
-               // log it
-               $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $tagUsage[$tag] );
-               $deleteResult->value = $logId;
-               return $deleteResult;
-       }
-
-       /**
-        * Lists those tags which extensions report as being "active".
-        *
-        * @return array
-        * @since 1.25
-        */
-       public static function listExtensionActivatedTags() {
-               // Caching...
-               global $wgMemc;
-               $key = wfMemcKey( 'active-tags' );
-               $tags = $wgMemc->get( $key );
-               if ( $tags ) {
-                       return $tags;
-               }
-
-               // ask extensions which tags they consider active
-               $extensionActive = array();
-               Hooks::run( 'ChangeTagsListActive', array( &$extensionActive ) );
-
-               // Short-term caching.
-               $wgMemc->set( $key, $extensionActive, 300 );
-               return $extensionActive;
-       }
-
-       /**
-        * Basically lists defined tags which count even if they aren't applied to anything.
-        * It returns a union of the results of listExplicitlyDefinedTags() and
-        * listExtensionDefinedTags().
-        *
-        * @return string[] Array of strings: tags
-        */
-       public static function listDefinedTags() {
-               $tags1 = self::listExplicitlyDefinedTags();
-               $tags2 = self::listExtensionDefinedTags();
-               return array_values( array_unique( array_merge( $tags1, $tags2 ) ) );
-       }
-
-       /**
-        * Lists tags explicitly defined in the `valid_tag` table of the database.
-        * Tags in table 'change_tag' which are not in table 'valid_tag' are not
-        * included.
-        *
-        * Tries memcached first.
-        *
-        * @return string[] Array of strings: tags
-        * @since 1.25
-        */
-       public static function listExplicitlyDefinedTags() {
-               // Caching...
-               global $wgMemc;
-               $key = wfMemcKey( 'valid-tags-db' );
-               $tags = $wgMemc->get( $key );
-               if ( $tags ) {
-                       return $tags;
-               }
-
-               $emptyTags = array();
-
-               // Some DB stuff
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'valid_tag', 'vt_tag', array(), __METHOD__ );
-               foreach ( $res as $row ) {
-                       $emptyTags[] = $row->vt_tag;
-               }
-
-               $emptyTags = array_filter( array_unique( $emptyTags ) );
-
-               // Short-term caching.
-               $wgMemc->set( $key, $emptyTags, 300 );
-               return $emptyTags;
-       }
-
-       /**
-        * Lists tags defined by extensions using the ListDefinedTags hook.
-        * Extensions need only define those tags they deem to be in active use.
-        *
-        * Tries memcached first.
-        *
-        * @return string[] Array of strings: tags
-        * @since 1.25
-        */
-       public static function listExtensionDefinedTags() {
-               // Caching...
-               global $wgMemc;
-               $key = wfMemcKey( 'valid-tags-hook' );
-               $tags = $wgMemc->get( $key );
-               if ( $tags ) {
-                       return $tags;
-               }
-
-               $emptyTags = array();
-               Hooks::run( 'ListDefinedTags', array( &$emptyTags ) );
-               $emptyTags = array_filter( array_unique( $emptyTags ) );
-
-               // Short-term caching.
-               $wgMemc->set( $key, $emptyTags, 300 );
-               return $emptyTags;
-       }
-
-       /**
-        * Invalidates the short-term cache of defined tags used by the
-        * list*DefinedTags functions, as well as the tag statistics cache.
-        * @since 1.25
-        */
-       public static function purgeTagCacheAll() {
-               global $wgMemc;
-               $wgMemc->delete( wfMemcKey( 'active-tags' ) );
-               $wgMemc->delete( wfMemcKey( 'valid-tags-db' ) );
-               $wgMemc->delete( wfMemcKey( 'valid-tags-hook' ) );
-               self::purgeTagUsageCache();
-       }
-
-       /**
-        * Invalidates the tag statistics cache only.
-        * @since 1.25
-        */
-       public static function purgeTagUsageCache() {
-               global $wgMemc;
-               $wgMemc->delete( wfMemcKey( 'change-tag-statistics' ) );
-       }
-
-       /**
-        * Returns a map of any tags used on the wiki to number of edits
-        * tagged with them, ordered descending by the hitcount.
-        *
-        * Keeps a short-term cache in memory, so calling this multiple times in the
-        * same request should be fine.
-        *
-        * @return array Array of string => int
-        */
-       public static function tagUsageStatistics() {
-               // Caching...
-               global $wgMemc;
-               $key = wfMemcKey( 'change-tag-statistics' );
-               $stats = $wgMemc->get( $key );
-               if ( $stats ) {
-                       return $stats;
-               }
-
-               $out = array();
-
-               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
-               $res = $dbr->select(
-                       'change_tag',
-                       array( 'ct_tag', 'hitcount' => 'count(*)' ),
-                       array(),
-                       __METHOD__,
-                       array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' )
-               );
-
-               foreach ( $res as $row ) {
-                       $out[$row->ct_tag] = $row->hitcount;
-               }
-               foreach ( self::listDefinedTags() as $tag ) {
-                       if ( !isset( $out[$tag] ) ) {
-                               $out[$tag] = 0;
-                       }
-               }
-
-               // Cache for a very short time
-               $wgMemc->set( $key, $out, 300 );
-               return $out;
-       }
-}
diff --git a/includes/MessageBlobStore.php b/includes/MessageBlobStore.php
deleted file mode 100644 (file)
index 011cae6..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-<?php
-/**
- * Resource message blobs storage used by the resource loader.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Roan Kattouw
- * @author Trevor Parscal
- */
-
-/**
- * This class provides access to the resource message blobs storage used by
- * the ResourceLoader.
- *
- * A message blob is a JSON object containing the interface messages for a
- * certain resource in a certain language. These message blobs are cached
- * in the msg_resource table and automatically invalidated when one of their
- * constituent messages or the resource itself is changed.
- */
-class MessageBlobStore {
-       /**
-        * Get the singleton instance
-        *
-        * @since 1.24
-        * @deprecated since 1.25
-        * @return MessageBlobStore
-        */
-       public static function getInstance() {
-               wfDeprecated( __METHOD__, '1.25' );
-               return new self;
-       }
-
-       /**
-        * Get the message blobs for a set of modules
-        *
-        * @param ResourceLoader $resourceLoader
-        * @param array $modules Array of module objects keyed by module name
-        * @param string $lang Language code
-        * @return array An array mapping module names to message blobs
-        */
-       public function get( ResourceLoader $resourceLoader, $modules, $lang ) {
-               if ( !count( $modules ) ) {
-                       return array();
-               }
-               // Try getting from the DB first
-               $blobs = $this->getFromDB( $resourceLoader, array_keys( $modules ), $lang );
-
-               // Generate blobs for any missing modules and store them in the DB
-               $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) );
-               foreach ( $missing as $name ) {
-                       $blob = $this->insertMessageBlob( $name, $modules[$name], $lang );
-                       if ( $blob ) {
-                               $blobs[$name] = $blob;
-                       }
-               }
-
-               return $blobs;
-       }
-
-       /**
-        * Generate and insert a new message blob. If the blob was already
-        * present, it is not regenerated; instead, the preexisting blob
-        * is fetched and returned.
-        *
-        * @param string $name Module name
-        * @param ResourceLoaderModule $module
-        * @param string $lang Language code
-        * @return mixed Message blob or false if the module has no messages
-        */
-       public function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
-               $blob = $this->generateMessageBlob( $module, $lang );
-
-               if ( !$blob ) {
-                       return false;
-               }
-
-               try {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $success = $dbw->insert( 'msg_resource', array(
-                                       'mr_lang' => $lang,
-                                       'mr_resource' => $name,
-                                       'mr_blob' => $blob,
-                                       'mr_timestamp' => $dbw->timestamp()
-                               ),
-                               __METHOD__,
-                               array( 'IGNORE' )
-                       );
-
-                       if ( $success ) {
-                               if ( $dbw->affectedRows() == 0 ) {
-                                       // Blob was already present, fetch it
-                                       $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
-                                                       'mr_resource' => $name,
-                                                       'mr_lang' => $lang,
-                                               ),
-                                               __METHOD__
-                                       );
-                               } else {
-                                       // Update msg_resource_links
-                                       $rows = array();
-
-                                       foreach ( $module->getMessages() as $key ) {
-                                               $rows[] = array(
-                                                       'mrl_resource' => $name,
-                                                       'mrl_message' => $key
-                                               );
-                                       }
-                                       $dbw->insert( 'msg_resource_links', $rows,
-                                               __METHOD__, array( 'IGNORE' )
-                                       );
-                               }
-                       }
-               } catch ( DBError $e ) {
-                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
-               }
-               return $blob;
-       }
-
-       /**
-        * Update the message blob for a given module in a given language
-        *
-        * @param string $name Module name
-        * @param ResourceLoaderModule $module
-        * @param string $lang Language code
-        * @return string Regenerated message blob, or null if there was no blob for
-        *   the given module/language pair.
-        */
-       public function updateModule( $name, ResourceLoaderModule $module, $lang ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $row = $dbw->selectRow( 'msg_resource', 'mr_blob',
-                       array( 'mr_resource' => $name, 'mr_lang' => $lang ),
-                       __METHOD__
-               );
-               if ( !$row ) {
-                       return null;
-               }
-
-               // Save the old and new blobs for later
-               $oldBlob = $row->mr_blob;
-               $newBlob = $this->generateMessageBlob( $module, $lang );
-
-               try {
-                       $newRow = array(
-                               'mr_resource' => $name,
-                               'mr_lang' => $lang,
-                               'mr_blob' => $newBlob,
-                               'mr_timestamp' => $dbw->timestamp()
-                       );
-
-                       $dbw->replace( 'msg_resource',
-                               array( array( 'mr_resource', 'mr_lang' ) ),
-                               $newRow, __METHOD__
-                       );
-
-                       // Figure out which messages were added and removed
-                       $oldMessages = array_keys( FormatJson::decode( $oldBlob, true ) );
-                       $newMessages = array_keys( FormatJson::decode( $newBlob, true ) );
-                       $added = array_diff( $newMessages, $oldMessages );
-                       $removed = array_diff( $oldMessages, $newMessages );
-
-                       // Delete removed messages, insert added ones
-                       if ( $removed ) {
-                               $dbw->delete( 'msg_resource_links', array(
-                                               'mrl_resource' => $name,
-                                               'mrl_message' => $removed
-                                       ), __METHOD__
-                               );
-                       }
-
-                       $newLinksRows = array();
-
-                       foreach ( $added as $message ) {
-                               $newLinksRows[] = array(
-                                       'mrl_resource' => $name,
-                                       'mrl_message' => $message
-                               );
-                       }
-
-                       if ( $newLinksRows ) {
-                               $dbw->insert( 'msg_resource_links', $newLinksRows, __METHOD__,
-                                       array( 'IGNORE' ) // just in case
-                               );
-                       }
-               } catch ( Exception $e ) {
-                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
-               }
-               return $newBlob;
-       }
-
-       /**
-        * Update a single message in all message blobs it occurs in.
-        *
-        * @param string $key Message key
-        */
-       public function updateMessage( $key ) {
-               try {
-                       $dbw = wfGetDB( DB_MASTER );
-
-                       // Keep running until the updates queue is empty.
-                       // Due to update conflicts, the queue might not be emptied
-                       // in one iteration.
-                       $updates = null;
-                       do {
-                               $updates = $this->getUpdatesForMessage( $key, $updates );
-
-                               foreach ( $updates as $k => $update ) {
-                                       // Update the row on the condition that it
-                                       // didn't change since we fetched it by putting
-                                       // the timestamp in the WHERE clause.
-                                       $success = $dbw->update( 'msg_resource',
-                                               array(
-                                                       'mr_blob' => $update['newBlob'],
-                                                       'mr_timestamp' => $dbw->timestamp() ),
-                                               array(
-                                                       'mr_resource' => $update['resource'],
-                                                       'mr_lang' => $update['lang'],
-                                                       'mr_timestamp' => $update['timestamp'] ),
-                                               __METHOD__
-                                       );
-
-                                       // Only requeue conflicted updates.
-                                       // If update() returned false, don't retry, for
-                                       // fear of getting into an infinite loop
-                                       if ( !( $success && $dbw->affectedRows() == 0 ) ) {
-                                               // Not conflicted
-                                               unset( $updates[$k] );
-                                       }
-                               }
-                       } while ( count( $updates ) );
-
-                       // No need to update msg_resource_links because we didn't add
-                       // or remove any messages, we just changed their contents.
-               } catch ( Exception $e ) {
-                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
-               }
-       }
-
-       public function clear() {
-               // TODO: Give this some more thought
-               try {
-                       // Not using TRUNCATE, because that needs extra permissions,
-                       // which maybe not granted to the database user.
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->delete( 'msg_resource', '*', __METHOD__ );
-                       $dbw->delete( 'msg_resource_links', '*', __METHOD__ );
-               } catch ( Exception $e ) {
-                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
-               }
-       }
-
-       /**
-        * Create an update queue for updateMessage()
-        *
-        * @param string $key Message key
-        * @param array $prevUpdates Updates queue to refresh or null to build a fresh update queue
-        * @return array Updates queue
-        */
-       private function getUpdatesForMessage( $key, $prevUpdates = null ) {
-               $dbw = wfGetDB( DB_MASTER );
-
-               if ( is_null( $prevUpdates ) ) {
-                       // Fetch all blobs referencing $key
-                       $res = $dbw->select(
-                               array( 'msg_resource', 'msg_resource_links' ),
-                               array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
-                               array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
-                               __METHOD__
-                       );
-               } else {
-                       // Refetch the blobs referenced by $prevUpdates
-
-                       // Reorganize the (resource, lang) pairs in the format
-                       // expected by makeWhereFrom2d()
-                       $twoD = array();
-
-                       foreach ( $prevUpdates as $update ) {
-                               $twoD[$update['resource']][$update['lang']] = true;
-                       }
-
-                       $res = $dbw->select( 'msg_resource',
-                               array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
-                               $dbw->makeWhereFrom2d( $twoD, 'mr_resource', 'mr_lang' ),
-                               __METHOD__
-                       );
-               }
-
-               // Build the new updates queue
-               $updates = array();
-
-               foreach ( $res as $row ) {
-                       $updates[] = array(
-                               'resource' => $row->mr_resource,
-                               'lang' => $row->mr_lang,
-                               'timestamp' => $row->mr_timestamp,
-                               'newBlob' => $this->reencodeBlob( $row->mr_blob, $key, $row->mr_lang )
-                       );
-               }
-
-               return $updates;
-       }
-
-       /**
-        * Reencode a message blob with the updated value for a message
-        *
-        * @param string $blob Message blob (JSON object)
-        * @param string $key Message key
-        * @param string $lang Language code
-        * @return string Message blob with $key replaced with its new value
-        */
-       private function reencodeBlob( $blob, $key, $lang ) {
-               $decoded = FormatJson::decode( $blob, true );
-               $decoded[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
-
-               return FormatJson::encode( (object)$decoded );
-       }
-
-       /**
-        * Get the message blobs for a set of modules from the database.
-        * Modules whose blobs are not in the database are silently dropped.
-        *
-        * @param ResourceLoader $resourceLoader
-        * @param array $modules Array of module names
-        * @param string $lang Language code
-        * @throws MWException
-        * @return array Array mapping module names to blobs
-        */
-       private function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) {
-               $config = $resourceLoader->getConfig();
-               $retval = array();
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'msg_resource',
-                       array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
-                       array( 'mr_resource' => $modules, 'mr_lang' => $lang ),
-                       __METHOD__
-               );
-
-               foreach ( $res as $row ) {
-                       $module = $resourceLoader->getModule( $row->mr_resource );
-                       if ( !$module ) {
-                               // This shouldn't be possible
-                               throw new MWException( __METHOD__ . ' passed an invalid module name' );
-                       }
-
-                       // Update the module's blobs if the set of messages changed or if the blob is
-                       // older than the CacheEpoch setting
-                       $keys = array_keys( FormatJson::decode( $row->mr_blob, true ) );
-                       $values = array_values( array_unique( $module->getMessages() ) );
-                       if ( $keys !== $values
-                               || wfTimestamp( TS_MW, $row->mr_timestamp ) <= $config->get( 'CacheEpoch' )
-                       ) {
-                               $retval[$row->mr_resource] = $this->updateModule( $row->mr_resource, $module, $lang );
-                       } else {
-                               $retval[$row->mr_resource] = $row->mr_blob;
-                       }
-               }
-
-               return $retval;
-       }
-
-       /**
-        * Generate the message blob for a given module in a given language.
-        *
-        * @param ResourceLoaderModule $module
-        * @param string $lang Language code
-        * @return string JSON object
-        */
-       private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
-               $messages = array();
-
-               foreach ( $module->getMessages() as $key ) {
-                       $messages[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
-               }
-
-               return FormatJson::encode( (object)$messages );
-       }
-}
index f862953..b122a9f 100644 (file)
@@ -3603,7 +3603,7 @@ class User implements IDBAccessObject {
                // This will be used for a CAS check as a last-resort safety
                // check against race conditions and slave lag.
                $oldTouched = $this->mTouched;
-               $this->mTouched = $this->newTouchedTimestamp();
+               $newTouched = $this->newTouchedTimestamp();
 
                if ( !$wgAuth->allowSetLocalPassword() ) {
                        $this->mPassword = self::getPasswordFactory()->newFromCiphertext( null );
@@ -3619,7 +3619,7 @@ class User implements IDBAccessObject {
                                'user_real_name' => $this->mRealName,
                                'user_email' => $this->mEmail,
                                'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
-                               'user_touched' => $dbw->timestamp( $this->mTouched ),
+                               'user_touched' => $dbw->timestamp( $newTouched ),
                                'user_token' => strval( $this->mToken ),
                                'user_email_token' => $this->mEmailToken,
                                'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
@@ -3631,22 +3631,31 @@ class User implements IDBAccessObject {
                );
 
                if ( !$dbw->affectedRows() ) {
+                       // Maybe the problem was a missed cache update; clear it to be safe
+                       $this->clearSharedCache();
                        // User was changed in the meantime or loaded with stale data
                        MWExceptionHandler::logException( new MWException(
                                "CAS update failed on user_touched for user ID '{$this->mId}';" .
                                "the version of the user to be saved is older than the current version."
                        ) );
-                       // Maybe the problem was a missed cache update; clear it to be safe
-                       $this->clearSharedCache();
 
                        return;
                }
 
+               $this->mTouched = $newTouched;
                $this->saveOptions();
 
                Hooks::run( 'UserSaveSettings', array( $this ) );
                $this->clearSharedCache();
                $this->getUserPage()->invalidateCache();
+
+               // T95839: clear the cache again post-commit to reduce race conditions
+               // where stale values are written back to the cache by other threads.
+               // Note: this *still* doesn't deal with REPEATABLE-READ snapshot lag...
+               $that = $this;
+               $dbw->onTransactionIdle( function() use ( $that ) {
+                       $that->clearSharedCache();
+               } );
        }
 
        /**
index 9c71f3e..f97dc6a 100644 (file)
@@ -78,7 +78,6 @@ if ( $IP === false ) {
 
 # Grab profiling functions
 require_once "$IP/includes/profiler/ProfilerFunctions.php";
-$wgRUstart = wfGetRusage() ?: array();
 
 # Start the autoloader, so that extensions can derive classes from core files
 require_once "$IP/includes/AutoLoader.php";
index 6693178..83185e4 100644 (file)
@@ -448,6 +448,10 @@ class HistoryPager extends ReverseChronologicalPager {
        }
 
        function doBatchLookups() {
+               if ( !Hooks::run( 'PageHistoryPager::doBatchLookups', array( $this, $this->mResult ) ) ) {
+                       return;
+               }
+
                # Do a link batch query
                $this->mResult->seek( 0 );
                $batch = new LinkBatch();
index 00fc12e..1e30616 100644 (file)
@@ -164,17 +164,17 @@ class ApiHelp extends ApiBase {
                                $old = $href;
                                $href = rawurldecode( $href );
                        } while ( $old !== $href );
-                       if ( preg_match( '!Special:ApiHelp/([^&/|]+)!', $href, $m ) ) {
+                       if ( preg_match( '!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
                                if ( isset( $localModules[$m[1]] ) ) {
-                                       $href = '#' . $m[1];
+                                       $href = $m[2] === '' ? '#' . $m[1] : $m[2];
                                } elseif ( $helptitle !== null ) {
-                                       $href = Title::newFromText( str_replace( '$1', $m[1], $helptitle ) )
+                                       $href = Title::newFromText( str_replace( '$1', $m[1], $helptitle ) . $m[2] )
                                                ->getFullUrl();
                                } else {
                                        $href = wfAppendQuery( wfScript( 'api' ), array(
                                                'action' => 'help',
                                                'modules' => $m[1],
-                                       ) );
+                                       ) ) . $m[2];
                                }
                                $node->setAttribute( 'href', $href );
                                $node->removeAttribute( 'title' );
@@ -472,6 +472,8 @@ class ApiHelp extends ApiBase {
                                                                                ->params( $context->getLanguage()->commaList( $submodules ) )
                                                                                ->parse();
                                                                        $hintPipeSeparated = false;
+                                                                       // No type message necessary, we have a list of values.
+                                                                       $type = null;
                                                                        break;
 
                                                                case 'namespace':
@@ -482,6 +484,8 @@ class ApiHelp extends ApiBase {
                                                                                ->params( $context->getLanguage()->commaList( $namespaces ) )
                                                                                ->parse();
                                                                        $hintPipeSeparated = false;
+                                                                       // No type message necessary, we have a list of values.
+                                                                       $type = null;
                                                                        break;
 
                                                                case 'limit':
@@ -524,10 +528,27 @@ class ApiHelp extends ApiBase {
                                                                case 'upload':
                                                                        $info[] = $context->msg( 'api-help-param-upload' )
                                                                                ->parse();
+                                                                       // No type message necessary, api-help-param-upload should handle it.
+                                                                       $type = null;
+                                                                       break;
+
+                                                               case 'string':
+                                                                       // Displaying a type message here would be useless.
+                                                                       $type = null;
                                                                        break;
                                                        }
                                                }
 
+                                               // Add type. Messages for grep: api-help-param-type-limit
+                                               // api-help-param-type-integer api-help-param-type-boolean
+                                               // api-help-param-type-timestamp api-help-param-type-user
+                                               if ( is_string( $type ) ) {
+                                                       $msg = $context->msg( "api-help-param-type-$type" );
+                                                       if ( !$msg->isDisabled() ) {
+                                                               $info[] = $msg->params( $multi ? 2 : 1 )->parse();
+                                                       }
+                                               }
+
                                                if ( $multi ) {
                                                        $extra = array();
                                                        if ( $hintPipeSeparated ) {
index 465025c..7e3dcff 100644 (file)
@@ -1305,6 +1305,7 @@ class ApiMain extends ApiBase {
                        }
                        $help[$k] = $v;
                }
+               $help['datatypes'] = '';
                $help['credits'] = '';
 
                // Fill 'permissions'
@@ -1337,10 +1338,18 @@ class ApiMain extends ApiBase {
                $help['permissions'] .= Html::closeElement( 'dl' );
                $help['permissions'] .= Html::closeElement( 'div' );
 
-               // Fill 'credits', if applicable
+               // Fill 'datatypes' and 'credits', if applicable
                if ( empty( $options['nolead'] ) ) {
-                       $help['credits'] .= Html::element( 'h' . min( 6, $options['headerlevel'] + 1 ),
-                               array( 'id' => '+credits', 'class' => 'apihelp-header' ),
+                       $help['datatypes'] .= Html::rawelement( 'h' . min( 6, $options['headerlevel'] + 1 ),
+                               array( 'id' => 'main/datatypes', 'class' => 'apihelp-header' ),
+                               Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/datatypes' ) ) ) .
+                               $this->msg( 'api-help-datatypes-header' )->parse()
+                       );
+                       $help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock();
+
+                       $help['credits'] .= Html::rawelement( 'h' . min( 6, $options['headerlevel'] + 1 ),
+                               array( 'id' => 'main/credits', 'class' => 'apihelp-header' ),
+                               Html::element( 'span', array( 'id' => Sanitizer::escapeId( 'main/credits' ) ) ) .
                                $this->msg( 'api-credits-header' )->parse()
                        );
                        $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
index 070a6ff..37a3b97 100644 (file)
        "apihelp-dumpfm-description": "Daten im PHP-<code>var_dump()</code>-Format ausgeben (schöngedruckt in HTML).",
        "apihelp-json-description": "Daten im JSON-Format ausgeben.",
        "apihelp-json-param-callback": "Falls angegeben, wird die Ausgabe in einen angegebenen Funktionsaufruf eingeschlossen. Aus Sicherheitsgründen sind benutzerspezifische Daten beschränkt.",
-       "apihelp-json-param-utf8": "Falls angegeben, kodiert die meisten (aber nicht alle) Nicht-ASCII-Zeichen als UTF-8 anstatt sie mit hexadezimalen Escape-Sequenzen zu ersetzen.",
+       "apihelp-json-param-utf8": "Falls angegeben, kodiert die meisten (aber nicht alle) Nicht-ASCII-Zeichen als UTF-8 anstatt sie mit hexadezimalen Escape-Sequenzen zu ersetzen. Standard, wenn <var>formatversion</var> nicht <kbd>1</kbd> ist.",
        "apihelp-jsonfm-description": "Daten im JSON-Format ausgeben (schöngedruckt in HTML).",
        "apihelp-none-description": "Nichts ausgeben.",
        "apihelp-php-description": "Daten im serialisierten PHP-Format ausgeben.",
index ee66238..615d60a 100644 (file)
        "api-help-parameters": "{{PLURAL:$1|Parameter|Parameters}}:",
        "api-help-param-deprecated": "Deprecated.",
        "api-help-param-required": "This parameter is required.",
+       "api-help-datatypes-header": "Data types",
+       "api-help-datatypes": "Some API parameter types need further explanation:\n;boolean\n:Boolean parameters work like HTML checkboxes: if the parameter is specified, regardless of value, it is considered true. For a false value, omit the parameter entirely.\n;timestamp\n:Timestamps may be specified in several formats. ISO 8601 date and time is recommended. All times are in UTC, any included timezone is ignored.\n:* ISO 8601 date and time, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (punctuation and <kbd>Z</kbd> are optional)\n:* ISO 8601 date and time with (ignored) fractional seconds, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (dashes, colons, and <kbd>Z</kbd> are optional)\n:* MediaWiki format, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Generic numeric format, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (optional timezone of <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> is ignored)\n:* EXIF format, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*RFC 2822 format (timezone may be omitted), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 format (timezone may be omitted), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime format, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Seconds since 1970-01-01T00:00:00Z as a 1 to 13 digit integer",
+       "api-help-param-type-limit": "Type: integer or <kbd>max</kbd>",
+       "api-help-param-type-integer": "Type: {{PLURAL:$1|1=integer|2=list of integers}}",
+       "api-help-param-type-boolean": "Type: boolean ([[Special:ApiHelp/main#main/datatypes|details]])",
+       "api-help-param-type-timestamp": "Type: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
+       "api-help-param-type-user": "Type: {{PLURAL:$1|1=user name|2=list of user names}}",
        "api-help-param-list": "{{PLURAL:$1|1=One value|2=Values (separate with <kbd>{{!}}</kbd>)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Must be empty|Can be empty, or $2}}",
        "api-help-param-limit": "No more than $1 allowed.",
index 5efe91d..bbcf1e7 100644 (file)
        "apihelp-query+watchlist-param-excludeuser": "No listar cambios de este usuario.",
        "apihelp-query+watchlistraw-param-show": "Sólo listar los elementos que cumplen estos criterios.",
        "apihelp-query+watchlistraw-example-simple": "Listar las páginas de la lista de seguimiento del usuario actual.",
+       "apihelp-tag-param-logid": "Uno o más identificadores de entradas del registro a los que agregar o eliminar la etiqueta.",
+       "apihelp-tag-param-reason": "Motivo del cambio.",
+       "apihelp-tag-example-log": "Eliminar la etiqueta <kbd>spam</kbd> de la entrada del registro con identificador 123 con el motivo <kbd>aplicada incorrectamente</kbd>",
        "apihelp-unblock-example-user": "Desbloquear al usuario <kbd>Bob</kbd> con el motivo <kbd>Lo siento, Bob</kbd>",
        "apihelp-undelete-example-revisions": "Restaurar dos revisiones de la página <kbd>Portada</kbd>.",
        "apihelp-upload-param-watch": "Vigilar la página.",
index baf874c..64c7146 100644 (file)
@@ -11,7 +11,8 @@
                        "Nicolapps",
                        "Raulel",
                        "Arkanosis",
-                       "Ltrlg"
+                       "Ltrlg",
+                       "Crochet.david"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:API:Errors_and_warnings|API: Errors and warnings]].",
        "apihelp-setnotificationtimestamp-example-page": "Réinitialiser l’état de notification pour la <kbd>Page principale<kbd>.",
        "apihelp-setnotificationtimestamp-example-pagetimestamp": "Fixer l’horodatage de notification pour <kbd>Page principale</kbd> afin que toutes les modifications depuis le 1 janvier 2012 soient non vues",
        "apihelp-setnotificationtimestamp-example-allpages": "Réinitialiser l’état de notification sur les pages dans l’espace de noms <kbd>{{ns:user}}</kbd>.",
+       "apihelp-tag-param-reason": "Motif de la modification.",
        "apihelp-tokens-description": "Obtenir les jetons pour les actions modifiant les données.\n\nCe module est obsolète, remplacé par [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
        "apihelp-tokens-param-type": "Types de jeton à demander.",
        "apihelp-tokens-example-edit": "Récupérer un jeton de modification (par défaut).",
index dcc27e7..292acab 100644 (file)
        "apihelp-setnotificationtimestamp-example-page": "Restaurar o estado de notificación para a <kbd>Páxina Principal</kbd>.",
        "apihelp-setnotificationtimestamp-example-pagetimestamp": "Fixar o selo de tempo de notificación para a <kbd>Main page</kbd> de forma que todas as edicións dende o 1 se xaneiro de 2012 queden sen revisar.",
        "apihelp-setnotificationtimestamp-example-allpages": "Restaurar o estado de notificación para as páxinas no espazo de nomes de <kbd>{{ns:user}}</kbd>.",
+       "apihelp-tag-param-reason": "Razón para o cambio.",
+       "apihelp-tag-example-rev": "Engadir a marca <kbd>vandalismo</kbd> á modificación con identificador 123 sen indicar unha razón.",
+       "apihelp-tag-example-log": "Eliminar a etiqueta <kbd>publicidade</kbd> da entrada do rexistro con identificador 123 co motivo <kbd>aplicada incorrectamente</kbd>",
        "apihelp-tokens-description": "Obter os identificadores para accións de modificación de datos.\n\nEste módulo está obsoleto e foi reemprazado por [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
        "apihelp-tokens-param-type": "Tipos de identificadores a consultar.",
        "apihelp-tokens-example-edit": "Recuperar un identificador de modificación (por defecto).",
        "apihelp-dumpfm-description": "Datos de saída en formato <code>var_dump()</code> de PHP(impresión en HTML).",
        "apihelp-json-description": "Datos de saída en formato JSON.",
        "apihelp-json-param-callback": "Se está especificado, inclúe a saída na chamada da función indicada. Para maior seguridade, todos os datos específicos do usuario serán restrinxidos.",
-       "apihelp-json-param-utf8": "Se está especificado, codifica a maioría (pero non todos) dos caracteres ASCII como UTF-8 no canto de reemprazalos con secuencias de escape hexadecimais.",
+       "apihelp-json-param-utf8": "Se está especificado, codifica a maioría (pero non todos) dos caracteres ASCII como UTF-8 no canto de reemprazalos con secuencias de escape hexadecimais. Por defecto cando <var>formatversion</var> non é <kbd>1</kbd>.",
        "apihelp-jsonfm-description": "Datos de saída en formato JSON(impresión en HTML).",
        "apihelp-none-description": "Ningunha saída.",
        "apihelp-php-description": "Datos de saída en formato serializado de PHP.",
index fcc7f7b..8121898 100644 (file)
@@ -42,6 +42,7 @@
        "apihelp-delete-param-unwatch": "הסרת הדף מרשימת המעקב של של המשתמש הנוכחי.",
        "apihelp-delete-example-simple": "מחיקת הדף הראשי",
        "apihelp-edit-param-text": "תוכן הדף.",
+       "apihelp-edit-param-tags": "אילו תגי שינוי להחיל על הגרסה.",
        "apihelp-edit-param-minor": "עריכה משנית.",
        "apihelp-edit-example-edit": "עריכת דף",
        "apihelp-emailuser-description": "שליחת דוא\"ל למשתמש.",
index 25e1002..2bc9de6 100644 (file)
@@ -13,6 +13,7 @@
        "apihelp-main-param-requestid": "Jehde Aanjahb vun heh weed widder med ußjejovve. Esuh kam_mer einzel Affrohre ussenein hallde.",
        "apihelp-main-param-servedby": "Donn däm ẞööver, dä et jedonn hät, singe Nahme med ußjävve.",
        "apihelp-main-param-curtimestamp": "Donn de aktoälle Zigg un et Dattum med ußjävve.",
+       "apihelp-main-param-uselang": "De schprohch för et Övversäzze vun Täxte un Nohreeschte. En Leß met de Köözelle kam_mer vun dä Sigg <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> holle, met <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">siprop=languages</kbd>, udder jiff <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">user</kbd> aan, öm dem aktoälle Metmaacher sing eetzde Schprohch ze krijje, udder nemm <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">content</kbd> öm heh dämm Wikki singe Ennhald sing Schprohch ze krijje.",
        "apihelp-block-description": "Ene Metmaacher schpärre.",
        "apihelp-block-param-user": "Däm Nahme vun däm Metmaacher, de <i lang=\"en\" xml:lang=\"en\" title=\"Internet Protocol\">IP</i>-Addräß udder dä Berätt, dä De Schpärre wells.",
        "apihelp-block-param-reason": "Der Schpärrjrond.",
@@ -22,6 +23,7 @@
        "apihelp-block-param-reblock": "Wann dä Metmaacher als jeschpächd es, donn dat övverschrihve.",
        "apihelp-block-param-watchuser": "Donn de Metmaachersigg un de Klaafsigg dohzoh op mig Oppaßleß säze.",
        "apihelp-block-example-ip-simple": "Donn de <i lang=\"en\" xmL:lang=\"en\" title=\"Internet Protocol\">IP</i>-Addräß <kbd>192.0.2.5</kbd> för drei ääsch schpärre mem Jrond: <kbd>Eestschlaach</kbd>.",
+       "apihelp-checktoken-param-token": "De Makehrong zom Pröhve.",
        "apihelp-compare-description": "Donn de Ongerscheide zwesche zwai Sigge beschtemme.\n\nDo moß derför jeweils en Väsjohn, en Övverschreff för di Sigg, odder ener Sigg iehr Kännong aanjävve, för de beide Sigge.",
        "apihelp-compare-param-fromtitle": "Der Tettel vun dä eezte Sigg zom verjlihsche.",
        "apihelp-compare-param-fromid": "De Kännong vun dä eezte Sigg zom verjlihsche.",
@@ -53,6 +55,7 @@
        "apihelp-edit-param-section": "De Nommer vum Affschnedd. Nemm „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">0</kbd>“ för wat vör der eezde Övverschreff schteihd. Ene neue Affscnedd määt mer met „<var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</var>“.",
        "apihelp-edit-param-sectiontitle": "De Övverschreff för ene neue Affschnett.",
        "apihelp-edit-param-text": "Dä Sigg ehre Ennhalld.",
+       "apihelp-edit-param-tags": "De Mekhonge för op heh di väsjohn aanzewännde.",
        "apihelp-edit-param-minor": "En klein Änderong.",
        "apihelp-edit-param-notminor": "Kein klein Änderong.",
        "apihelp-edit-param-bot": "Makeer heh di Änderog als vun enem Bot jemaat.",
        "apihelp-opensearch-param-search": "Noh wat söhke?",
        "apihelp-opensearch-param-limit": "De hühßte Aanzahl vun Äjeebnesse för zeröck ze jävve",
        "apihelp-opensearch-param-namespace": "En wällschem Appachtemang söhke.",
-       "apihelp-opensearch-param-redirects": "How to handle redirects:\n;return:Return the redirect itself.\n;resolve:Return the target page. May return fewer than $1limit results.\nFor historical reasons, the default is \"return\" for $1format=json and \"resolve\" for other formats.\n<!-- \nhttps://translatewiki.net/wiki/Thread:Support/About_MediaWiki:Apihelp-opensearch-param-redirects/en\n-->",
+       "apihelp-opensearch-param-redirects": "Wi met Ömleidonge ömjonn?\n;return:Jivv de Ömleidonge sällver uß.\n;resolve:Jiff de Sigg uß, woh de Ömleidong hen jeiht. Dat künnt winnijer wi „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1limit</code>“ Sigge ußjävve.\nTradizonäll es dä Schtandatt „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">return</code>“ för „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1format=json</code>“ un „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">resolve</code>“ för alle anndere.",
        "apihelp-opensearch-param-format": "Et Fommaht zom Ußjävve.",
        "apihelp-opensearch-example-te": "Fengk Sigge, di met <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Te</kbd> aanfange.",
        "apihelp-options-param-reset": "Säz de Enschtällonge op dem Wikki singe Standatt.",
        "apihelp-parse-param-summary": "De Zersammefaßong för ze pahse.",
        "apihelp-parse-param-section": "Holl blohß dann der Ennhalld vun däm Affschnett met dä Nommer, udder wann „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</kbd>“ enjejovve es, maach ene neu Affschnett derbei.",
        "apihelp-parse-param-sectiontitle": "De Övverschreff för dä neuje Afschnet, wann <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">section</var> = <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</kbd> es.\n\nAnders wi beim Beärbeide vun dä Sigg weed dä Parramehter nit dorsch de <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">summary</var> ußjetuusch, wann hä fottjelohße udder läddesch es.",
+       "apihelp-parse-param-disabletoc": "Donn et Ennhaldsverzeijscheneß en de Ußjahbe vottlohze.",
        "apihelp-parse-example-page": "Donn en Sigg pahse.",
        "apihelp-parse-example-text": "Donn Wikkitäx pahse.",
        "apihelp-parse-example-texttitle": "Donn Wikkitäx pahse, un jiff derför en Övverschreff för en Sigg aan.",
index 72044db..e7ee956 100644 (file)
@@ -89,6 +89,9 @@
        "api-help-title": "Ajuda API da MediaWiki",
        "api-help-main-header": "Módulo principal",
        "api-help-flag-deprecated": "Este módulo está obsoleto.",
+       "api-help-license": "Licença: [[$1|$2]]",
+       "api-help-license-noname": "Licença: [[$1|Ver ligação]]",
+       "api-help-license-unknown": "Licença: <span class=\"apihelp-unknown\">desconhecida</span>",
        "api-help-parameters": "{{PLURAL:$1|Parâmetro|Parâmetros}}:",
        "api-help-param-deprecated": "Obsoleto.",
        "api-help-param-required": "Este parâmetro é obrigatório.",
index d2c6ec9..876f598 100644 (file)
        "api-help-parameters": "Label for the API help parameters section\n\nParameters:\n* $1 - Number of parameters to be displayed\n{{Identical|Parameter}}",
        "api-help-param-deprecated": "Displayed in the API help for any deprecated parameter\n{{Identical|Deprecated}}",
        "api-help-param-required": "Displayed in the API help for any required parameter",
+       "api-help-datatypes-header": "Header for the data type section in the API help output",
+       "api-help-datatypes": "{{technical}} {{doc-important|Do not translate or reformat dates inside &lt;kbd%gt; tags}} Documentation of certain API data types\nSee also:\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd%gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-integer": "{{technical}} Used to indicate that a parameter is an integer or list of integers. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-boolean": "{{technical}} {{doc-important|Do not translate <code>Special:ApiHelp</code> in this message.}} Used to indicate that a parameter is a boolean. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-timestamp": "{{technical}} {{doc-important|Do not translate <code>Special:ApiHelp</code> in this message.}} Used to indicate that a parameter is a timestamp or list of timestamps. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-user": "{{technical}} Used to indicate that a parameter is a username or list of usernames. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-list": "Used to display the possible values for a parameter taking a list of values\n\nParameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes any number of values\n* $2 - Comma-separated list of values, possibly formatted using {{msg-mw|api-help-param-list-can-be-empty}}\n{{Identical|Value}}",
        "api-help-param-list-can-be-empty": "Used to indicate that one of the possible values in the list is the empty string.\n\nParameters:\n* $1 - Number of items in the rest of the list; may be 0\n* $2 - Remainder of the list as a comma-separated string",
        "api-help-param-limit": "Used to display the maximum value of a limit parameter\n\nParameters:\n* $1 - Maximum value",
diff --git a/includes/cache/MessageBlobStore.php b/includes/cache/MessageBlobStore.php
new file mode 100644 (file)
index 0000000..011cae6
--- /dev/null
@@ -0,0 +1,390 @@
+<?php
+/**
+ * Resource message blobs storage used by the resource loader.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Roan Kattouw
+ * @author Trevor Parscal
+ */
+
+/**
+ * This class provides access to the resource message blobs storage used by
+ * the ResourceLoader.
+ *
+ * A message blob is a JSON object containing the interface messages for a
+ * certain resource in a certain language. These message blobs are cached
+ * in the msg_resource table and automatically invalidated when one of their
+ * constituent messages or the resource itself is changed.
+ */
+class MessageBlobStore {
+       /**
+        * Get the singleton instance
+        *
+        * @since 1.24
+        * @deprecated since 1.25
+        * @return MessageBlobStore
+        */
+       public static function getInstance() {
+               wfDeprecated( __METHOD__, '1.25' );
+               return new self;
+       }
+
+       /**
+        * Get the message blobs for a set of modules
+        *
+        * @param ResourceLoader $resourceLoader
+        * @param array $modules Array of module objects keyed by module name
+        * @param string $lang Language code
+        * @return array An array mapping module names to message blobs
+        */
+       public function get( ResourceLoader $resourceLoader, $modules, $lang ) {
+               if ( !count( $modules ) ) {
+                       return array();
+               }
+               // Try getting from the DB first
+               $blobs = $this->getFromDB( $resourceLoader, array_keys( $modules ), $lang );
+
+               // Generate blobs for any missing modules and store them in the DB
+               $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) );
+               foreach ( $missing as $name ) {
+                       $blob = $this->insertMessageBlob( $name, $modules[$name], $lang );
+                       if ( $blob ) {
+                               $blobs[$name] = $blob;
+                       }
+               }
+
+               return $blobs;
+       }
+
+       /**
+        * Generate and insert a new message blob. If the blob was already
+        * present, it is not regenerated; instead, the preexisting blob
+        * is fetched and returned.
+        *
+        * @param string $name Module name
+        * @param ResourceLoaderModule $module
+        * @param string $lang Language code
+        * @return mixed Message blob or false if the module has no messages
+        */
+       public function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
+               $blob = $this->generateMessageBlob( $module, $lang );
+
+               if ( !$blob ) {
+                       return false;
+               }
+
+               try {
+                       $dbw = wfGetDB( DB_MASTER );
+                       $success = $dbw->insert( 'msg_resource', array(
+                                       'mr_lang' => $lang,
+                                       'mr_resource' => $name,
+                                       'mr_blob' => $blob,
+                                       'mr_timestamp' => $dbw->timestamp()
+                               ),
+                               __METHOD__,
+                               array( 'IGNORE' )
+                       );
+
+                       if ( $success ) {
+                               if ( $dbw->affectedRows() == 0 ) {
+                                       // Blob was already present, fetch it
+                                       $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
+                                                       'mr_resource' => $name,
+                                                       'mr_lang' => $lang,
+                                               ),
+                                               __METHOD__
+                                       );
+                               } else {
+                                       // Update msg_resource_links
+                                       $rows = array();
+
+                                       foreach ( $module->getMessages() as $key ) {
+                                               $rows[] = array(
+                                                       'mrl_resource' => $name,
+                                                       'mrl_message' => $key
+                                               );
+                                       }
+                                       $dbw->insert( 'msg_resource_links', $rows,
+                                               __METHOD__, array( 'IGNORE' )
+                                       );
+                               }
+                       }
+               } catch ( DBError $e ) {
+                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
+               }
+               return $blob;
+       }
+
+       /**
+        * Update the message blob for a given module in a given language
+        *
+        * @param string $name Module name
+        * @param ResourceLoaderModule $module
+        * @param string $lang Language code
+        * @return string Regenerated message blob, or null if there was no blob for
+        *   the given module/language pair.
+        */
+       public function updateModule( $name, ResourceLoaderModule $module, $lang ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $row = $dbw->selectRow( 'msg_resource', 'mr_blob',
+                       array( 'mr_resource' => $name, 'mr_lang' => $lang ),
+                       __METHOD__
+               );
+               if ( !$row ) {
+                       return null;
+               }
+
+               // Save the old and new blobs for later
+               $oldBlob = $row->mr_blob;
+               $newBlob = $this->generateMessageBlob( $module, $lang );
+
+               try {
+                       $newRow = array(
+                               'mr_resource' => $name,
+                               'mr_lang' => $lang,
+                               'mr_blob' => $newBlob,
+                               'mr_timestamp' => $dbw->timestamp()
+                       );
+
+                       $dbw->replace( 'msg_resource',
+                               array( array( 'mr_resource', 'mr_lang' ) ),
+                               $newRow, __METHOD__
+                       );
+
+                       // Figure out which messages were added and removed
+                       $oldMessages = array_keys( FormatJson::decode( $oldBlob, true ) );
+                       $newMessages = array_keys( FormatJson::decode( $newBlob, true ) );
+                       $added = array_diff( $newMessages, $oldMessages );
+                       $removed = array_diff( $oldMessages, $newMessages );
+
+                       // Delete removed messages, insert added ones
+                       if ( $removed ) {
+                               $dbw->delete( 'msg_resource_links', array(
+                                               'mrl_resource' => $name,
+                                               'mrl_message' => $removed
+                                       ), __METHOD__
+                               );
+                       }
+
+                       $newLinksRows = array();
+
+                       foreach ( $added as $message ) {
+                               $newLinksRows[] = array(
+                                       'mrl_resource' => $name,
+                                       'mrl_message' => $message
+                               );
+                       }
+
+                       if ( $newLinksRows ) {
+                               $dbw->insert( 'msg_resource_links', $newLinksRows, __METHOD__,
+                                       array( 'IGNORE' ) // just in case
+                               );
+                       }
+               } catch ( Exception $e ) {
+                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
+               }
+               return $newBlob;
+       }
+
+       /**
+        * Update a single message in all message blobs it occurs in.
+        *
+        * @param string $key Message key
+        */
+       public function updateMessage( $key ) {
+               try {
+                       $dbw = wfGetDB( DB_MASTER );
+
+                       // Keep running until the updates queue is empty.
+                       // Due to update conflicts, the queue might not be emptied
+                       // in one iteration.
+                       $updates = null;
+                       do {
+                               $updates = $this->getUpdatesForMessage( $key, $updates );
+
+                               foreach ( $updates as $k => $update ) {
+                                       // Update the row on the condition that it
+                                       // didn't change since we fetched it by putting
+                                       // the timestamp in the WHERE clause.
+                                       $success = $dbw->update( 'msg_resource',
+                                               array(
+                                                       'mr_blob' => $update['newBlob'],
+                                                       'mr_timestamp' => $dbw->timestamp() ),
+                                               array(
+                                                       'mr_resource' => $update['resource'],
+                                                       'mr_lang' => $update['lang'],
+                                                       'mr_timestamp' => $update['timestamp'] ),
+                                               __METHOD__
+                                       );
+
+                                       // Only requeue conflicted updates.
+                                       // If update() returned false, don't retry, for
+                                       // fear of getting into an infinite loop
+                                       if ( !( $success && $dbw->affectedRows() == 0 ) ) {
+                                               // Not conflicted
+                                               unset( $updates[$k] );
+                                       }
+                               }
+                       } while ( count( $updates ) );
+
+                       // No need to update msg_resource_links because we didn't add
+                       // or remove any messages, we just changed their contents.
+               } catch ( Exception $e ) {
+                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
+               }
+       }
+
+       public function clear() {
+               // TODO: Give this some more thought
+               try {
+                       // Not using TRUNCATE, because that needs extra permissions,
+                       // which maybe not granted to the database user.
+                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw->delete( 'msg_resource', '*', __METHOD__ );
+                       $dbw->delete( 'msg_resource_links', '*', __METHOD__ );
+               } catch ( Exception $e ) {
+                       wfDebug( __METHOD__ . " failed to update DB: $e\n" );
+               }
+       }
+
+       /**
+        * Create an update queue for updateMessage()
+        *
+        * @param string $key Message key
+        * @param array $prevUpdates Updates queue to refresh or null to build a fresh update queue
+        * @return array Updates queue
+        */
+       private function getUpdatesForMessage( $key, $prevUpdates = null ) {
+               $dbw = wfGetDB( DB_MASTER );
+
+               if ( is_null( $prevUpdates ) ) {
+                       // Fetch all blobs referencing $key
+                       $res = $dbw->select(
+                               array( 'msg_resource', 'msg_resource_links' ),
+                               array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
+                               array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
+                               __METHOD__
+                       );
+               } else {
+                       // Refetch the blobs referenced by $prevUpdates
+
+                       // Reorganize the (resource, lang) pairs in the format
+                       // expected by makeWhereFrom2d()
+                       $twoD = array();
+
+                       foreach ( $prevUpdates as $update ) {
+                               $twoD[$update['resource']][$update['lang']] = true;
+                       }
+
+                       $res = $dbw->select( 'msg_resource',
+                               array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
+                               $dbw->makeWhereFrom2d( $twoD, 'mr_resource', 'mr_lang' ),
+                               __METHOD__
+                       );
+               }
+
+               // Build the new updates queue
+               $updates = array();
+
+               foreach ( $res as $row ) {
+                       $updates[] = array(
+                               'resource' => $row->mr_resource,
+                               'lang' => $row->mr_lang,
+                               'timestamp' => $row->mr_timestamp,
+                               'newBlob' => $this->reencodeBlob( $row->mr_blob, $key, $row->mr_lang )
+                       );
+               }
+
+               return $updates;
+       }
+
+       /**
+        * Reencode a message blob with the updated value for a message
+        *
+        * @param string $blob Message blob (JSON object)
+        * @param string $key Message key
+        * @param string $lang Language code
+        * @return string Message blob with $key replaced with its new value
+        */
+       private function reencodeBlob( $blob, $key, $lang ) {
+               $decoded = FormatJson::decode( $blob, true );
+               $decoded[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
+
+               return FormatJson::encode( (object)$decoded );
+       }
+
+       /**
+        * Get the message blobs for a set of modules from the database.
+        * Modules whose blobs are not in the database are silently dropped.
+        *
+        * @param ResourceLoader $resourceLoader
+        * @param array $modules Array of module names
+        * @param string $lang Language code
+        * @throws MWException
+        * @return array Array mapping module names to blobs
+        */
+       private function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) {
+               $config = $resourceLoader->getConfig();
+               $retval = array();
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'msg_resource',
+                       array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
+                       array( 'mr_resource' => $modules, 'mr_lang' => $lang ),
+                       __METHOD__
+               );
+
+               foreach ( $res as $row ) {
+                       $module = $resourceLoader->getModule( $row->mr_resource );
+                       if ( !$module ) {
+                               // This shouldn't be possible
+                               throw new MWException( __METHOD__ . ' passed an invalid module name' );
+                       }
+
+                       // Update the module's blobs if the set of messages changed or if the blob is
+                       // older than the CacheEpoch setting
+                       $keys = array_keys( FormatJson::decode( $row->mr_blob, true ) );
+                       $values = array_values( array_unique( $module->getMessages() ) );
+                       if ( $keys !== $values
+                               || wfTimestamp( TS_MW, $row->mr_timestamp ) <= $config->get( 'CacheEpoch' )
+                       ) {
+                               $retval[$row->mr_resource] = $this->updateModule( $row->mr_resource, $module, $lang );
+                       } else {
+                               $retval[$row->mr_resource] = $row->mr_blob;
+                       }
+               }
+
+               return $retval;
+       }
+
+       /**
+        * Generate the message blob for a given module in a given language.
+        *
+        * @param ResourceLoaderModule $module
+        * @param string $lang Language code
+        * @return string JSON object
+        */
+       private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
+               $messages = array();
+
+               foreach ( $module->getMessages() as $key ) {
+                       $messages[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
+               }
+
+               return FormatJson::encode( (object)$messages );
+       }
+}
diff --git a/includes/changetags/ChangeTags.php b/includes/changetags/ChangeTags.php
new file mode 100644 (file)
index 0000000..43f957c
--- /dev/null
@@ -0,0 +1,1222 @@
+<?php
+/**
+ * Recent changes tagging.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Change tagging
+ */
+
+class ChangeTags {
+       /**
+        * Can't delete tags with more than this many uses. Similar in intent to
+        * the bigdelete user right
+        * @todo Use the job queue for tag deletion to avoid this restriction
+        */
+       const MAX_DELETE_USES = 5000;
+
+       /**
+        * Creates HTML for the given tags
+        *
+        * @param string $tags Comma-separated list of tags
+        * @param string $page A label for the type of action which is being displayed,
+        *   for example: 'history', 'contributions' or 'newpages'
+        * @return array Array with two items: (html, classes)
+        *   - html: String: HTML for displaying the tags (empty string when param $tags is empty)
+        *   - classes: Array of strings: CSS classes used in the generated html, one class for each tag
+        */
+       public static function formatSummaryRow( $tags, $page ) {
+               global $wgLang;
+
+               if ( !$tags ) {
+                       return array( '', array() );
+               }
+
+               $classes = array();
+
+               $tags = explode( ',', $tags );
+               $displayTags = array();
+               foreach ( $tags as $tag ) {
+                       if ( !$tag ) {
+                               continue;
+                       }
+                       $description = self::tagDescription( $tag );
+                       if ( $description === false ) {
+                               continue;
+                       }
+                       $displayTags[] = Xml::tags(
+                               'span',
+                               array( 'class' => 'mw-tag-marker ' .
+                                                               Sanitizer::escapeClass( "mw-tag-marker-$tag" ) ),
+                               $description
+                       );
+                       $classes[] = Sanitizer::escapeClass( "mw-tag-$tag" );
+               }
+
+               if ( !$displayTags ) {
+                       return array( '', array() );
+               }
+
+               $markers = wfMessage( 'tag-list-wrapper' )
+                       ->numParams( count( $displayTags ) )
+                       ->rawParams( $wgLang->commaList( $displayTags ) )
+                       ->parse();
+               $markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
+
+               return array( $markers, $classes );
+       }
+
+       /**
+        * Get a short description for a tag.
+        *
+        * Checks if message key "mediawiki:tag-$tag" exists. If it does not,
+        * returns the HTML-escaped tag name. Uses the message if the message
+        * exists, provided it is not disabled. If the message is disabled,
+        * we consider the tag hidden, and return false.
+        *
+        * @param string $tag Tag
+        * @return string|bool Tag description or false if tag is to be hidden.
+        * @since 1.25 Returns false if tag is to be hidden.
+        */
+       public static function tagDescription( $tag ) {
+               $msg = wfMessage( "tag-$tag" );
+               if ( !$msg->exists() ) {
+                       // No such message, so return the HTML-escaped tag name.
+                       return htmlspecialchars( $tag );
+               }
+               if ( $msg->isDisabled() ) {
+                       // The message exists but is disabled, hide the tag.
+                       return false;
+               }
+
+               // Message exists and isn't disabled, use it.
+               return $msg->parse();
+       }
+
+       /**
+        * Add tags to a change given its rc_id, rev_id and/or log_id
+        *
+        * @param string|array $tags Tags to add to the change
+        * @param int|null $rc_id The rc_id of the change to add the tags to
+        * @param int|null $rev_id The rev_id of the change to add the tags to
+        * @param int|null $log_id The log_id of the change to add the tags to
+        * @param string $params Params to put in the ct_params field of table 'change_tag'
+        *
+        * @throws MWException
+        * @return bool False if no changes are made, otherwise true
+        */
+       public static function addTags( $tags, $rc_id = null, $rev_id = null,
+               $log_id = null, $params = null
+       ) {
+               $result = self::updateTags( $tags, null, $rc_id, $rev_id, $log_id, $params );
+               return (bool)$result[0];
+       }
+
+       /**
+        * Add and remove tags to/from a change given its rc_id, rev_id and/or log_id,
+        * without verifying that the tags exist or are valid. If a tag is present in
+        * both $tagsToAdd and $tagsToRemove, it will be removed.
+        *
+        * This function should only be used by extensions to manipulate tags they
+        * have registered using the ListDefinedTags hook. When dealing with user
+        * input, call updateTagsWithChecks() instead.
+        *
+        * @param string|array|null $tagsToAdd Tags to add to the change
+        * @param string|array|null $tagsToRemove Tags to remove from the change
+        * @param int|null &$rc_id The rc_id of the change to add the tags to.
+        * Pass a variable whose value is null if the rc_id is not relevant or unknown.
+        * @param int|null &$rev_id The rev_id of the change to add the tags to.
+        * Pass a variable whose value is null if the rev_id is not relevant or unknown.
+        * @param int|null &$log_id The log_id of the change to add the tags to.
+        * Pass a variable whose value is null if the log_id is not relevant or unknown.
+        * @param string $params Params to put in the ct_params field of table
+        * 'change_tag' when adding tags
+        *
+        * @throws MWException When $rc_id, $rev_id and $log_id are all null
+        * @return array Index 0 is an array of tags actually added, index 1 is an
+        * array of tags actually removed, index 2 is an array of tags present on the
+        * revision or log entry before any changes were made
+        *
+        * @since 1.25
+        */
+       public static function updateTags( $tagsToAdd, $tagsToRemove, &$rc_id = null,
+               &$rev_id = null, &$log_id = null, $params = null ) {
+
+               $tagsToAdd = array_filter( (array)$tagsToAdd ); // Make sure we're submitting all tags...
+               $tagsToRemove = array_filter( (array)$tagsToRemove );
+
+               if ( !$rc_id && !$rev_id && !$log_id ) {
+                       throw new MWException( 'At least one of: RCID, revision ID, and log ID MUST be ' .
+                               'specified when adding or removing a tag from a change!' );
+               }
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               // Might as well look for rcids and so on.
+               if ( !$rc_id ) {
+                       // Info might be out of date, somewhat fractionally, on slave.
+                       if ( $log_id ) {
+                               $rc_id = $dbw->selectField(
+                                       'recentchanges',
+                                       'rc_id',
+                                       array( 'rc_logid' => $log_id ),
+                                       __METHOD__
+                               );
+                       } elseif ( $rev_id ) {
+                               $rc_id = $dbw->selectField(
+                                       'recentchanges',
+                                       'rc_id',
+                                       array( 'rc_this_oldid' => $rev_id ),
+                                       __METHOD__
+                               );
+                       }
+               } elseif ( !$log_id && !$rev_id ) {
+                       // Info might be out of date, somewhat fractionally, on slave.
+                       $log_id = $dbw->selectField(
+                               'recentchanges',
+                               'rc_logid',
+                               array( 'rc_id' => $rc_id ),
+                               __METHOD__
+                       );
+                       $rev_id = $dbw->selectField(
+                               'recentchanges',
+                               'rc_this_oldid',
+                               array( 'rc_id' => $rc_id ),
+                               __METHOD__
+                       );
+               }
+
+               // update the tag_summary row
+               $prevTags = array();
+               if ( !self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $rc_id, $rev_id,
+                       $log_id, $prevTags ) ) {
+
+                       // nothing to do
+                       return array( array(), array(), $prevTags );
+               }
+
+               // insert a row into change_tag for each new tag
+               if ( count( $tagsToAdd ) ) {
+                       $tagsRows = array();
+                       foreach ( $tagsToAdd as $tag ) {
+                               // Filter so we don't insert NULLs as zero accidentally.
+                               // Keep in mind that $rc_id === null means "I don't care/know about the
+                               // rc_id, just delete $tag on this revision/log entry". It doesn't
+                               // mean "only delete tags on this revision/log WHERE rc_id IS NULL".
+                               $tagsRows[] = array_filter(
+                                       array(
+                                               'ct_tag' => $tag,
+                                               'ct_rc_id' => $rc_id,
+                                               'ct_log_id' => $log_id,
+                                               'ct_rev_id' => $rev_id,
+                                               'ct_params' => $params
+                                       )
+                               );
+                       }
+
+                       $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array( 'IGNORE' ) );
+               }
+
+               // delete from change_tag
+               if ( count( $tagsToRemove ) ) {
+                       foreach ( $tagsToRemove as $tag ) {
+                               $conds = array_filter(
+                                       array(
+                                               'ct_tag' => $tag,
+                                               'ct_rc_id' => $rc_id,
+                                               'ct_log_id' => $log_id,
+                                               'ct_rev_id' => $rev_id
+                                       )
+                               );
+                               $dbw->delete( 'change_tag', $conds, __METHOD__ );
+                       }
+               }
+
+               self::purgeTagUsageCache();
+               return array( $tagsToAdd, $tagsToRemove, $prevTags );
+       }
+
+       /**
+        * Adds or removes a given set of tags to/from the relevant row of the
+        * tag_summary table. Modifies the tagsToAdd and tagsToRemove arrays to
+        * reflect the tags that were actually added and/or removed.
+        *
+        * @param array &$tagsToAdd
+        * @param array &$tagsToRemove If a tag is present in both $tagsToAdd and
+        * $tagsToRemove, it will be removed
+        * @param int|null $rc_id Null if not known or not applicable
+        * @param int|null $rev_id Null if not known or not applicable
+        * @param int|null $log_id Null if not known or not applicable
+        * @param array &$prevTags Optionally outputs a list of the tags that were
+        * in the tag_summary row to begin with
+        * @return bool True if any modifications were made, otherwise false
+        * @since 1.25
+        */
+       protected static function updateTagSummaryRow( &$tagsToAdd, &$tagsToRemove,
+               $rc_id, $rev_id, $log_id, &$prevTags = array() ) {
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $tsConds = array_filter( array(
+                       'ts_rc_id' => $rc_id,
+                       'ts_rev_id' => $rev_id,
+                       'ts_log_id' => $log_id
+               ) );
+
+               // Can't both add and remove a tag at the same time...
+               $tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
+
+               // Update the summary row.
+               // $prevTags can be out of date on slaves, especially when addTags is called consecutively,
+               // causing loss of tags added recently in tag_summary table.
+               $prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
+               $prevTags = $prevTags ? $prevTags : '';
+               $prevTags = array_filter( explode( ',', $prevTags ) );
+
+               // add tags
+               $tagsToAdd = array_values( array_diff( $tagsToAdd, $prevTags ) );
+               $newTags = array_unique( array_merge( $prevTags, $tagsToAdd ) );
+
+               // remove tags
+               $tagsToRemove = array_values( array_intersect( $tagsToRemove, $newTags ) );
+               $newTags = array_values( array_diff( $newTags, $tagsToRemove ) );
+
+               sort( $prevTags );
+               sort( $newTags );
+               if ( $prevTags == $newTags ) {
+                       // No change.
+                       return false;
+               }
+
+               if ( !$newTags ) {
+                       // no tags left, so delete the row altogether
+                       $dbw->delete( 'tag_summary', $tsConds, __METHOD__ );
+               } else {
+                       $dbw->replace( 'tag_summary',
+                               array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
+                               array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ),
+                               __METHOD__
+                       );
+               }
+
+               return true;
+       }
+
+       /**
+        * Helper function to generate a fatal status with a 'not-allowed' type error.
+        *
+        * @param string $msgOne Message key to use in the case of one tag
+        * @param string $msgMulti Message key to use in the case of more than one tag
+        * @param array $tags Restricted tags (passed as $1 into the message, count of
+        * $tags passed as $2)
+        * @return Status
+        * @since 1.25
+        */
+       protected static function restrictedTagError( $msgOne, $msgMulti, $tags ) {
+               $lang = RequestContext::getMain()->getLanguage();
+               $count = count( $tags );
+               return Status::newFatal( ( $count > 1 ) ? $msgMulti : $msgOne,
+                       $lang->commaList( $tags ), $count );
+       }
+
+       /**
+        * Is it OK to allow the user to apply all the specified tags at the same time
+        * as they edit/make the change?
+        *
+        * @param array $tags Tags that you are interested in applying
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canAddTagsAccompanyingChange( array $tags,
+               User $user = null ) {
+
+               if ( !is_null( $user ) && !$user->isAllowed( 'applychangetags' ) ) {
+                       return Status::newFatal( 'tags-apply-no-permission' );
+               }
+
+               // to be applied, a tag has to be explicitly defined
+               // @todo Allow extensions to define tags that can be applied by users...
+               $allowedTags = self::listExplicitlyDefinedTags();
+               $disallowedTags = array_diff( $tags, $allowedTags );
+               if ( $disallowedTags ) {
+                       return self::restrictedTagError( 'tags-apply-not-allowed-one',
+                               'tags-apply-not-allowed-multi', $disallowedTags );
+               }
+
+               return Status::newGood();
+       }
+
+       /**
+        * Adds tags to a given change, checking whether it is allowed first, but
+        * without adding a log entry. Useful for cases where the tag is being added
+        * along with the action that generated the change (e.g. tagging an edit as
+        * it is being made).
+        *
+        * Extensions should not use this function, unless directly handling a user
+        * request to add a particular tag. Normally, extensions should call
+        * ChangeTags::updateTags() instead.
+        *
+        * @param array $tags Tags to apply
+        * @param int|null $rc_id The rc_id of the change to add the tags to
+        * @param int|null $rev_id The rev_id of the change to add the tags to
+        * @param int|null $log_id The log_id of the change to add the tags to
+        * @param string $params Params to put in the ct_params field of table
+        * 'change_tag' when adding tags
+        * @param User $user Who to give credit for the action
+        * @return Status
+        * @since 1.25
+        */
+       public static function addTagsAccompanyingChangeWithChecks( array $tags,
+               $rc_id, $rev_id, $log_id, $params, User $user ) {
+
+               // are we allowed to do this?
+               $result = self::canAddTagsAccompanyingChange( $tags, $user );
+               if ( !$result->isOK() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // do it!
+               self::addTags( $tagsToAdd, $rc_id, $rev_id, $log_id, $params );
+
+               return Status::newGood( true );
+       }
+
+       /**
+        * Is it OK to allow the user to adds and remove the given tags tags to/from a
+        * change?
+        *
+        * @param array $tagsToAdd Tags that you are interested in adding
+        * @param array $tagsToRemove Tags that you are interested in removing
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canUpdateTags( array $tagsToAdd, array $tagsToRemove,
+               User $user = null ) {
+
+               if ( !is_null( $user ) && !$user->isAllowed( 'changetags' ) ) {
+                       return Status::newFatal( 'tags-update-no-permission' );
+               }
+
+               // to be added, a tag has to be explicitly defined
+               // @todo Allow extensions to define tags that can be applied by users...
+               $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
+               $diff = array_diff( $tagsToAdd, $explicitlyDefinedTags );
+               if ( $diff ) {
+                       return self::restrictedTagError( 'tags-update-add-not-allowed-one',
+                               'tags-update-add-not-allowed-multi', $diff );
+               }
+
+               // to be removed, a tag has to be either explicitly defined or not defined
+               // at all
+               $definedTags = self::listDefinedTags();
+               $diff = array_diff( $tagsToRemove, $explicitlyDefinedTags );
+               if ( $diff ) {
+                       $intersect = array_intersect( $diff, $definedTags );
+                       if ( $intersect ) {
+                               return self::restrictedTagError( 'tags-update-remove-not-allowed-one',
+                                       'tags-update-remove-not-allowed-multi', $intersect );
+                       }
+               }
+
+               return Status::newGood();
+       }
+
+       /**
+        * Adds and/or removes tags to/from a given change, checking whether it is
+        * allowed first, and adding a log entry afterwards.
+        *
+        * Includes a call to ChangeTag::canUpdateTags(), so your code doesn't need
+        * to do that. However, it doesn't check whether the *_id parameters are a
+        * valid combination. That is up to you to enforce. See ApiTag::execute() for
+        * an example.
+        *
+        * @param array|null $tagsToAdd If none, pass array() or null
+        * @param array|null $tagsToRemove If none, pass array() or null
+        * @param int|null $rc_id The rc_id of the change to add the tags to
+        * @param int|null $rev_id The rev_id of the change to add the tags to
+        * @param int|null $log_id The log_id of the change to add the tags to
+        * @param string $params Params to put in the ct_params field of table
+        * 'change_tag' when adding tags
+        * @param string $reason Comment for the log
+        * @param User $user Who to give credit for the action
+        * @return Status If successful, the value of this Status object will be an
+        * object (stdClass) with the following fields:
+        *  - logId: the ID of the added log entry, or null if no log entry was added
+        *    (i.e. no operation was performed)
+        *  - addedTags: an array containing the tags that were actually added
+        *  - removedTags: an array containing the tags that were actually removed
+        * @since 1.25
+        */
+       public static function updateTagsWithChecks( $tagsToAdd, $tagsToRemove,
+               $rc_id, $rev_id, $log_id, $params, $reason, User $user ) {
+
+               if ( is_null( $tagsToAdd ) ) {
+                       $tagsToAdd = array();
+               }
+               if ( is_null( $tagsToRemove ) ) {
+                       $tagsToRemove = array();
+               }
+               if ( !$tagsToAdd && !$tagsToRemove ) {
+                       // no-op, don't bother
+                       return Status::newGood( (object)array(
+                               'logId' => null,
+                               'addedTags' => array(),
+                               'removedTags' => array(),
+                       ) );
+               }
+
+               // are we allowed to do this?
+               $result = self::canUpdateTags( $tagsToAdd, $tagsToRemove, $user );
+               if ( !$result->isOK() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // basic rate limiting
+               if ( $user->pingLimiter( 'changetag' ) ) {
+                       return Status::newFatal( 'actionthrottledtext' );
+               }
+
+               // do it!
+               list( $tagsAdded, $tagsRemoved, $initialTags ) = self::updateTags( $tagsToAdd,
+                       $tagsToRemove, $rc_id, $rev_id, $log_id, $params );
+               if ( !$tagsAdded && !$tagsRemoved ) {
+                       // no-op, don't log it
+                       return Status::newGood( (object)array(
+                               'logId' => null,
+                               'addedTags' => array(),
+                               'removedTags' => array(),
+                       ) );
+               }
+
+               // log it
+               $logEntry = new ManualLogEntry( 'tag', 'update' );
+               $logEntry->setPerformer( $user );
+               $logEntry->setComment( $reason );
+
+               // find the appropriate target page
+               if ( $rev_id ) {
+                       $rev = Revision::newFromId( $rev_id );
+                       if ( $rev ) {
+                               $title = $rev->getTitle();
+                               $logEntry->setTarget( $rev->getTitle() );
+                       }
+               } elseif ( $log_id ) {
+                       // This function is from revision deletion logic and has nothing to do with
+                       // change tags, but it appears to be the only other place in core where we
+                       // perform logged actions on log items.
+                       $logEntry->setTarget( RevDelLogList::suggestTarget( 0, array( $log_id ) ) );
+               }
+
+               if ( !$logEntry->getTarget() ) {
+                       // target is required, so we have to set something
+                       $logEntry->setTarget( SpecialPage::getTitleFor( 'Tags' ) );
+               }
+
+               $logParams = array(
+                       '4::revid' => $rev_id,
+                       '5::logid' => $log_id,
+                       '6:list:tagsAdded' => $tagsAdded,
+                       '7:number:tagsAddedCount' => count( $tagsAdded ),
+                       '8:list:tagsRemoved' => $tagsRemoved,
+                       '9:number:tagsRemovedCount' => count( $tagsRemoved ),
+                       'initialTags' => $initialTags,
+               );
+               $logEntry->setParameters( $logParams );
+               $logEntry->setRelations( array( 'Tag' => array_merge( $tagsAdded, $tagsRemoved ) ) );
+
+               $dbw = wfGetDB( DB_MASTER );
+               $logId = $logEntry->insert( $dbw );
+               // Only send this to UDP, not RC, similar to patrol events
+               $logEntry->publish( $logId, 'udp' );
+
+               return Status::newGood( (object)array(
+                       'logId' => $logId,
+                       'addedTags' => $tagsAdded,
+                       'removedTags' => $tagsRemoved,
+               ) );
+       }
+
+       /**
+        * Applies all tags-related changes to a query.
+        * Handles selecting tags, and filtering.
+        * Needs $tables to be set up properly, so we can figure out which join conditions to use.
+        *
+        * @param string|array $tables Table names, see DatabaseBase::select
+        * @param string|array $fields Fields used in query, see DatabaseBase::select
+        * @param string|array $conds Conditions used in query, see DatabaseBase::select
+        * @param array $join_conds Join conditions, see DatabaseBase::select
+        * @param array $options Options, see Database::select
+        * @param bool|string $filter_tag Tag to select on
+        *
+        * @throws MWException When unable to determine appropriate JOIN condition for tagging
+        */
+       public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
+                                                                               &$join_conds, &$options, $filter_tag = false ) {
+               global $wgRequest, $wgUseTagFilter;
+
+               if ( $filter_tag === false ) {
+                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
+               }
+
+               // Figure out which conditions can be done.
+               if ( in_array( 'recentchanges', $tables ) ) {
+                       $join_cond = 'ct_rc_id=rc_id';
+               } elseif ( in_array( 'logging', $tables ) ) {
+                       $join_cond = 'ct_log_id=log_id';
+               } elseif ( in_array( 'revision', $tables ) ) {
+                       $join_cond = 'ct_rev_id=rev_id';
+               } elseif ( in_array( 'archive', $tables ) ) {
+                       $join_cond = 'ct_rev_id=ar_rev_id';
+               } else {
+                       throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
+               }
+
+               $fields['ts_tags'] = wfGetDB( DB_SLAVE )->buildGroupConcatField(
+                       ',', 'change_tag', 'ct_tag', $join_cond
+               );
+
+               if ( $wgUseTagFilter && $filter_tag ) {
+                       // Somebody wants to filter on a tag.
+                       // Add an INNER JOIN on change_tag
+
+                       $tables[] = 'change_tag';
+                       $join_conds['change_tag'] = array( 'INNER JOIN', $join_cond );
+                       $conds['ct_tag'] = $filter_tag;
+               }
+       }
+
+       /**
+        * Build a text box to select a change tag
+        *
+        * @param string $selected Tag to select by default
+        * @param bool $fullForm
+        *        - if false, then it returns an array of (label, form).
+        *        - if true, it returns an entire form around the selector.
+        * @param Title $title Title object to send the form to.
+        *        Used when, and only when $fullForm is true.
+        * @return string|array
+        *        - if $fullForm is false: Array with
+        *        - if $fullForm is true: String, html fragment
+        */
+       public static function buildTagFilterSelector( $selected = '',
+               $fullForm = false, Title $title = null
+       ) {
+               global $wgUseTagFilter;
+
+               if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) ) {
+                       return $fullForm ? '' : array();
+               }
+
+               $data = array(
+                       Html::rawElement(
+                               'label',
+                               array( 'for' => 'tagfilter' ),
+                               wfMessage( 'tag-filter' )->parse()
+                       ),
+                       Xml::input(
+                               'tagfilter',
+                               20,
+                               $selected,
+                               array( 'class' => 'mw-tagfilter-input mw-ui-input mw-ui-input-inline', 'id' => 'tagfilter' )
+                       )
+               );
+
+               if ( !$fullForm ) {
+                       return $data;
+               }
+
+               $html = implode( '&#160;', $data );
+               $html .= "\n" .
+                       Xml::element(
+                               'input',
+                               array( 'type' => 'submit', 'value' => wfMessage( 'tag-filter-submit' )->text() )
+                       );
+               $html .= "\n" . Html::hidden( 'title', $title->getPrefixedText() );
+               $html = Xml::tags(
+                       'form',
+                       array( 'action' => $title->getLocalURL(), 'class' => 'mw-tagfilter-form', 'method' => 'get' ),
+                       $html
+               );
+
+               return $html;
+       }
+
+       /**
+        * Defines a tag in the valid_tag table, without checking that the tag name
+        * is valid.
+        * Extensions should NOT use this function; they can use the ListDefinedTags
+        * hook instead.
+        *
+        * @param string $tag Tag to create
+        * @since 1.25
+        */
+       public static function defineTag( $tag ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->replace( 'valid_tag',
+                       array( 'vt_tag' ),
+                       array( 'vt_tag' => $tag ),
+                       __METHOD__ );
+
+               // clear the memcache of defined tags
+               self::purgeTagCacheAll();
+       }
+
+       /**
+        * Removes a tag from the valid_tag table. The tag may remain in use by
+        * extensions, and may still show up as 'defined' if an extension is setting
+        * it from the ListDefinedTags hook.
+        *
+        * @param string $tag Tag to remove
+        * @since 1.25
+        */
+       public static function undefineTag( $tag ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete( 'valid_tag', array( 'vt_tag' => $tag ), __METHOD__ );
+
+               // clear the memcache of defined tags
+               self::purgeTagCacheAll();
+       }
+
+       /**
+        * Writes a tag action into the tag management log.
+        *
+        * @param string $action
+        * @param string $tag
+        * @param string $reason
+        * @param User $user Who to attribute the action to
+        * @param int $tagCount For deletion only, how many usages the tag had before
+        * it was deleted.
+        * @since 1.25
+        */
+       protected static function logTagManagementAction( $action, $tag, $reason,
+               User $user, $tagCount = null ) {
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $logEntry = new ManualLogEntry( 'managetags', $action );
+               $logEntry->setPerformer( $user );
+               // target page is not relevant, but it has to be set, so we just put in
+               // the title of Special:Tags
+               $logEntry->setTarget( Title::newFromText( 'Special:Tags' ) );
+               $logEntry->setComment( $reason );
+
+               $params = array( '4::tag' => $tag );
+               if ( !is_null( $tagCount ) ) {
+                       $params['5:number:count'] = $tagCount;
+               }
+               $logEntry->setParameters( $params );
+               $logEntry->setRelations( array( 'Tag' => $tag ) );
+
+               $logId = $logEntry->insert( $dbw );
+               $logEntry->publish( $logId );
+               return $logId;
+       }
+
+       /**
+        * Is it OK to allow the user to activate this tag?
+        *
+        * @param string $tag Tag that you are interested in activating
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canActivateTag( $tag, User $user = null ) {
+               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+                       return Status::newFatal( 'tags-manage-no-permission' );
+               }
+
+               // non-existing tags cannot be activated
+               $tagUsage = self::tagUsageStatistics();
+               if ( !isset( $tagUsage[$tag] ) ) {
+                       return Status::newFatal( 'tags-activate-not-found', $tag );
+               }
+
+               // defined tags cannot be activated (a defined tag is either extension-
+               // defined, in which case the extension chooses whether or not to active it;
+               // or user-defined, in which case it is considered active)
+               $definedTags = self::listDefinedTags();
+               if ( in_array( $tag, $definedTags ) ) {
+                       return Status::newFatal( 'tags-activate-not-allowed', $tag );
+               }
+
+               return Status::newGood();
+       }
+
+       /**
+        * Activates a tag, checking whether it is allowed first, and adding a log
+        * entry afterwards.
+        *
+        * Includes a call to ChangeTag::canActivateTag(), so your code doesn't need
+        * to do that.
+        *
+        * @param string $tag
+        * @param string $reason
+        * @param User $user Who to give credit for the action
+        * @param bool $ignoreWarnings Can be used for API interaction, default false
+        * @return Status If successful, the Status contains the ID of the added log
+        * entry as its value
+        * @since 1.25
+        */
+       public static function activateTagWithChecks( $tag, $reason, User $user,
+               $ignoreWarnings = false ) {
+
+               // are we allowed to do this?
+               $result = self::canActivateTag( $tag, $user );
+               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // do it!
+               self::defineTag( $tag );
+
+               // log it
+               $logId = self::logTagManagementAction( 'activate', $tag, $reason, $user );
+               return Status::newGood( $logId );
+       }
+
+       /**
+        * Is it OK to allow the user to deactivate this tag?
+        *
+        * @param string $tag Tag that you are interested in deactivating
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canDeactivateTag( $tag, User $user = null ) {
+               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+                       return Status::newFatal( 'tags-manage-no-permission' );
+               }
+
+               // only explicitly-defined tags can be deactivated
+               $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
+               if ( !in_array( $tag, $explicitlyDefinedTags ) ) {
+                       return Status::newFatal( 'tags-deactivate-not-allowed', $tag );
+               }
+               return Status::newGood();
+       }
+
+       /**
+        * Deactivates a tag, checking whether it is allowed first, and adding a log
+        * entry afterwards.
+        *
+        * Includes a call to ChangeTag::canDeactivateTag(), so your code doesn't need
+        * to do that.
+        *
+        * @param string $tag
+        * @param string $reason
+        * @param User $user Who to give credit for the action
+        * @param bool $ignoreWarnings Can be used for API interaction, default false
+        * @return Status If successful, the Status contains the ID of the added log
+        * entry as its value
+        * @since 1.25
+        */
+       public static function deactivateTagWithChecks( $tag, $reason, User $user,
+               $ignoreWarnings = false ) {
+
+               // are we allowed to do this?
+               $result = self::canDeactivateTag( $tag, $user );
+               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // do it!
+               self::undefineTag( $tag );
+
+               // log it
+               $logId = self::logTagManagementAction( 'deactivate', $tag, $reason, $user );
+               return Status::newGood( $logId );
+       }
+
+       /**
+        * Is it OK to allow the user to create this tag?
+        *
+        * @param string $tag Tag that you are interested in creating
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canCreateTag( $tag, User $user = null ) {
+               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+                       return Status::newFatal( 'tags-manage-no-permission' );
+               }
+
+               // no empty tags
+               if ( $tag === '' ) {
+                       return Status::newFatal( 'tags-create-no-name' );
+               }
+
+               // tags cannot contain commas (used as a delimiter in tag_summary table) or
+               // slashes (would break tag description messages in MediaWiki namespace)
+               if ( strpos( $tag, ',' ) !== false || strpos( $tag, '/' ) !== false ) {
+                       return Status::newFatal( 'tags-create-invalid-chars' );
+               }
+
+               // could the MediaWiki namespace description messages be created?
+               $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Tag-$tag-description" );
+               if ( is_null( $title ) ) {
+                       return Status::newFatal( 'tags-create-invalid-title-chars' );
+               }
+
+               // does the tag already exist?
+               $tagUsage = self::tagUsageStatistics();
+               if ( isset( $tagUsage[$tag] ) ) {
+                       return Status::newFatal( 'tags-create-already-exists', $tag );
+               }
+
+               // check with hooks
+               $canCreateResult = Status::newGood();
+               Hooks::run( 'ChangeTagCanCreate', array( $tag, $user, &$canCreateResult ) );
+               return $canCreateResult;
+       }
+
+       /**
+        * Creates a tag by adding a row to the `valid_tag` table.
+        *
+        * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
+        * do that.
+        *
+        * @param string $tag
+        * @param string $reason
+        * @param User $user Who to give credit for the action
+        * @param bool $ignoreWarnings Can be used for API interaction, default false
+        * @return Status If successful, the Status contains the ID of the added log
+        * entry as its value
+        * @since 1.25
+        */
+       public static function createTagWithChecks( $tag, $reason, User $user,
+               $ignoreWarnings = false ) {
+
+               // are we allowed to do this?
+               $result = self::canCreateTag( $tag, $user );
+               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // do it!
+               self::defineTag( $tag );
+
+               // log it
+               $logId = self::logTagManagementAction( 'create', $tag, $reason, $user );
+               return Status::newGood( $logId );
+       }
+
+       /**
+        * Permanently removes all traces of a tag from the DB. Good for removing
+        * misspelt or temporary tags.
+        *
+        * This function should be directly called by maintenance scripts only, never
+        * by user-facing code. See deleteTagWithChecks() for functionality that can
+        * safely be exposed to users.
+        *
+        * @param string $tag Tag to remove
+        * @return Status The returned status will be good unless a hook changed it
+        * @since 1.25
+        */
+       public static function deleteTagEverywhere( $tag ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->startAtomic( __METHOD__ );
+
+               // delete from valid_tag
+               self::undefineTag( $tag );
+
+               // find out which revisions use this tag, so we can delete from tag_summary
+               $result = $dbw->select( 'change_tag',
+                       array( 'ct_rc_id', 'ct_log_id', 'ct_rev_id', 'ct_tag' ),
+                       array( 'ct_tag' => $tag ),
+                       __METHOD__ );
+               foreach ( $result as $row ) {
+                       // remove the tag from the relevant row of tag_summary
+                       $tagsToAdd = array();
+                       $tagsToRemove = array( $tag );
+                       self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $row->ct_rc_id,
+                               $row->ct_rev_id, $row->ct_log_id );
+               }
+
+               // delete from change_tag
+               $dbw->delete( 'change_tag', array( 'ct_tag' => $tag ), __METHOD__ );
+
+               $dbw->endAtomic( __METHOD__ );
+
+               // give extensions a chance
+               $status = Status::newGood();
+               Hooks::run( 'ChangeTagAfterDelete', array( $tag, &$status ) );
+               // let's not allow error results, as the actual tag deletion succeeded
+               if ( !$status->isOK() ) {
+                       wfDebug( 'ChangeTagAfterDelete error condition downgraded to warning' );
+                       $status->ok = true;
+               }
+
+               // clear the memcache of defined tags
+               self::purgeTagCacheAll();
+
+               return $status;
+       }
+
+       /**
+        * Is it OK to allow the user to delete this tag?
+        *
+        * @param string $tag Tag that you are interested in deleting
+        * @param User|null $user User whose permission you wish to check, or null if
+        * you don't care (e.g. maintenance scripts)
+        * @return Status
+        * @since 1.25
+        */
+       public static function canDeleteTag( $tag, User $user = null ) {
+               $tagUsage = self::tagUsageStatistics();
+
+               if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+                       return Status::newFatal( 'tags-manage-no-permission' );
+               }
+
+               if ( !isset( $tagUsage[$tag] ) ) {
+                       return Status::newFatal( 'tags-delete-not-found', $tag );
+               }
+
+               if ( $tagUsage[$tag] > self::MAX_DELETE_USES ) {
+                       return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
+               }
+
+               $extensionDefined = self::listExtensionDefinedTags();
+               if ( in_array( $tag, $extensionDefined ) ) {
+                       // extension-defined tags can't be deleted unless the extension
+                       // specifically allows it
+                       $status = Status::newFatal( 'tags-delete-not-allowed' );
+               } else {
+                       // user-defined tags are deletable unless otherwise specified
+                       $status = Status::newGood();
+               }
+
+               Hooks::run( 'ChangeTagCanDelete', array( $tag, $user, &$status ) );
+               return $status;
+       }
+
+       /**
+        * Deletes a tag, checking whether it is allowed first, and adding a log entry
+        * afterwards.
+        *
+        * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
+        * do that.
+        *
+        * @param string $tag
+        * @param string $reason
+        * @param User $user Who to give credit for the action
+        * @param bool $ignoreWarnings Can be used for API interaction, default false
+        * @return Status If successful, the Status contains the ID of the added log
+        * entry as its value
+        * @since 1.25
+        */
+       public static function deleteTagWithChecks( $tag, $reason, User $user,
+               $ignoreWarnings = false ) {
+
+               // are we allowed to do this?
+               $result = self::canDeleteTag( $tag, $user );
+               if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+                       $result->value = null;
+                       return $result;
+               }
+
+               // store the tag usage statistics
+               $tagUsage = self::tagUsageStatistics();
+
+               // do it!
+               $deleteResult = self::deleteTagEverywhere( $tag );
+               if ( !$deleteResult->isOK() ) {
+                       return $deleteResult;
+               }
+
+               // log it
+               $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $tagUsage[$tag] );
+               $deleteResult->value = $logId;
+               return $deleteResult;
+       }
+
+       /**
+        * Lists those tags which extensions report as being "active".
+        *
+        * @return array
+        * @since 1.25
+        */
+       public static function listExtensionActivatedTags() {
+               // Caching...
+               global $wgMemc;
+               $key = wfMemcKey( 'active-tags' );
+               $tags = $wgMemc->get( $key );
+               if ( $tags ) {
+                       return $tags;
+               }
+
+               // ask extensions which tags they consider active
+               $extensionActive = array();
+               Hooks::run( 'ChangeTagsListActive', array( &$extensionActive ) );
+
+               // Short-term caching.
+               $wgMemc->set( $key, $extensionActive, 300 );
+               return $extensionActive;
+       }
+
+       /**
+        * Basically lists defined tags which count even if they aren't applied to anything.
+        * It returns a union of the results of listExplicitlyDefinedTags() and
+        * listExtensionDefinedTags().
+        *
+        * @return string[] Array of strings: tags
+        */
+       public static function listDefinedTags() {
+               $tags1 = self::listExplicitlyDefinedTags();
+               $tags2 = self::listExtensionDefinedTags();
+               return array_values( array_unique( array_merge( $tags1, $tags2 ) ) );
+       }
+
+       /**
+        * Lists tags explicitly defined in the `valid_tag` table of the database.
+        * Tags in table 'change_tag' which are not in table 'valid_tag' are not
+        * included.
+        *
+        * Tries memcached first.
+        *
+        * @return string[] Array of strings: tags
+        * @since 1.25
+        */
+       public static function listExplicitlyDefinedTags() {
+               // Caching...
+               global $wgMemc;
+               $key = wfMemcKey( 'valid-tags-db' );
+               $tags = $wgMemc->get( $key );
+               if ( $tags ) {
+                       return $tags;
+               }
+
+               $emptyTags = array();
+
+               // Some DB stuff
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( 'valid_tag', 'vt_tag', array(), __METHOD__ );
+               foreach ( $res as $row ) {
+                       $emptyTags[] = $row->vt_tag;
+               }
+
+               $emptyTags = array_filter( array_unique( $emptyTags ) );
+
+               // Short-term caching.
+               $wgMemc->set( $key, $emptyTags, 300 );
+               return $emptyTags;
+       }
+
+       /**
+        * Lists tags defined by extensions using the ListDefinedTags hook.
+        * Extensions need only define those tags they deem to be in active use.
+        *
+        * Tries memcached first.
+        *
+        * @return string[] Array of strings: tags
+        * @since 1.25
+        */
+       public static function listExtensionDefinedTags() {
+               // Caching...
+               global $wgMemc;
+               $key = wfMemcKey( 'valid-tags-hook' );
+               $tags = $wgMemc->get( $key );
+               if ( $tags ) {
+                       return $tags;
+               }
+
+               $emptyTags = array();
+               Hooks::run( 'ListDefinedTags', array( &$emptyTags ) );
+               $emptyTags = array_filter( array_unique( $emptyTags ) );
+
+               // Short-term caching.
+               $wgMemc->set( $key, $emptyTags, 300 );
+               return $emptyTags;
+       }
+
+       /**
+        * Invalidates the short-term cache of defined tags used by the
+        * list*DefinedTags functions, as well as the tag statistics cache.
+        * @since 1.25
+        */
+       public static function purgeTagCacheAll() {
+               global $wgMemc;
+               $wgMemc->delete( wfMemcKey( 'active-tags' ) );
+               $wgMemc->delete( wfMemcKey( 'valid-tags-db' ) );
+               $wgMemc->delete( wfMemcKey( 'valid-tags-hook' ) );
+               self::purgeTagUsageCache();
+       }
+
+       /**
+        * Invalidates the tag statistics cache only.
+        * @since 1.25
+        */
+       public static function purgeTagUsageCache() {
+               global $wgMemc;
+               $wgMemc->delete( wfMemcKey( 'change-tag-statistics' ) );
+       }
+
+       /**
+        * Returns a map of any tags used on the wiki to number of edits
+        * tagged with them, ordered descending by the hitcount.
+        *
+        * Keeps a short-term cache in memory, so calling this multiple times in the
+        * same request should be fine.
+        *
+        * @return array Array of string => int
+        */
+       public static function tagUsageStatistics() {
+               // Caching...
+               global $wgMemc;
+               $key = wfMemcKey( 'change-tag-statistics' );
+               $stats = $wgMemc->get( $key );
+               if ( $stats ) {
+                       return $stats;
+               }
+
+               $out = array();
+
+               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+               $res = $dbr->select(
+                       'change_tag',
+                       array( 'ct_tag', 'hitcount' => 'count(*)' ),
+                       array(),
+                       __METHOD__,
+                       array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' )
+               );
+
+               foreach ( $res as $row ) {
+                       $out[$row->ct_tag] = $row->hitcount;
+               }
+               foreach ( self::listDefinedTags() as $tag ) {
+                       if ( !isset( $out[$tag] ) ) {
+                               $out[$tag] = 0;
+                       }
+               }
+
+               // Cache for a very short time
+               $wgMemc->set( $key, $out, 300 );
+               return $out;
+       }
+}
index 245022d..bab8eba 100644 (file)
@@ -451,15 +451,14 @@ class SectionProfiler {
        }
 
        /**
-        * Get the initial time of the request, based either on $wgRequestTime or
-        * $wgRUstart. Will return null if not able to find data.
+        * Get the initial time of the request, based on getrusage()
         *
         * @param string|bool $metric Metric to use, with the following possibilities:
         *   - user: User CPU time (without system calls)
         *   - cpu: Total CPU time (user and system calls)
         *   - wall (or any other string): elapsed time
         *   - false (default): will fall back to default metric
-        * @return float|null
+        * @return float
         */
        protected function getTime( $metric = 'wall' ) {
                if ( $metric === 'cpu' || $metric === 'user' ) {
index c1a9593..9a1c5e5 100644 (file)
@@ -265,8 +265,8 @@ class SpecialVersion extends SpecialPage {
                        $version = $wgVersion . ' ' .
                                wfMessage(
                                        'version-svn-revision',
-                                       isset( $info['directory-rev'] ) ? $info['directory-rev'] : '',
-                                       $info['checkout-rev']
+                                       isset( $svnInfo['directory-rev'] ) ? $svnInfo['directory-rev'] : '',
+                                       isset( $svnInfo['checkout-rev'] ) ? $svnInfo['checkout-rev'] : ''
                                )->text();
                }
 
index f1d18b8..77f3ef9 100644 (file)
@@ -22,6 +22,7 @@
        "tog-watchdefault": "हमरा द्वारा निर्मित पृष्ठ आ हमरा द्वारा लादल फ़ाइलन के हमार ध्यानसूची में जोड़ी",
        "tog-watchmoves": "हमरा द्वारा स्थानांतरित पृष्ठ आ लादल फाईल के हमरा ध्यानसूची में जोड़ी",
        "tog-watchdeletion": "हमरा द्वारा मिटावल पृष्ठ फ़ाइलन के हमार ध्यानसूची में जोड़ी",
+       "tog-watchrollback": "हमरा द्वारा प्रत्यापन्न (रोलबैक) करल गइल पन्नन के हमार ध्यानसूची में जोड़ीं।",
        "tog-minordefault": "सारा सम्पादन के छोट सम्पादन के रुप में चिन्हित करीं",
        "tog-previewontop": "सम्पादन बक्सा से पहिले पुर्वदर्शन दिखाईं।",
        "tog-previewonfirst": "पहिलका सम्पादन पर पूर्वावलोकन देखीं",
        "projectpage": "परियोजना पन्ना देखीं",
        "imagepage": "फाइल पन्ना देखीं",
        "mediawikipage": "सन्देश पन्ना देखीं",
-       "templatepage": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f पन्ना देखीं",
+       "templatepage": "à¤\96ाà¤\81à¤\9aा पन्ना देखीं",
        "viewhelppage": "मदद पन्ना देखीं",
        "categorypage": "श्रेणी पन्ना देखीं",
        "viewtalkpage": "बात-चीत देखीं",
        "pool-queuefull": "पूल पंक्ति भर गइल",
        "pool-errorunknown": "अज्ञात त्रुटि",
        "pool-servererror": "पूल काउंटर सेवा उपलब्ध नाही बा ($1)।",
+       "poolcounter-usage-error": "उपयोग त्रुटि: $1",
        "aboutsite": "{{SITENAME}} के बारे में",
        "aboutpage": "Project:बारे में",
        "copyright": "उपलब्ध सामग्री $1 के अधीन उपलब्ध बा जब तक की अलग से उल्लेख ना करल गईल होखे ।",
        "hidetoc": "छुपाईं",
        "collapsible-collapse": "सेकुड़ीं",
        "collapsible-expand": "फैलाईं",
+       "confirmable-confirm": "का {{GENDER:$1|आप}} निश्चित बानी?",
        "confirmable-yes": "जी",
        "confirmable-no": "ना",
        "thisisdeleted": "देखीं या भंडार करीं $1?",
        "nstab-project": "परियोजना पन्ना",
        "nstab-image": "फाइल",
        "nstab-mediawiki": "सन्देश",
-       "nstab-template": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f",
+       "nstab-template": "à¤\96ाà¤\81à¤\9aा",
        "nstab-help": "मदद पन्ना",
        "nstab-category": "श्रेणी",
        "nosuchaction": "अईसन कौनो कार्रवाई नाहि",
        "readonly_lag": "उपमुख्य डाटाबेस सर्वर मुख्य डाटाबेस के बराबर परावर्तित होत समय मुख्य डाटाबेस सर्वर अपने आप लॉक हो गइल।",
        "internalerror": "आन्तरिक त्रुटि",
        "internalerror_info": "आन्तरिक त्रुटि: $1",
+       "internalerror-fatal-exception": "प्रकार के गंभीर अपवाद \"$1\"",
        "filecopyerror": "\"$1\" फ़ाइल के \"$2\" पर प्रतिलिपि ना बन पाईल।",
        "filerenameerror": "\"$1\" फ़ाइल के नाम बदल के \"$2\" नइखे रखल जा सकत।",
        "filedeleteerror": "\"$1\" फ़ाइल के ना हटावल जा सकल।",
        "directorycreateerror": "\"$1\" डाइरेक्टरी ना बनावल जा सकल।",
+       "directoryreadonlyerror": "निर्देशिका \"$1\" सिर्फ पठनीय बा।",
+       "directorynotreadableerror": "निर्देशिका \"$1\" पठनीय नइखे।",
        "filenotfound": "\"$1\" फ़ाइल ना मिलल।",
        "unexpected": "अनपेक्षित मूल्य: \"$1\"=\"$2\".",
        "formerror": "त्रुटि: फ़ॉर्म सबमिट ना करल जा सकल।",
        "viewyourtext": "रउआ इ पन्ना में ''आपन सम्पादन'' के स्रोत देख सकत बानी आ ओकर नकल उतार सकत बानी:",
        "protectedinterface": "इ पन्ना इ विकी के सॉफ़्टवेयर के इंटरफ़ेस पाठ्य के देवेला, आ इ के गलत प्रयोग से बचावे खातिर सुरक्षित कर दिहल गइल बा।\nसभन विकियन खातिर अनुवाद जोड़े या बदले खातिर कृपया मीडियाविकि के क्षेत्रीयकरण प्रकल्प [//translatewiki.net/ translatewiki.net] के प्रयोग करीं।",
        "editinginterface": "<strong>चेतावनी:</strong> आप एगो अइसन पन्ना के बदल बदल रहल बानी जवन सॉफ़्टवेयर के इंटरफ़ेस पाठ प्रदान करेला। इ पृष्ठ के बदले से अन्य सदस्यवन के प्रदर्शित इंटरफ़ेस के शक्लोसूरत में बदलाव आई।",
+       "translateinterface": "सभन विकियन खातिर अनुवाद जोड़े या बदले खातिर मीडियाविकि क्षेत्रीयकरण परियोजना [//translatewiki.net/ translatewiki.net] के प्रयोग करीं।",
        "cascadeprotected": "इ पन्ना पर सम्पादन करे के अधिकार खत्म कर दिहल गइल बा काहे कि इ निम्न में शामिल बा {{PLURAL:$1|पन्ना, जउन|पन्नां, जउन}} \"व्यापक\" विकल्प के चालू कइला के साथ सुरक्षित कर दिहल गइल बा:\n$2",
        "namespaceprotected": "रउआ के '''$1''' नामस्थान के पन्नं में सम्पादन करे के अधिकार नइखे दिहल गइल।",
        "customcssprotected": "रउआ के इ CSS पन्ना के संपादित करे के अनुमति नइखे, काहे कि इ में अन्य सदस्यं के व्यक्तिगत सेटिंग्स समाविष्ट बा।",
        "wrongpassword": "गलत गुप्त-शब्द डलले बानी।\nकृपया फिर से कोशिश करीं।",
        "wrongpasswordempty": "गुप्त-शब्द खाली बा। कृपया फिर से कोशिश करीं।",
        "passwordtooshort": "गुप्त-शब्द कम से कम {{PLURAL:$1|1 अक्षर|$1 अक्षर}} के होवे के चाहीं।",
+       "passwordtoolong": "गुप्त-शब्द {{PLURAL:$1|$1 अक्षर}} से अधिक लमहर नइखे हो सकत।",
        "password-name-match": "राउर गुप्त-शब्द राउर प्रयोगकर्ता नाम से अलग होवे के चाहीं।",
        "password-login-forbidden": "इस सदस्यनाम आ गुप्तशब्द के प्रयोग वर्जित बा।",
        "mailmypassword": "गुप्तशब्द रिसेट करीं",
        "createaccount-text": "राउर ई-मेल पता खातिर {{SITENAME}} ($4) पर \"$2\" सदस्य नाम से \"$3\" गुप्तशब्द (पासवर्ड) सहित खाता खोलले बानी। रउआ खाता में प्रवेश कर के आपन गुप्तशब्द (पासवर्ड) तुरंत बदल लेवे के चाहीं।\n\nयदि इ खाता गलती से खोलल गईल बा, त रउआ इ संदेश के अनदेखा कर सकत बानी।",
        "login-throttled": "रउआ हाले में कईयन बार खाता में प्रवेश करे के कोशिश कर चुकल बानी।\nकृपया $1 प्रतिक्षा करला के बाद फिर से प्रयास करब।",
        "login-abort-generic": "राउर खाता में प्रवेश असफल रहल - निष्फलित",
+       "login-migrated-generic": "आप के खाता माइग्रेट हो चुकल बा अउर आप के सदस्यनाम इ विकी पर अब मौजूद नइखे।",
        "loginlanguagelabel": "भाषा: $1",
        "suspicious-userlogout": "राउर खाता से बाहर जाये के अनुरोध अस्वीकृत कर दिहल गइल बा काहे कि  अइसन लग रहल बा कि इ कउनो खराब ब्राउज़र या कैश करे वाली प्रॉक्सी द्वारा भेजल गईल रहल।",
        "createacct-another-realname-tip": "असली नाम वैकल्पिक बा।\nयदि रउआ इ के उपलब्ध करावे के चुनत बानी त, एकर प्रयोग सदस्य के ओकरा काम के अधिकार देवे खातिर होखी।",
        "changeemail-throttled": "रउआ हाले में कईयन बार खाता में प्रवेश करे के कोशिश कर चुकल बानी।\nकृपया $1 प्रतिक्षा करला के बाद फिर से प्रयास करब।",
        "resettokens": "टोकन रीसेट करीं",
        "resettokens-text": "जौन टोकन राउर खाता से सम्बद्ध कुछ विशिष्ट व्यक्तिगत जानकारी प्रदान करेला, आप उ के अहिजा रीसेट कर सकत बानी।\n\nयदि रउआ ई के गलती से केहू के दिखा देले बानी या फिर राउर खाता हैक हो गईल बा त रउआ ई के रीसेट कर देवे के चाहीं।",
+       "resettokens-no-tokens": "रीसेट करे खातिर कउनो टोकन नइखे।",
+       "resettokens-legend": "टोकन रीसेट करीं",
+       "resettokens-tokens": "टोकन:",
+       "resettokens-token-label": "$1 (वर्तमान मूल्य: $2)",
+       "resettokens-watchlist-token": "[[Special:Watchlist|आपके ध्यानसूची के पन्नन में बदलावसभ]] के वेब फ़ीड (Atom/RSS) हेतु टोकन",
+       "resettokens-done": "टोकन रीसेट हो चुकल बा।",
+       "resettokens-resetbutton": "चुनल गइल टोकन रीसेट करीं",
        "bold_sample": "मोट पाठ्य",
        "bold_tip": "मोट पाठ्य",
        "italic_sample": "इटालिक पाठ्य",
        "preview": "पूर्वावलोकन",
        "showpreview": "पूर्वावलोकन देखाईं",
        "showdiff": "परिवर्तन देखाईं",
+       "blankarticle": "<strong>चेतावनी:</strong> आप एगो खाली पन्ना के बनावे जा रहल बानी।\nयदि आप \"{{int:savearticle}}\" के फेर से दबायेब त पन्ना बिना कउनो सामग्री के बन जाई।",
        "anoneditwarning": "<strong>चेतावनी:</strong> रउआ आपन खाता में प्रवेश नईखीं कईले। यदि रउआ कवनो बदलाव करत बानी त राउर आईपी पता खुलेआम दिखाई दी। यदि रउआ <strong>[$1 लॉग इन]</strong> या <strong>[$2 नया खाता बनायेब]</strong> त, अन्य सुविधावन के साथ राउर सम्पादन के श्रेय राउर सदस्यनाम पर चल जाई।",
        "anonpreviewwarning": "''रउआ खाता में प्रवेश नईखीं भईल। सुरक्षित करेब त ई पन्ना के सम्पादन इतिहास पर राउर आई पी पता दर्ज हो जाई।\"",
        "missingsummary": "'''स्मरणपत्र:'''रउआ एगो सारांश के सम्पादन नईखीं प्रदान कईले। अगर रउआ \"फिर से सुरक्षित करीं\" पर क्लिक करेब, त राउर सम्पादन बिना एगो सारांश के सुरक्षित हो जाई।",
+       "selfredirect": "<strong>चेतावनी:</strong> आप खुद के इ पन्ना पर पुनः निर्देशित कर रहल बानी।\nआप अनुप्रेषित खातिर गलत लक्ष्य निर्दिष्ट हो सकत बानी, या आप के द्वारा गलत पन्ना के संपादन हो सकत बा।\nआप यदि फेर से \"{{int:savearticle}}\" क्लिक करत बानी त, पुन: निर्देषण ओइसहु बनावल जाई।",
        "missingcommenttext": "कृपया निचे एगो टिप्पणी करीं।",
        "missingcommentheader": "'''स्मरणपत्र:''' रउआ ई टिप्पणी खातिर कौनो विषय/शिर्षक प्रदान नईखीं कईले। \"{{int:savearticle}}\" यदि रउआ फिर से सुरक्षित करब त राउर सम्पादन बिना कौनो शिर्षक के सुरक्षित हो जाई।",
        "summary-preview": "सारांश पूर्वावलोकन:",
        "subject-preview": "विषय/शिर्षक पूर्वावलोकन:",
+       "previewerrortext": "जब आप आपन बदलाव के पुर्वावलोकन देखे खातिर प्रयास कइनी ह तवने घड़ी एगो त्रुटी उत्पन्न हो गइल बा।",
        "blockedtitle": "निष्क्रिय प्रयोगकर्ता",
        "blockedtext": "'''राउर सदस्यनाम अथवा आइ॰पी पता अवरोधित कर दिहल गईल बा ।'''\n\nअवरोध $1 द्वारा करल गईल रहल।\nअवरोध के कारण बा ''$2''\n\n* अवरोध के आरंभ: $8\n* अवरोध के समाप्ति: $6\n* अवरोधित इकाई: $7\n\nइ अवरोध के बारे में चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबन्धक]] से संपर्क कर सकत बानी।\nअगर रउआ [[Special:Preferences|आपन वरीयता]] में वैद्य ई-मेल पता प्रविष्ट कइले होखब तबे 'इ प्रयोक्ता के ई-मेल भेजीं' वाला सुविधा के प्रयोग कर सकत बानी अउर रउआ एकर प्रयोग करे से ना रोकल गईल होखे।\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी भी शामिल करब।",
        "autoblockedtext": "राउर आइ॰पी पता अपने आप अवरुद्ध हो गईल बा काहे कि एकर प्रयोग केहु अन्य सदस्य द्वारा होत रहल,\nजे $1 द्वारा अवरोधित करल गईल रहलन। \nअवरोध करे के कारण बा:\n\n:''$2''\n\n* अवरोध प्रारंभ: $8\n* अवरोध समाप्ति: $6\n* अवरोधित सदस्य: $7\n\nअवरोध के चर्चा करे खातिर रउआ $1 या केहु अन्य [[{{MediaWiki:Grouppage-sysop}}|प्रबंधक]] से संपर्क कर सकत बानी।\n\nकृपया ध्यान रहे कि यदि रउआ \"इ सदस्य के ई-मेल भेजीं\" वाला सुविधा के प्रयोग करे के चाहत बानी त राउर [[Special:Preferences|वरीयता]] में वैद्य ई-मेल पता होखे के चाहीं अउर एकर प्रयोग रउआ खातिर अवरोधित ना भईल होखे।\n\nराउर हाल के आइ॰पी पता $3 ह अउर अवरोध क्रमांक #$5 ह।\nआपन कउनो भी प्रश्न में कृपया इ सब जानकारी शामिल करब।",
        "blockednoreason": "कउनो कारण उल्लेखित नईखे",
        "whitelistedittext": "रउआ पन्ना सम्पादन करे खातिर $1 करे के पड़ी।",
+       "confirmedittext": "संपादन करे से पहिले आपके अापना ई-मेल पता प्रमाणित करावल जरुरी बा।\nकृपया आपन [[Special:Preferences|राउर पसन्द]] में जाके अापन ई-मेल पता दिहीं अउर उके प्रमाणित करीं।",
        "nosuchsectiontitle": "खण्ड ना मिल सकल।",
        "loginreqtitle": "खाता में प्रवेश जरुरी बा",
        "loginreqlink": "खाता में प्रवेश",
        "yourtext": "राउर पाठ्य",
        "storedversion": "सुरक्षित करल गईल संशोधन",
        "yourdiff": "अंतर",
+       "templatesused": "इ पन्ना पर प्रयुक्त {{PLURAL:$1|खाँचा|खाँचा कुल}}:",
+       "templatesusedpreview": "इ पुर्वावलोकन में प्रयुक्त {{PLURAL:$1|खाँचा|खाँचा कुल}}:",
        "template-protected": "(संरक्षित)",
        "template-semiprotected": "(अर्ध-सुरक्षित)",
        "nocreate-loggedin": "नया पन्ना बनावे रउआ अधिकार नइखे।",
        "content-model-text": "सामान्य पाठ",
        "content-model-javascript": "जावास्क्रिप्ट",
        "content-model-css": "सी॰एस॰एस",
+       "duplicate-args-category": "टेम्पलेट कॉल में डुप्लिकेट तर्क के उपयोग करते हुए पन्नासभ",
+       "post-expand-template-inclusion-warning": "'''चेतावनी:''' खाँचा जोड़े के सीमा पार हो चुकल बा।\nकुछ खाँचा ना जोड़ल जाई।",
        "post-expand-template-inclusion-category": "अइसन पृष्ठ जे पर साँचा जोडे के सीमा पार हो गइल बा",
        "cantcreateaccounttitle": "खाता खुल नईखे सकत",
        "nohistory": "ए पन्ना के कौनों संपादन इतिहास नइखे",
        "right-move-categorypages": "श्रेणी पन्नवन के स्थानांतरित करीं",
        "right-movefile": "फाइल सब स्थानांतरित करीं",
        "right-suppressredirect": "स्थानांतरण करत घरी मूल पन्ना से पुनर्निदेश मत बनाईं",
-       "right-upload": "फाà¤\87ल à¤²à¤¾à¤¦ीं",
+       "right-upload": "फाà¤\87ल à¤\85पलà¥\8bड à¤\95रीं",
        "right-reupload": "पुरान फाइल की ऊपर नया लादीं",
        "right-reupload-own": "खुदे लादल फाइल पर नया फाइल लादीं",
        "right-delete": "पन्ना हटाईं",
        "action-move-rootuserpages": "मूल सदस्यपन्ना स्थानांतरित करीं",
        "action-move-categorypages": "श्रेणी पन्ना स्थानांतरित करीं",
        "action-movefile": "ई फाइल स्थानांतरित करीं",
-       "action-upload": "à¤\88 à¤«à¤¾à¤\87ल à¤²à¤¾à¤¦ीं",
+       "action-upload": "à¤\87 à¤«à¤¾à¤\87ल à¤\85पलà¥\8bड à¤\95रीं",
        "action-reupload": "पहिले से मौजूद ए फाइल पर दूसर लादीं",
        "action-delete": "ई पन्ना के मिटाईं",
        "recentchanges": "तुरंत भइल परिवर्तन",
        "recentchangeslinked-feed": "सम्बन्धित बदलाव",
        "recentchangeslinked-toolbox": "सम्बन्धित बदलाव",
        "recentchangeslinked-page": "पन्ना नाम:",
-       "upload": "फाà¤\88ल à¤²à¤¾à¤¦ीं",
+       "upload": "फाà¤\88ल à¤\85पलà¥\8bड à¤\95रीं",
        "uploadlogpage": "लदनी (अपलोड) के लॉग",
        "filename": "फाइलनाँव",
        "filedesc": "सारांश",
        "sharedupload": "इ फाईल $1 से बा आ दुसर परियोजना में प्रयोग करल जा सकत बा।",
        "sharedupload-desc-there": "इ फाईल $1 से बा आ दुसर परियोजना में प्रयोग करल जा सकत बा। अधिक जानकारी खातिर कृपया [$2 फाईल विवरण पन्ना] देखीं।",
        "filepage-nofile": "इ नाम से कौनो फाईल उपलब्ध नईखे।",
-       "filepage-nofile-link": "à¤\87 à¤¨à¤¾à¤® à¤¸à¥\87 à¤\95à¥\8cनà¥\8b à¤«à¤¾à¤\88ल à¤\89पलबà¥\8dध à¤¨à¤\88à¤\96à¥\87, à¤²à¥\87à¤\95िन à¤°à¤\89à¤\86 [$1 à¤\95à¥\87 à¤²à¤¾à¤¦] सकत बानी।",
+       "filepage-nofile-link": "à¤\87 à¤¨à¤¾à¤® à¤¸à¥\87 à¤\95à¥\8cनà¥\8b à¤«à¤¾à¤\88ल à¤\89पलबà¥\8dध à¤¨à¤\88à¤\96à¥\87, à¤²à¥\87à¤\95िन à¤°à¤\89à¤\86 [$1 à¤\95à¥\87 à¤\85पलà¥\8bड à¤\95र] सकत बानी।",
        "uploadnewversion-linktext": "इ फाईल के नया संस्करण लादीं।",
        "shared-repo-from": "$1 से",
        "shared-repo": "एगो आवटिंत भंडार गृह",
        "filedelete-submit": "मिटाईं",
        "filedelete-success": "'''$1''' के मिटा दिहल गईल बा।",
        "filedelete-nofile": "'''$1''' उपलब्ध नईखे।",
+       "unusedtemplates": "बिना प्रयोग के खाँचा",
        "randompage": "अविशिष्ट पन्ना",
+       "doubleredirects": "दोहरा पुननिर्देशित पन्ना",
+       "brokenredirects": "टूटल पुनर्निर्देशन पन्ना",
        "nbytes": "$1 {{PLURAL:$1|बाईट|बाईट्स}}",
        "lonelypages": "अनाथ पन्ना",
        "lonelypagestext": "ई पन्ना कुल कौनों दूसर पन्ना से नइखें जुड़ल न कौनों में ट्रांसक्लूड बाड़ें",
        "unusedimages": "बिना इस्तेमाल फाइल",
        "wantedcategories": "श्रेणी चाहत बा",
        "wantedpages": "पन्ना चाहत बा",
+       "wantedtemplates": "जरुरत के खाँचा",
+       "longpages": "लमहर पन्ना",
+       "deadendpages": "मरल-खपल पन्ना",
        "newpages": "नवका पन्ना",
+       "ancientpages": "सभन से पुरान पन्नासभ",
        "move": "स्थान्तरण",
        "movethispage": "ई पन्ना के स्थांतरण करीं",
        "booksources": "किताबी स्रोत",
        "tooltip-t-recentchangeslinked": "ई पन्ना से जुड़ल पन्नवन पर तुरंत भईल परिवर्तन",
        "tooltip-feed-atom": "ई पन्ना खातिर अणु फ़ीड",
        "tooltip-t-contributions": "इ सदस्य के योगदान के सूची",
-       "tooltip-t-upload": "फाà¤\88ल à¤²à¤¾à¤¦à¥\80à¤\82 (à¤\85पलà¥\8bड )",
+       "tooltip-t-upload": "फाà¤\88ल à¤\85पलà¥\8bड à¤\95रà¥\80à¤\82",
        "tooltip-t-specialpages": "ख़ाश पन्नवन के सूची",
        "tooltip-t-print": "ई पन्ना के छापे लायक संस्करण।",
        "tooltip-t-permalink": "ई पन्ना के संसोधन खातिर स्थायी लिंक।",
        "revdelete-restricted": "प्रबंधक पर प्रतिबंध लागू",
        "revdelete-unrestricted": "प्रबंधक पर से प्रतिबंध समाप्त",
        "revdelete-summary": "सारांश संपादन",
-       "searchsuggest-search": "खोजीं"
+       "searchsuggest-search": "खोजीं",
+       "expandtemplates": "फैलल खाँचा"
 }
index bb5d745..4771142 100644 (file)
        "patrol-log-page": "পরীক্ষণ লগ",
        "patrol-log-header": "এটি যাচাইকৃত রিভিশনের তালিকা।",
        "log-show-hide-patrol": "পরীক্ষণ লগ $1",
+       "log-show-hide-tag": "ট্যাগ লগ $1",
        "deletedrevision": "মুছে ফেলা পুরাতন সংশোধন $1",
        "filedeleteerror-short": "ফাইল মুছতে গিয়ে ত্রুটি: $1",
        "filedeleteerror-long": "ফাইলটি মুছার সময় ত্রুটি দেখা দিয়েছে:\n\n$1",
        "tags-deactivate-title": "নিষ্ক্রিয় ট্যাগ",
        "tags-deactivate-reason": "কারণ:",
        "tags-deactivate-submit": "নিষ্ক্রিয়",
+       "tags-edit-title": "ট্যাগ সম্পাদনা করুন",
        "comparepages": "পাতার তুলনা",
        "compare-page1": "পাতা ১",
        "compare-page2": "পাতা ২",
        "logentry-upload-overwrite": "$1 $3-এর একটি নতুন সংস্করণ {{GENDER:$2|আপলোড করেছেন}}",
        "logentry-upload-revert": "$1 $3 {{GENDER:$2|আপলোড করেছেন}}",
        "log-name-managetags": "ট্যাগ ব্যবস্থাপনা লগ",
+       "log-name-tag": "ট্যাগ লগ",
        "rightsnone": "(কিছু নাই)",
        "revdelete-summary": "সম্পাদনা সারাংশ",
        "feedback-adding": "পাতায় প্রতিক্রিয়া যোগ হচ্ছে...",
index 56bbec3..6aece30 100644 (file)
        "history-feed-description": "Historie editací této stránky",
        "history-feed-item-nocomment": "$1 v $2",
        "history-feed-empty": "Požadovaná stránka neexistuje.\nMohla být smazána či přejmenována.\nZkuste [[Special:Search|hledání]].",
+       "history-edit-tags": "Editovat značky vybraných revizí",
        "rev-deleted-comment": "(shrnutí editace odstraněno)",
        "rev-deleted-user": "(uživatelské jméno odstraněno)",
        "rev-deleted-event": "(podrobnosti odstraněny)",
        "right-sendemail": "Odesílání e-mailů ostatním uživatelům",
        "right-passwordreset": "Prohlížení e-mailů pro znovunastavení hesla",
        "right-managechangetags": "Vytváření [[Special:Tags|značek]] a jejich mazání z databáze",
+       "right-applychangetags": "Přidávání [[Special:Tags|značek]] k vlastním změnám",
+       "right-changetags": "Přidávání libovolných [[Special:Tags|značek]] na jednotlivé revize a protokolovací záznamy a jejich odebírání",
        "newuserlogpage": "Kniha nových uživatelů",
        "newuserlogpagetext": "Toto je záznam nově zaregistrovaných uživatelů.",
        "rightslog": "Kniha práv uživatelů",
        "action-editmyprivateinfo": "změnit své soukromé údaje",
        "action-editcontentmodel": "editovat model obsahu stránky",
        "action-managechangetags": "vytvářet a mazat značky z databáze",
+       "action-applychangetags": "přidávat značky k vlastním změnám",
+       "action-changetags": "přidávat libovolné značky na jednotlivé revize a protokolovací záznamy a odebírat je",
        "nchanges": "$1 {{PLURAL:$1|změna|změny|změn}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|od poslední návštěvy}}",
        "enhancedrc-history": "historie",
        "logempty": "Protokol neobsahuje žádný odpovídající záznam.",
        "log-title-wildcard": "Hledat názvy začínající na tento text",
        "showhideselectedlogentries": "Ukázat/skrýt vybrané záznamy",
+       "log-edit-tags": "Editovat značky vybraných protokolovacích záznamů",
        "allpages": "Všechny stránky",
        "nextpage": "Další stránka ($1)",
        "prevpage": "Předchozí stránka ($1)",
        "patrol-log-page": "Kniha prověřených editací",
        "patrol-log-header": "Toto je kniha prověřených verzí.",
        "log-show-hide-patrol": "$1 knihu záznamů patroly",
+       "log-show-hide-tag": "$1 knihu značek",
        "deletedrevision": "Smazána stará revize $1",
        "filedeleteerror-short": "Chyba při mazání souboru: $1",
        "filedeleteerror-long": "Vyskytla se chyba při mazání souboru:\n\n$1",
        "tags-deactivate-reason": "Důvod:",
        "tags-deactivate-not-allowed": "Značku „$1“ nelze deaktivovat.",
        "tags-deactivate-submit": "Deaktivovat",
+       "tags-apply-no-permission": "Nemáte oprávnění přidávat značky k vlastním změnám",
+       "tags-apply-not-allowed-one": "Značku „$1“ není dovoleno ručně přidávat.",
+       "tags-apply-not-allowed-multi": "Následující {{PLURAL:$2|značku|značky}} není dovoleno ručně přidávat: $1",
+       "tags-update-no-permission": "Nemáte oprávnění přidávat libovolné značky na jednotlivé revize a protokolovací záznamy a odebírat je",
+       "tags-update-add-not-allowed-one": "Značku „$1“ není dovoleno ručně přidávat.",
+       "tags-update-add-not-allowed-multi": "Následující {{PLURAL:$2|značku|značky}} není dovoleno ručně přidávat: $1",
+       "tags-update-remove-not-allowed-one": "Značku „$1“ není dovoleno odebírat.",
+       "tags-update-remove-not-allowed-multi": "Následující {{PLURAL:$2|značku|značky}} není dovoleno ručně odebírat: $1",
+       "tags-edit-title": "Editace značek",
+       "tags-edit-manage-link": "Spravovat značky",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Vybraná|Vybrané}} revize stránky [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Vybraný protokolovací záznam|Vybrané protokolovací záznamy}}:",
+       "tags-edit-revision-legend": "Změna značek u {{PLURAL:$1|této revize|těchto $1 revizí}}",
+       "tags-edit-logentry-legend": "Změna značek u {{PLURAL:$1|tohoto protokolovacího záznamu|těchto $1 protokolovacích záznamů}}",
+       "tags-edit-existing-tags": "Stávající značky:",
+       "tags-edit-existing-tags-none": "''Žádná''",
+       "tags-edit-new-tags": "Nové značky:",
+       "tags-edit-add": "Přidat tyto značky:",
+       "tags-edit-remove": "Odebrat tyto značky:",
+       "tags-edit-remove-all-tags": "(odebrat všechny značky)",
+       "tags-edit-chosen-placeholder": "Vyberte nějaké značky",
+       "tags-edit-chosen-no-results": "Nenalezeny žádné značky odpovídající",
+       "tags-edit-reason": "Důvod:",
+       "tags-edit-revision-submit": "Aplikovat změny na {{PLURAL:$1|tuto revizi|tyto $1 revize|těchto $1 revizí}}",
+       "tags-edit-logentry-submit": "Aplikovat změny na {{PLURAL:$1|tento protokolovací záznam|tyto $1 protokolovací záznamy|těchto $1 protokolovacích záznamů}}",
+       "tags-edit-success": "<strong>Změny byly úspěšně aplikovány.</strong>",
+       "tags-edit-failure": "<strong>Změny se nepodařilo provést:</strong>\n$1",
+       "tags-edit-nooldid-title": "Neplatná cílová revize",
+       "tags-edit-nooldid-text": "Buď jste nezadali žádnou cílovou revizi, na kterou by se tato funkce měla použít, nebo uvedená revize neexistuje.",
+       "tags-edit-none-selected": "Vyberte prosím nejméně jednu značku, kterou chcete přidat či odebrat.",
        "comparepages": "Porovnání stránek",
        "compare-page1": "Stránka 1",
        "compare-page2": "Stránka 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|smazal|smazala}} značku „$4“ (odstraněna z $5 {{PLURAL:$5|revize nebo protokolovacího záznamu|revizí nebo protokolovacích záznamů}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktivoval|aktivovala}} značku „$4“ pro uživatele a boty",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|deaktivoval|deaktivovala}} značku „$4“ pro uživatele a boty",
+       "log-name-tag": "Kniha značek",
+       "log-description-tag": "Tato stránka zobrazuje přidání či odebrání [[Special:Tags|značek]] na stránkách či protokolovacích záznamech uživateli. Tato kniha nezaznamenává označování probíhající jako součást editace, smazání či podobné akce.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|přidal|přidala}} {{PLURAL:$7|značku|značky}} $6 na revizi $4 stránky $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|přidal|přidala}} {{PLURAL:$7|značku|značky}} $6 na protokolovací záznam $5 k stránce $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|odebral|odebrala}} {{PLURAL:$9|značku|značky}} $8 z revize $4 stránky $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|odebral|odebrala}} {{PLURAL:$9|značku|značky}} $8 z protokolovacího záznamu $5 k stránce $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|změnil|změnila}} značky na revizi $4 stránky $3 ({{PLURAL:$7|přidáno}} $6; {{PLURAL:$9|odebráno}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|změnil|změnila}} značky na protokolovacím záznamu $5 k stránce $3 ({{PLURAL:$7|přidáno}} $6; {{PLURAL:$9|odebráno}} $8)",
        "rightsnone": "(žádné)",
        "revdelete-summary": "shrnutí editace",
        "feedback-adding": "Komentář se přidává na stránku…",
index 5f1e17c..864a785 100644 (file)
        "shortpages": "Кĕске статьясем",
        "longpages": "Вăрăм страницăсем",
        "deadendpages": "Ниăçта та урăх ертмен страницăсем",
-       "protectedpages": "хӳтĕленĕ страницăсем",
+       "protectedpages": "Хӳтĕленĕ страницăсем",
        "protectedtitles": "Юраман ятсем",
        "listusers": "Хутшăнакансен списокĕ",
        "newpages": "Çĕнĕ страницăсем",
index bb0340b..c46c447 100644 (file)
        "history-feed-description": "Versionsgeschichte dieser Seite in {{SITENAME}}",
        "history-feed-item-nocomment": "$1 am $3 um $4 Uhr",
        "history-feed-empty": "Die angeforderte Seite existiert nicht. Vielleicht wurde sie gelöscht oder verschoben. [[Special:Search|Durchsuche]] {{SITENAME}} nach passenden neuen Seiten.",
+       "history-edit-tags": "Markierungen ausgewählter Versionen bearbeiten",
        "rev-deleted-comment": "(Zusammenfassung entfernt)",
        "rev-deleted-user": "(Benutzername entfernt)",
        "rev-deleted-event": "(Logbucheinzelheiten entfernt)",
        "right-sendemail": "E-Mails an andere Benutzer senden",
        "right-passwordreset": "Passwort eines Benutzers zurücksetzen und die dazu verschickte E-Mail einsehen",
        "right-managechangetags": "[[Special:Tags|Markierungen]] erstellen und aus der Datenbank löschen",
+       "right-applychangetags": "[[Special:Tags|Markierungen]] zusammen mit den Änderungen anwenden",
+       "right-changetags": "Beliebige [[Special:Tags|Markierungen]] zu einzelnen Versionen und Logbucheinträgen hinzufügen und entfernen",
        "newuserlogpage": "Neuanmeldungs-Logbuch",
        "newuserlogpagetext": "Dies ist ein Logbuch der neu erstellten Benutzerkonten.",
        "rightslog": "Rechte-Logbuch",
        "action-editmyprivateinfo": "deine privaten Informationen zu bearbeiten",
        "action-editcontentmodel": "das Inhaltsmodell einer Seite zu bearbeiten",
        "action-managechangetags": "Markierungen zu erstellen und aus der Datenbank zu löschen",
+       "action-applychangetags": "Markierungen zusammen mit deinen Änderungen anzuwenden",
+       "action-changetags": "beliebige Markierungen zu einzelnen Versionen und Logbucheinträgen hinzuzufügen und zu entfernen",
        "nchanges": "$1 {{PLURAL:$1|Änderung|Änderungen}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|seit dem letzten Besuch}}",
        "enhancedrc-history": "Versionsgeschichte",
        "logempty": "Keine passenden Einträge.",
        "log-title-wildcard": "Titel beginnt mit …",
        "showhideselectedlogentries": "Ausgewählte Logbucheinträge anzeigen/verstecken",
+       "log-edit-tags": "Markierungen ausgewählter Logbucheinträge bearbeiten",
        "allpages": "Alle Seiten",
        "nextpage": "Nächste Seite ($1)",
        "prevpage": "Vorherige Seite ($1)",
        "patrol-log-page": "Kontroll-Logbuch",
        "patrol-log-header": "Dies ist das Kontroll-Logbuch.",
        "log-show-hide-patrol": "Kontroll-Logbuch $1",
+       "log-show-hide-tag": "Markierungs-Logbuch $1",
        "deletedrevision": "alte Version $1 gelöscht",
        "filedeleteerror-short": "Fehler bei Datei-Löschung: $1",
        "filedeleteerror-long": "Bei der Datei-Löschung wurden Fehler festgestellt:\n\n$1",
        "tags-deactivate-reason": "Grund:",
        "tags-deactivate-not-allowed": "Es ist nicht möglich, die Markierung „$1“ zu deaktivieren.",
        "tags-deactivate-submit": "Deaktivieren",
+       "tags-apply-no-permission": "Du hast keine Berechtigung, um Änderungsmarkierungen zusammen mit deinen Änderungen anzuwenden.",
+       "tags-apply-not-allowed-one": "Die Markierung „$1“ darf nicht manuell angewendet werden.",
+       "tags-apply-not-allowed-multi": "Die {{PLURAL:$2|folgende Markierung darf|folgenden Markierungen dürfen}} nicht manuell angewendet werden: $1",
+       "tags-update-no-permission": "Du hast keine Berechtigung, um Änderungsmarkierungen von einzelnen Versionen oder Logbucheinträgen hinzuzufügen oder zu entfernen.",
+       "tags-update-add-not-allowed-one": "Die Markierung „$1“ darf nicht manuell hinzugefügt werden.",
+       "tags-update-add-not-allowed-multi": "Die {{PLURAL:$2|folgende Markierung darf|folgenden Markierungen dürfen}} nicht manuell hinzugefügt werden: $1",
+       "tags-update-remove-not-allowed-one": "Die Markierung „$1“ darf nicht entfernt werden.",
+       "tags-update-remove-not-allowed-multi": "Die {{PLURAL:$2|folgende Markierung darf|folgenden Markierungen dürfen}} nicht manuell entfernt werden: $1",
+       "tags-edit-title": "Markierungen bearbeiten",
+       "tags-edit-manage-link": "Markierungen verwalten",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Ausgewählte Version|Ausgewählte Versionen}} von [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Ausgewähltes Logbuchereignis|Ausgewählte Logbuchereignisse}}:",
+       "tags-edit-revision-legend": "Markierungen von {{PLURAL:$1|dieser Version|allen $1 Versionen}} hinzufügen oder entfernen",
+       "tags-edit-logentry-legend": "Markierungen von {{PLURAL:$1|diesem Logbucheintrag|allen $1 Logbucheinträgen}} hinzufügen oder entfernen",
+       "tags-edit-existing-tags": "Vorhandene Markierungen:",
+       "tags-edit-existing-tags-none": "''Keine''",
+       "tags-edit-new-tags": "Neue Markierungen:",
+       "tags-edit-add": "Diese Markierungen hinzufügen:",
+       "tags-edit-remove": "Diese Markierungen entfernen:",
+       "tags-edit-remove-all-tags": "(alle Markierungen entfernen)",
+       "tags-edit-chosen-placeholder": "Einige Markierungen auswählen",
+       "tags-edit-chosen-no-results": "Keine übereinstimmenden Markierungen gefunden",
+       "tags-edit-reason": "Grund:",
+       "tags-edit-revision-submit": "Änderungen an {{PLURAL:$1|diese Version|$1 Versionen}} anwenden",
+       "tags-edit-logentry-submit": "Änderungen an {{PLURAL:$1|diesen Logbucheintrag|$1 Logbucheinträgen}} anwenden",
+       "tags-edit-success": "<strong>Die Änderungen wurden erfolgreich angewandt.</strong>",
+       "tags-edit-failure": "<strong>Die Änderungen konnten nicht angewandt werden:</strong>\n$1",
+       "tags-edit-nooldid-title": "Ungültige Zielversion",
+       "tags-edit-nooldid-text": "Du hast entweder keine Zielversion angegeben, für die diese Funktion ausgeführt werden soll oder die angegebene Version ist nicht vorhanden.",
+       "tags-edit-none-selected": "Bitte wähle mindestens eine hinzuzufügende oder zu entfernende Markierung aus.",
        "comparepages": "Seiten vergleichen",
        "compare-page1": "Seite 1",
        "compare-page2": "Seite 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|löschte}} die Markierung „$4“ (entfernt von {{PLURAL:$5|einer Version oder einem Logbucheintrag|$5 Versionen und/oder Logbucheinträgen}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktivierte}} die Markierung „$4“ zur Verwendung durch Benutzer und Bots",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|deaktivierte}} die Markierung „$4“ zur Verwendung durch Benutzer und Bots",
+       "log-name-tag": "Markierungs-Logbuch",
+       "log-description-tag": "Diese Seite wird angezeigt, wenn Benutzer [[Special:Tags|Markierungen]] von einzelnen Versionen oder Logbucheinträgen hinzugefügt oder entfernt haben. Das Logbuch listet keine Markierungsaktionen auf, die als Teil einer Bearbeitung, Löschung oder einer ähnlichen Aktion auftreten.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|fügte}} die {{PLURAL:$7|Markierung|Markierungen}} $6 zur Version $4 der Seite $3 hinzu",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|fügte}} die {{PLURAL:$7|Markierung|Markierungen}} $6 zum Logbucheintrag $5 der Seite $3 hinzu",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|entfernte}} die {{PLURAL:$9|Markierung|Markierungen}} $8 von der Version $4 der Seite $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|entfernte}} die {{PLURAL:$9|Markierung|Markierungen}} $8 vom Logbucheintrag $5 der Seite $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|aktualisierte}} Markierungen der Version $4 der Seite $3 ($6 {{PLURAL:$7|hinzugefügt}}; $8 {{PLURAL:$9|entfernt}})",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|aktualisierte}} Markierungen des Logbucheintrags $5 der Seite $3 ($6 {{PLURAL:$7|hinzugefügt}}; $8 {{PLURAL:$9|entfernt}})",
        "rightsnone": "(–)",
        "revdelete-summary": "Zusammenfassungskommentar",
        "feedback-adding": "Rückmeldung wird zur Seite hinzugefügt …",
index 9f2744c..aaca6ef 100644 (file)
        "history-feed-description": "Historial de revisiones para esta página en el wiki",
        "history-feed-item-nocomment": "$1 en $2",
        "history-feed-empty": "La página solicitada no existe.\nPuede que haya sido renombrada o borrada del wiki.\nPrueba a [[Special:Search|buscar en el wiki]] otras páginas relacionadas.",
+       "history-edit-tags": "Editar etiquetas de revisiones seleccionadas",
        "rev-deleted-comment": "(resumen de edición eliminado)",
        "rev-deleted-user": "(nombre de usuario eliminado)",
        "rev-deleted-event": "(detalles del registro eliminados)",
        "right-sendemail": "Enviar correo electrónico a otros usuarios",
        "right-passwordreset": "Ver los mensajes de restablecimiento de contraseña",
        "right-managechangetags": "Crear y eliminar [[Special:Tags|etiquetas]] en la base de datos",
+       "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] junto con los cambios propios",
+       "right-changetags": "Agregar y quitar [[Special:Tags|etiquetas]] arbitrarias a revisiones individuales y entradas del registro",
        "newuserlogpage": "Registro de creación de usuarios",
        "newuserlogpagetext": "Este es un registro de creación de usuarios.",
        "rightslog": "Registro de permisos de usuario",
        "action-editmyprivateinfo": "editar tu información privada",
        "action-editcontentmodel": "editar el modelo de contenido de una página",
        "action-managechangetags": "crear y eliminar etiquetas en la base de datos",
+       "action-applychangetags": "aplicar etiquetas junto con los cambios",
+       "action-changetags": "agregar y quitar etiquetas arbitrarias a revisiones individuales y entradas del registro",
        "nchanges": "$1 {{PLURAL:$1|cambio|cambios}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde la última visita}}",
        "enhancedrc-history": "historial",
        "logempty": "No hay elementos en el registro con esas condiciones.",
        "log-title-wildcard": "Buscar títulos que empiecen con este texto",
        "showhideselectedlogentries": "Mostrar u ocultar las entradas seleccionadas del registro",
+       "log-edit-tags": "Editar las etiquetas de las entradas del registro seleccionadas",
        "allpages": "Todas las páginas",
        "nextpage": "Siguiente página ($1)",
        "prevpage": "Página anterior ($1)",
        "patrol-log-page": "Registro de revisiones",
        "patrol-log-header": "Este es un registro de revisiones patrulladas.",
        "log-show-hide-patrol": "$1 registro de patrullaje",
+       "log-show-hide-tag": "$1 registro de etiquetas",
        "deletedrevision": "Borrada revisión antigua $1",
        "filedeleteerror-short": "Error al borrar el archivo: $1",
        "filedeleteerror-long": "Se han producido errores mientras se borraba el archivo:\n\n$1",
        "tags-deactivate-reason": "Motivo:",
        "tags-deactivate-not-allowed": "No es posible desactivar la etiqueta «$1».",
        "tags-deactivate-submit": "Desactivar",
+       "tags-apply-no-permission": "No tienes permiso para aplicar etiquetas de cambios, junto con tus cambios.",
+       "tags-apply-not-allowed-one": "La etiqueta \"$1\" no se puede aplicar manualmente.",
+       "tags-apply-not-allowed-multi": "{{PLURAL:$2|La siguiente etiqueta no se puede|Las siguientes etiquetas no se pueden}} aplicar manualmente: $1",
+       "tags-update-no-permission": "No tienes permiso para agregar o quitar etiquetas de cambio de las revisiones individuales o las entradas del registro.",
+       "tags-update-add-not-allowed-one": "La etiqueta \"$1\" no se puede agregar manualmente.",
+       "tags-update-add-not-allowed-multi": "{{PLURAL:$2|La siguiente etiqueta no se puede|Las siguientes etiquetas no se pueden}} agregar manualmente: $1",
+       "tags-update-remove-not-allowed-one": "La etiqueta \"$1\" no se puede eliminar.",
+       "tags-update-remove-not-allowed-multi": "{{PLURAL:$2|La siguiente etiqueta no se puede|Las siguientes etiquetas no se pueden}} eliminar manualmente: $1",
+       "tags-edit-title": "Editar etiquetas",
+       "tags-edit-manage-link": "Administrar etiquetas",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Revisión seleccionada|Revisiones seleccionadas}} de [[:$2]]:",
+       "tags-edit-revision-legend": "Agregar o quitar etiquetas de {{PLURAL:$1|esta revisión|todas las $1 revisiones}}",
+       "tags-edit-logentry-legend": "Agregar o quitar etiquetas de {{PLURAL:$1|esta entrada del registro|todas las $1 entradas del registro}}",
+       "tags-edit-existing-tags": "Etiquetas existentes:",
+       "tags-edit-existing-tags-none": "''Ninguna''",
+       "tags-edit-new-tags": "Etiquetas nuevas:",
+       "tags-edit-add": "Agregar estas etiquetas:",
+       "tags-edit-remove": "Eliminar estas etiquetas:",
+       "tags-edit-remove-all-tags": "(eliminar todas las etiquetas)",
+       "tags-edit-chosen-placeholder": "Selecciona algunas etiquetas",
+       "tags-edit-chosen-no-results": "No se encontraron etiquetas que coincidan",
+       "tags-edit-reason": "Motivo:",
+       "tags-edit-revision-submit": "Aplicar los cambios a {{PLURAL:$1|esta revisión|$1 revisiones}}",
+       "tags-edit-logentry-submit": "Aplicar los cambios a {{PLURAL:$1|esta entrada del registro|$1 entradas del registro}}",
+       "tags-edit-success": "<strong>Los cambios se aplicaron con éxito.</strong>",
+       "tags-edit-failure": "<strong>no se pudieron aplicar los cambios:</strong>\n$1",
+       "tags-edit-none-selected": "Selecciona al menos una etiqueta que añadir o quitar.",
        "comparepages": "Comparar páginas",
        "compare-page1": "Página 1",
        "compare-page2": "Página 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|eliminó}} la etiqueta «$4» (quitada de $5 {{PLURAL:$5|revisión o entrada de registro|revisiones o entradas de registro}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|activó}} la etiqueta «$4» para su uso por usuarios y bots",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|desactivó}} la etiqueta «$4» para evitar su uso por usuarios y bots",
+       "log-name-tag": "Registro de etiquetas",
+       "log-description-tag": "Esta página muestra cuando los usuarios han añadido o eliminado [[Special:Tags|etiquetas]] de revisiones individuales o entradas del registro. El registro no muestra las acciones de etiquetado cuando se producen como parte de una edición, eliminación o acciones similares.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|agregó}} {{PLURAL:$7|la etiqueta|las etiquetas}} $6 a la revisión $4 de la página $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|agregó}} {{PLURAL:$7|la etiqueta|las etiquetas}} $6 a la entrada del registro $5 de la página $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|eliminó}} {{PLURAL:$9|la etiqueta|las etiquetas}} $8 de la revisión $4 de la página $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|eliminó}} {{PLURAL:$9|la etiqueta|las etiquetas}} $8 de la entrada del registro $5 de la página $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|actualizó}} etiquetas de la revisión $4 de la página $3 ({{PLURAL:$7|agregó}} $6; {{PLURAL:$9|eliminó}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|actualizó}} etiquetas de la entrada del registro $5 de la página $3 ({{PLURAL:$7|agregó}} $6; {{PLURAL:$9|eliminó}} $8)",
        "rightsnone": "(ninguno)",
        "revdelete-summary": "resumen de edición",
        "feedback-adding": "Añadiendo comentarios a la página...",
index af51a24..21da054 100644 (file)
        "history-feed-description": "Selle lehekülje redigeerimiste ajalugu",
        "history-feed-item-nocomment": "$1 – $2",
        "history-feed-empty": "Soovitud lehekülge ei ole olemas.\nSee võib olla vikist kustutatud või ümber nimetatud.\nÜrita [[Special:Search|vikist otsida]] teemakohaseid lehekülgi.",
+       "history-edit-tags": "Muuda valitud redaktsioonide märgiseid",
        "rev-deleted-comment": "(muudatuse resümee eemaldatud)",
        "rev-deleted-user": "(kasutajanimi eemaldatud)",
        "rev-deleted-event": "(logi üksikasjad eemaldatud)",
        "logdelete-success": "'''Logi nähtavus edukalt muudetud.'''",
        "logdelete-failure": "'''Logi nähtavust ei saanud paika:'''\n$1",
        "revdel-restore": "muuda nähtavust",
-       "pagehist": "Lehekülje ajalugu",
-       "deletedhist": "Kustutatud ajalugu",
+       "pagehist": "lehekülje ajalugu",
+       "deletedhist": "kustutatud ajalugu",
        "revdelete-hide-current": "Tõrge üksuse kuupäevaga $2, kell $1 peitmisel: see on praegune redaktsioon.\nSeda ei saa peita.",
        "revdelete-show-no-access": "Tõrge ajatempliga $1 kell $2 üksuse näitamisel: selle on märge \"piiranguga\".\nSul ei ole sellele ligipääsu.",
        "revdelete-modify-no-access": "Tõrge üksuse kuupäeva $2, kell $1 muutmisel: see on märgitud kui \"piiranguga\".\nSul ei ole sellele ligipääsu.",
        "right-sendemail": "Saata teistele kasutajatele e-kirju",
        "right-passwordreset": "Vaadata parooli lähtestamise e-kirju",
        "right-managechangetags": "Koostada [[Special:Tags|märgiseid]] ja kustutada neid andmebaasist",
+       "right-applychangetags": "Rakendada [[Special:Tags|märgiseid]] enda muudatuste suhtes",
+       "right-changetags": "Lisada ja eemaldada käsitsi rakendatavaid [[Special:Tags|märgiseid]] üksikute redaktsioonide ja logisissekannete juures",
        "newuserlogpage": "Konto loomise logi",
        "newuserlogpagetext": "Siin on logitud kasutajate registreerimine.",
        "rightslog": "Kasutajaõiguste logi",
        "action-editmyprivateinfo": "oma eraandmeid redigeerida",
        "action-editcontentmodel": "lehekülje sisumudelit muuta",
        "action-managechangetags": "märgiseid koostada ege neid andmebaasist kustutada",
+       "action-applychangetags": "rakendada märgiseid oma muudatuste suhtes",
+       "action-changetags": "käsitsi rakendatavaid märgiseid üksikute redaktsioonide ega logisissekannete juures lisada ega eemaldada",
        "nchanges": "$1 {{PLURAL:$1|muudatus|muudatust}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|viimase vaatamise järel}}",
        "enhancedrc-history": "ajalugu",
        "logempty": "Logis puuduvad vastavad kirjed.",
        "log-title-wildcard": "Selle tekstiga algavad pealkirjad",
        "showhideselectedlogentries": "Muuda valitud logisissekannete nähtavust",
+       "log-edit-tags": "Muuda valitud logisissekannete märgiseid",
        "allpages": "Kõik leheküljed",
        "nextpage": "Järgmine lehekülg ($1)",
        "prevpage": "Eelmine lehekülg ($1)",
        "tags-deactivate-reason": "Põhjus:",
        "tags-deactivate-not-allowed": "Märgist \"$1\" pole võimalik keelata.",
        "tags-deactivate-submit": "Keela",
+       "tags-edit-title": "Märgiste muutmine",
+       "tags-edit-manage-link": "halda märgiseid",
+       "tags-edit-revision-selected": "Lehekülje \"[[:$2]]\" valitud {{PLURAL:$1|redaktsioon|redaktsioonid}}:",
+       "tags-edit-logentry-selected": "Valitud {{PLURAL:$1|logisündmus|logisündmused}}:",
+       "tags-edit-revision-legend": "Märgiste lisamine või nende eemaldamine {{PLURAL:$1|sellelt|kõigilt $1}} redaktsioonilt",
+       "tags-edit-logentry-legend": "Märgiste lisamine või nende eemaldamine {{PLURAL:$1|sellelt|kõigilt $1}} logisissekandelt",
+       "tags-edit-add": "Lisa need märgised:",
+       "tags-edit-remove": "Eemalda need märgised:",
+       "tags-edit-remove-all-tags": "(kõik märgised)",
+       "tags-edit-chosen-placeholder": "Vali mõni märgis",
+       "tags-edit-reason": "Põhjus:",
+       "tags-edit-revision-submit": "Rakenda {{PLURAL:$1|selle|$1}} redaktsiooni suhtes",
+       "tags-edit-logentry-submit": "Rakenda {{PLURAL:$1|selle|$1}} logisissekande suhtes",
+       "tags-edit-nooldid-title": "Vigane sihtredaktsioon",
+       "tags-edit-nooldid-text": "Selle toimingu jaoks pole määratud ühtegi sihtredaktsiooni või määratud redaktsiooni pole olemas.",
        "comparepages": "Lehekülgede kõrvutamine",
        "compare-page1": "Lehekülg 1",
        "compare-page2": "Lehekülg 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|kustutas}} märgise \"$4\" (eemaldatud {{PLURAL:$5|ühe redaktsiooni või|$5 redaktsiooni ja/või}} logisissekande juurest)",
        "logentry-managetags-activate": "$1 {{GENDER:$2|lubas}} märgise \"$4\" kasutamise kasutajate ja robotite jaoks",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|keelas}} märgise \"$4\" kasutamise kasutajate ja robotite jaoks",
+       "log-name-tag": "Märgiste logi",
+       "log-description-tag": "Sellel leheküljel on toodud [[Special:Tags|märgiste]] lisamine ja nende eemaldamine üksikute redaktsioonide ja logisissekannete juurest. Logis ei kajastu redigeerimise, kustutamise või sarnase toiminguga kaasnenud märgistused.",
        "rightsnone": "(puudub)",
        "revdelete-summary": "resümee",
        "feedback-adding": "Tagasiside lisamine leheküljele...",
index 3bd4085..98f3bf2 100644 (file)
        "blocklogpage": "Blokeo erregistroa",
        "blocklog-showlog": "Lankide hau blokeatua izan da lehenago ere.\nHona hemen blokeoen erregistroa, erreferentzia gisa:",
        "blocklog-showsuppresslog": "Lankide hau blokeatua eta ezkutatua izan da lehenago ere. Hona hemen erregistroa, erreferentzia gisa:",
-       "blocklogentry": "wikilariak [[$1]] erabiltzailea blokeatu du. Blokeoaldia: $2 $3",
+       "blocklogentry": "administratzaileak [[$1]] blokeatu du. Iraupena: $2 $3",
        "reblock-logentry": "[[$1]] wikilariari blokeoaldia aldatu diogu. Blokeoaldi berria: $2 $3",
        "blocklogtext": "Erabiltzaileen blokeoen ezarpen eta ezabaketen erregistroa da hau. \nAutomatikoki blokeatutako IP helbideak ez dira zerrendatzen. \nIkus [[Special:BlockList|blokeoen zerrenda]] aktibo dauden blokeoak eta debekuak aztertzeko.",
        "unblocklogentry": "$1 desblokeatu da",
        "revdelete-uname-unhid": "lankide ezkutua erakutsi",
        "revdelete-restricted": "administratzaileentzako mugak ezarri dira",
        "revdelete-unrestricted": "administratzaileentzako mugak kendu dira",
+       "logentry-block-block": "administratzaileak $1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} blokeatu du. Iraupena: $5 $6",
+       "logentry-block-reblock": "$1 administratzaileak {{GENDER:$4|$3}} wikilariaren blokeoa {{GENDER:$2|aldatu du}}. Blokeoaldia: $5 $6",
+       "logentry-suppress-block": "administratzaileak $1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} blokeatu du. Iraupena: $5 $6",
+       "logentry-suppress-reblock": "$1 administratzaileak {{GENDER:$4|$3}} wikilariaren blokeoa {{GENDER:$2|aldatu du}}. Blokeoaldia: $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketarik utzi gabe",
        "logentry-move-move_redir": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik",
index 16ecd3e..56be236 100644 (file)
@@ -89,7 +89,7 @@
        "underline-always": "Aina",
        "underline-never": "Ei koskaan",
        "underline-default": "Ulkoasun tai selaimen oletustapa",
-       "editfont-style": "Muokkauskentän kirjasintyyppi",
+       "editfont-style": "Muokkauskentän kirjasintyyppi:",
        "editfont-default": "Selaimen vakioasetus",
        "editfont-monospace": "Tasalevyinen kirjasin",
        "editfont-sansserif": "Sans-serif-kirjasin",
        "categorypage": "Näytä luokkasivu",
        "viewtalkpage": "Näytä keskustelusivu",
        "otherlanguages": "Muilla kielillä",
-       "redirectedfrom": "Ohjattu sivulta $1",
+       "redirectedfrom": "(Ohjattu sivulta $1)",
        "redirectpagesub": "Ohjaussivu",
        "redirectto": "Ohjaus sivulle:",
        "lastmodifiedat": "Sivua on viimeksi muutettu $1 kello $2.",
        "sort-ascending": "Lajittele nousevassa järjestyksessä",
        "nstab-main": "Sivu",
        "nstab-user": "Käyttäjäsivu",
-       "nstab-media": "Media",
+       "nstab-media": "Mediasivu",
        "nstab-special": "Toimintosivu",
        "nstab-project": "Projektisivu",
        "nstab-image": "Tiedosto",
        "nstab-mediawiki": "Järjestelmäviesti",
        "nstab-template": "Malline",
-       "nstab-help": "Ohje",
+       "nstab-help": "Ohjesivu",
        "nstab-category": "Luokka",
-       "nosuchaction": "Määrittelemätön pyyntö",
-       "nosuchactiontext": "Ohjelmisto ei tunnista URL:ssä määriteltyä pyyntöä.\nOlet saattanut kirjoittaa väärin, tai seurannut virheellistä linkkiä.\nTämä voi myös mahdollisesti olla ohjelmistovirhe.",
+       "nosuchaction": "Toimintoa ei ole olemassa",
+       "nosuchactiontext": "URL:ssä määritelty toiminto ei ole kelvollinen.\nOlet saattanut kirjoittaa URL:in väärin tai olet seurannut virheellistä linkkiä.\nKyseessä voi myös mahdollisesti olla virhe sivuston {{SITENAME}} käyttämässä ohjelmistossa.",
        "nosuchspecialpage": "Kyseistä toimintosivua ei ole",
        "nospecialpagetext": "<strong>Ohjelmisto ei tunnista pyytämääsi toimintosivua.</strong>\n\nLuettelo toimintosivuista löytyy sivulta [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Virhe",
        "enterlockreason": "Anna lukituksen syy sekä sen arvioitu poistamisaika",
        "readonlytext": "Tietokanta on tällä hetkellä lukittu. Uusia sivuja ei voi luoda eikä muitakaan muutoksia tehdä. Syynä ovat todennäköisimmin rutiininomaiset tietokannan ylläpitotoimet.\n\nTietokannan lukinneen ylläpitäjän selitys: $1",
        "missing-article": "Sivun sisältöä ei löytynyt tietokannasta nimellä \"$1\" $2.\n\nYleensä tämä johtuu vanhentuneesta vertailu- tai historialinkistä sivulle, joka on poistettu.\n\nJos kyseessä ei ole poistettu sivu, olet ehkä löytänyt virheen ohjelmistossa.\nIlmoita tästä [[Special:ListUsers/sysop|ylläpitäjälle]] ja kerro viestissäsi sivun URL.",
-       "missingarticle-rev": "(versio: $1)",
+       "missingarticle-rev": "(versio nro: $1)",
        "missingarticle-diff": "(vertailu: $1, $2)",
        "readonly_lag": "Tietokanta on automaattisesti lukittu, jotta kaikki tietokantapalvelimet saisivat kaikki tuoreet muutokset",
        "internalerror": "Sisäinen virhe",
        "querypage-no-updates": "Tämän sivun tietoja ei toistaiseksi päivitetä.",
        "viewsource": "Näytä wikiteksti",
        "viewsource-title": "Lähdekoodi sivulle $1",
-       "actionthrottled": "Toiminto nopeusrajoitettu",
-       "actionthrottledtext": "Ylläpitosyistä tämän toiminnon suorittamista on rajoitettu. Olet suorittanut tämän toiminnon liian monta kertaa lyhyen ajan sisällä. Yritä myöhemmin uudelleen.",
-       "protectedpagetext": "Tämä sivu on suojattu muutoksilta.",
+       "actionthrottled": "Toiminnon useaa suorittamista on rajoitettu",
+       "actionthrottledtext": "Häiriköinnin estämiseksi tämän toiminnon suorittamista on rajoitettu niin, että sitä ei voi tehdä useita kertoja lyhyen ajan sisällä. Olet suorittanut toiminnon nyt liian monta kertaa. \nYritä uudelleen muutaman minuutin kuluttua.",
+       "protectedpagetext": "Tämä sivu on suojattu muutoksilta ja muilta toiminnoilta.",
        "viewsourcetext": "Voit katsoa ja kopioida tämän sivun lähdetekstiä:",
        "viewyourtext": "Voit tarkastella ja kopioida lähdekoodin '''tekemistäsi muutoksista''' tähän sivuun:",
        "protectedinterface": "Tämä sivu sisältää ohjelmiston käyttöliittymätekstiä ja on suojattu häiriköinnin estämiseksi.\nViestien kääntäminen tulisi tehdä [//translatewiki.net/ translatewiki.netissä] – MediaWikin kotoistusprojektissa.",
        "history-feed-description": "Tämän sivun muutoshistoria",
        "history-feed-item-nocomment": "$1 ($2)",
        "history-feed-empty": "Pyydettyä sivua ei ole olemassa.\nSe on saatettu poistaa wikistä tai nimetä uudelleen.\nKokeile [[Special:Search|hakua]] löytääksesi asiaan liittyviä sivuja.",
+       "history-edit-tags": "Muokkaa valittujen sivuversioiden merkkauksia",
        "rev-deleted-comment": "(muokkausyhteenveto poistettu)",
        "rev-deleted-user": "(käyttäjänimi poistettu)",
        "rev-deleted-event": "(lokitiedot poistettu)",
        "right-sendemail": "Lähettää sähköpostia muille käyttäjille",
        "right-passwordreset": "Tarkastella salasanan alustusviestejä",
        "right-managechangetags": "Luoda ja poistaa [[Special:Tags|merkkauksia]] tietokannasta",
+       "right-applychangetags": "Asettaa [[Special:Tags|merkkauksia]] omien muutosten yhteyteen",
+       "right-changetags": "Lisätä ja poistaa satunnaisia [[Special:Tags|merkkauksia]] yksittäisissä sivuversioissa tai lokimerkinnöissä",
        "newuserlogpage": "Uudet käyttäjät",
        "newuserlogpagetext": "Tämä on loki luoduista käyttäjätunnuksista.",
        "rightslog": "Käyttöoikeusloki",
        "action-editmyprivateinfo": "muokata omia yksityisiä tietojasi",
        "action-editcontentmodel": "muokata sivun sisältömallia",
        "action-managechangetags": "luoda ja poistaa merkkauksia tietokannasta",
+       "action-applychangetags": "käyttää merkkauksia muutostesi yhteydessä",
+       "action-changetags": "lisätä ja poistaa satunnaisia merkkauksia yksittäisissä sivuversioissa ja lokimerkinnöissä",
        "nchanges": "$1 {{PLURAL:$1|muutos|muutosta}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|viimeisen käynnin jälkeen}}",
        "enhancedrc-history": "historia",
        "listfiles-delete": "poista",
        "listfiles-summary": "Tämä toimintosivu näyttää kaikki tallennetut tiedostot.",
        "listfiles_search_for": "Etsi tiedoston nimellä:",
+       "listfiles-userdoesnotexist": "Käyttäntunnusta \"$1\" ei ole rekisteröity.",
        "imgfile": "tiedosto",
        "listfiles": "Tiedostoluettelo",
        "listfiles_thumb": "Pienoiskuva",
        "logempty": "Ei tapahtumia lokissa.",
        "log-title-wildcard": "Haun kohteet alkavat tällä tekstillä",
        "showhideselectedlogentries": "Muuta valittujen lokitapahtumien näkyvyyttä",
+       "log-edit-tags": "Muuta merkkauksia valituissa lokimerkinnöissä",
        "allpages": "Kaikki sivut",
        "nextpage": "Seuraava sivu ($1)",
        "prevpage": "Edellinen sivu ($1)",
        "emailccsubject": "Kopio lähettämästäsi viestistä osoitteeseen $1: $2",
        "emailsent": "Sähköposti lähetetty",
        "emailsenttext": "Sähköpostiviestisi on lähetetty.",
-       "emailuserfooter": "Tämän sähköpostin lähetti $1 käyttäjälle $2 käyttämällä ”Lähetä sähköpostia” -toimintoa {{GRAMMAR:inessive|{{SITENAME}}}}.",
+       "emailuserfooter": "Tämän sähköpostin lähetti $1 vastaanottajalle $2 käyttämällä ”{{int:emailpage}}” -toimintoa {{GRAMMAR:inessive|{{SITENAME}}}}.",
        "usermessage-summary": "Jätetään järjestelmäviesti.",
        "usermessage-editor": "Järjestelmäviestittäjä",
        "watchlist": "Tarkkailulista",
        "patrol-log-page": "Muutostentarkastusloki",
        "patrol-log-header": "Tämä on loki tarkastetuista muutoksista.",
        "log-show-hide-patrol": "$1 muutostentarkastusloki",
+       "log-show-hide-tag": "$1 merkkausten loki",
        "deletedrevision": "Poistettiin vanha versio $1",
        "filedeleteerror-short": "Tiedoston $1 poistaminen epäonnistui",
        "filedeleteerror-long": "Tiedoston poistaminen epäonnistui:\n\n$1",
        "tags-deactivate-reason": "Syy:",
        "tags-deactivate-not-allowed": "Ei ole mahdollista poistaa käytöstä merkkausta \"$1\".",
        "tags-deactivate-submit": "Poista käytöstä",
+       "tags-apply-no-permission": "Sinulla ei ole oikeutta käyttää merkkauksia muutostesi yhteydessä.",
+       "tags-apply-not-allowed-one": "Merkkausta \"$1\" ei ole sallittua asettaa käsin.",
+       "tags-apply-not-allowed-multi": "Seuraavia {{PLURAL:$2|merkkauksia}} ei ole sallittua asettaa käsin: $1",
+       "tags-update-no-permission": "Sinulla ei ole oikeutta lisätä tai poistaa merkkauksia yksittäisissä sivuversioissa tai lokimerkinnöissä.",
+       "tags-update-add-not-allowed-one": "Merkkausta \"$1\" ei ole sallittua asettaa käsin.",
+       "tags-update-add-not-allowed-multi": "Seuraavia {{PLURAL:$2|merkkauksia}} ei ole sallittua asettaa käsin: $1",
+       "tags-update-remove-not-allowed-one": "Merkkausta \"$1\" ei ole sallittua poistaa.",
+       "tags-update-remove-not-allowed-multi": "Seuraavia {{PLURAL:$2|merkkauksia}} ei ole sallittua poistaa käsin: $1",
+       "tags-edit-title": "Muokkaa merkkauksia",
+       "tags-edit-manage-link": "Hallinnoi merkkauksia",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Valittu versio|Valitut versiot}} kohteesta [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Valittu lokitapahtuma|Valitut lokitapahtumat}}:",
+       "tags-edit-revision-legend": "Lisää tai poista merkkauksia {{PLURAL:$1|tässä versiossa|kaikissa $1 versiossa}}",
+       "tags-edit-logentry-legend": "Lisää tai poista merkkauksia {{PLURAL:$1|tässä lokimerkinnässä|kaikissa $1 lokimerkinnässä}}",
+       "tags-edit-existing-tags": "Tämänhetkiset merkkaukset:",
+       "tags-edit-existing-tags-none": "''Ei mitään''",
+       "tags-edit-new-tags": "Uudet merkkaukset:",
+       "tags-edit-add": "Lisää nämä merkkaukset:",
+       "tags-edit-remove": "Poista nämä merkkaukset:",
+       "tags-edit-remove-all-tags": "(poista kaikki merkkaukset)",
+       "tags-edit-chosen-placeholder": "Valitse joitakin merkkauksia",
+       "tags-edit-chosen-no-results": "Ei löytynyt vastaavia merkkauksia",
+       "tags-edit-reason": "Syy:",
+       "tags-edit-revision-submit": "Lähetä tekemäsi muutokset {{PLURAL:$1|tähän versioon|$1 versioon}}",
+       "tags-edit-logentry-submit": "Lähetä muutoksesi {{PLURAL:$1|tähän lokimerkintään|$1 lokimerkintään}}",
+       "tags-edit-success": "<strong>Muutokset on onnistuneesti toteutettu.</strong>",
+       "tags-edit-failure": "<strong>Muutoksia ei voitu toteuttaa:</strong> $1",
+       "tags-edit-nooldid-title": "Kohdeversio ei ole kelvollinen",
+       "tags-edit-none-selected": "Valitse ainakin yksi merkkaus, jonka lisäät tai poistat.",
        "comparepages": "Vertaile sivuja",
        "compare-page1": "Sivu 1",
        "compare-page2": "Sivu 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|tuhosi}} merkkauksen \"$4\" (poistettu $5 {{PLURAL:$5|sivuversiosta tai lokimerkinnästä}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktivoi}} merkkauksen \"$4\" käyttäjien ja bottien käytettäväksi",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|otti pois käytöstä}} merkkauksen \"$4\" käyttäjiltä ja boteilta",
+       "log-name-tag": "Merkkausten loki",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|lisäsi}} {{PLURAL:$7|merkkauksen|merkkaukset}} $6 kohdeversioon $4 sivulla $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lisäsi}} {{PLURAL:$7|merkkauksen|merkkaukset}} $6 lokimerkintään $5 sivulla $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|poisti}} {{PLURAL:$9|merkkauksen|merkkaukset}} $8 kohdeversiosta $4 sivulla $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|poisti}} {{PLURAL:$9|merkkauksen|merkkaukset}} $8 lokimerkinnästä $5 sivulla $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|päivitti}} merkkauksia kohdeversiossa $4 sivulla $3 ({{PLURAL:$7|lisätty}} $6; {{PLURAL:$9|poistettu}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|päivitti}} merkkauksia lokimerkinnässä $5 sivulla $3 ({{PLURAL:$7|lisätty}} $6; {{PLURAL:$9|poistettu}} $8)",
        "rightsnone": "(ei oikeuksia)",
        "revdelete-summary": "yhteenvedon",
        "feedback-adding": "Lisätään palautetta sivulle...",
        "feedback-error1": "Virhe: Ohjelmointirajapinnan vastausta ei tunnistettu",
        "feedback-error2": "Virhe: Muokkaus epäonnistui",
        "feedback-error3": "Virhe: Ohjelmointirajapinta ei vastaa",
+       "feedback-error4": "Virhe: Annetun palautteen otsikkoa ei voida lähettää",
        "feedback-message": "Viesti",
        "feedback-subject": "Otsikko",
        "feedback-submit": "Lähetä",
index cedbfad..c8d21dc 100644 (file)
        "tags-deactivate-reason": "Motif :",
        "tags-deactivate-not-allowed": "Il n'est pas possible de désactiver la balise « $1 ».",
        "tags-deactivate-submit": "Désactiver",
+       "tags-edit-existing-tags-none": "\"Aucun\"",
+       "tags-edit-reason": "Motif :",
+       "tags-edit-success": "<strong>Les modifications ont été appliquées avec succès.</strong>",
        "comparepages": "Comparer des pages",
        "compare-page1": "Page 1",
        "compare-page2": "Page 2",
index 7a79bd8..73b0680 100644 (file)
        "patrol-log-page": "Rexistro de revisións patrulladas",
        "patrol-log-header": "Este é un rexistro das revisións patrulladas.",
        "log-show-hide-patrol": "$1 o rexistro de patrullas",
+       "log-show-hide-tag": "$1 rexistro de etiquetas",
        "deletedrevision": "A revisión vella $1 foi borrada.",
        "filedeleteerror-short": "Erro ao eliminar o ficheiro: $1",
        "filedeleteerror-long": "Atopáronse erros ao eliminar o ficheiro:\n\n$1",
        "tags-deactivate-reason": "Motivo:",
        "tags-deactivate-not-allowed": "Non é posible reactivar a páxina \"$1\".",
        "tags-deactivate-submit": "Reactivar",
+       "tags-apply-no-permission": "Non ten permisos para aplicar etiquetas de cambios xunto cos seus tus cambios.",
+       "tags-apply-not-allowed-one": "A etiqueta \"$1\" non se puede aplicar manualmente.",
+       "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta non se pode|As seguintes etiquetas non se poden}} aplicar manualmente: $1",
+       "tags-update-no-permission": "Non ten permisos para engadir ou quitar etiquetas de cambio das revisións individuais ou das entradas do rexistro.",
+       "tags-update-add-not-allowed-one": "A etiqueta \"$1\" non se pode engadir manualmente.",
+       "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta non se pode|As seguintes etiquetas non se poden}} engadir manualmente: $1",
+       "tags-update-remove-not-allowed-one": "A etiqueta \"$1\" non se pode eliminar.",
        "comparepages": "Comparar páxinas",
        "compare-page1": "Páxina 1",
        "compare-page2": "Páxina 2",
index 0734b74..1fae9df 100644 (file)
        "history-feed-description": "היסטוריית הגרסאות של הדף הזה בוויקי",
        "history-feed-item-nocomment": "$1 ב־$2",
        "history-feed-empty": "הדף המבוקש לא נמצא.\nייתכן שהוא נמחק, או ששמו שונה.\nבאפשרותך לנסות [[Special:Search|לחפש]] דפים רלוונטיים חדשים.",
-       "history-edit-tags": "ער×\99×\9bת ×ª×\92×\99×\9d ×©×\9c ×\92רס×\90×\95ת × ×\91×\97ר×\95ת",
+       "history-edit-tags": "ער×\99×\9bת ×\94ת×\92×\99×\95ת ×©×\9c ×\94×\92רס×\90×\95ת ×©× ×\91×\97ר×\95",
        "rev-deleted-comment": "(תקציר העריכה הוסר)",
        "rev-deleted-user": "(שם המשתמש הוסר)",
        "rev-deleted-event": "(פרטים מהיומן הוסרו)",
        "right-sendemail": "שליחת דואר אלקטרוני למשתמשים אחרים",
        "right-passwordreset": "צפייה בדואר אלקטרוני של איפוס סיסמה",
        "right-managechangetags": "יצירת ומחיקת [[Special:Tags|תגיות]] מבסיס הנתונים",
-       "right-applychangetags": "×\94×\97×\9cת [[Special:Tags|ת×\92×\99×\9d]] יחד עם שינויים",
-       "right-changetags": "×\94×\95ספת ×\95×\94סר×\94 ×©×\9c [[Special:Tags|ת×\92×\99×\9d]] ×\91×\92רס×\90×\95ת ×\9eס×\95×\99×\9e×\95ת ×\95×\91רשומות יומן",
+       "right-applychangetags": "×\94×\97×\9cת [[Special:Tags|ת×\92×\99×\95ת]] יחד עם שינויים",
+       "right-changetags": "×\94×\95ספת ×\95×\94סר×\94 ×©×\9c [[Special:Tags|ת×\92×\99×\95ת]] ×\9b×\9cש×\94×\9f ×\9c×\92רס×\90×\95ת ×\9eס×\95×\99×\9e×\95ת ×\95×\9cרשומות יומן",
        "newuserlogpage": "יומן רישום משתמשים",
        "newuserlogpagetext": "זהו יומן המכיל הרשמות של משתמשים.",
        "rightslog": "יומן תפקידים",
        "action-editmyprivateinfo": "לערוך את המידע הפרטי שלך",
        "action-editcontentmodel": "לערוך את מודל התוכן של דף",
        "action-managechangetags": "ליצור ולמחוק תגיות מבסיס הנתונים",
-       "action-applychangetags": "×\9c×\94×\97×\99×\9c ×ª×\92×\99×\9d ×\9cשינויים שלכם",
-       "action-changetags": "×\9c×\94×\95ס×\99×£ ×\95×\9c×\94ס×\99ר ×ª×\92×\99×\9d ×©×¨×\99ר×\95ת×\99×\99×\9d ×\91×\92רס×\90×\95ת ×\9eס×\95×\99×\9e×\95ת ×\95×\91רשומות יומן",
+       "action-applychangetags": "×\9c×\94×\97×\99×\9c ×ª×\92×\99×\95ת ×\99×\97×\93 ×¢×\9d ×\94שינויים שלכם",
+       "action-changetags": "×\9c×\94×\95ס×\99×£ ×\95×\9c×\94ס×\99ר ×ª×\92×\99×\95ת ×\9b×\9cש×\94×\9f ×\9c×\92רס×\90×\95ת ×\9eס×\95×\99×\9e×\95ת ×\95×\9cרשומות יומן",
        "nchanges": "{{PLURAL:$1|שינוי אחד|$1 שינויים}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|מאז ביקורך האחרון}}",
        "enhancedrc-history": "היסטוריה",
        "logempty": "אין פריטים תואמים ביומן.",
        "log-title-wildcard": "חיפוש כותרות המתחילות באותיות אלה",
        "showhideselectedlogentries": "הצגת/הסתרת פעולות היומן שנבחרו",
-       "log-edit-tags": "ער×\99×\9bת ×ª×\92×\99×\9d ×\90×\95 ×¨×©×\95×\9e×\95ת ×\99×\95×\9e×\9f × ×\91×\97ר×\95ת",
+       "log-edit-tags": "ער×\99×\9bת ×\94ת×\92×\99×\95ת ×©×\9c ×¨×©×\95×\9e×\95ת ×\94×\99×\95×\9e×\9f ×©× ×\91×\97ר×\95",
        "allpages": "כל הדפים",
        "nextpage": "הדף הבא ($1)",
        "prevpage": "הדף הקודם ($1)",
        "patrol-log-page": "יומן שינויים בדוקים",
        "patrol-log-header": "יומן זה מציג גרסאות שנבדקו.",
        "log-show-hide-patrol": "$1 יומן שינויים בדוקים",
-       "log-show-hide-tag": "$1 ×\99×\95×\9e×\9f ×\94ת×\92×\99×\9d",
+       "log-show-hide-tag": "$1 ×\99×\95×\9e×\9f ×\94ת×\92×\99×\95ת",
        "deletedrevision": "מחק גרסה ישנה $1",
        "filedeleteerror-short": "שגיאה במחיקת הקובץ: $1",
        "filedeleteerror-long": "שגיאות שאירעו בעת מחיקת הקובץ:\n\n$1",
        "tags-deactivate-reason": "הסבר:",
        "tags-deactivate-not-allowed": "לא ניתן לבטל את הפעלת התגית \"$1\".",
        "tags-deactivate-submit": "ביטול הפעלה",
-       "tags-apply-no-permission": "אין לך הרשאה להחיל תגי שינוי עם השינויים שלך.",
-       "tags-apply-not-allowed-one": "לא ניתן להחיל את התג \"$1\" ידנית.",
-       "tags-apply-not-allowed-multi": "אי־אפשר להחיל את {{PLURAL:$2|התג הבא|התגים הבאים}} ידנית: $1",
-       "tags-update-no-permission": "אין לך הרשאה להוסיף תגי שינוי לגרסאות מסוימות א רשומות יומן או להסיר אותם.",
-       "tags-update-add-not-allowed-one": "אי־אפשר להוסיף את התג \"$1\" ידנית.",
-       "tags-update-add-not-allowed-multi": "אי־אפשר להוסיף את {{PLURAL:$2|התג הבא|התגים הבאים}}: $1",
-       "tags-update-remove-not-allowed-one": "לא ניתן להסיר את התג \"$1\".",
-       "tags-update-remove-not-allowed-multi": "אי־אפשר להסיר את {{PLURAL:$2|התג הבא|התגים הבאים}} ידנית: $1",
-       "tags-edit-title": "עריכת תגים",
+       "tags-apply-no-permission": "אין לך הרשאה להחיל תגיות שינויים יחד עם השינויים שלך.",
+       "tags-apply-not-allowed-one": "לא ניתן להחיל את התגית \"$1\" ידנית.",
+       "tags-apply-not-allowed-multi": "לא ניתן להחיל את {{PLURAL:$2|התגית הבאה|התגיות הבאות}} ידנית: $1",
+       "tags-update-no-permission": "אין לך הרשאה להוסיף או להסיר תגיות שינויים לגרסאות מסוימות או לרשומות יומן.",
+       "tags-update-add-not-allowed-one": "לא ניתן להוסיף את התגית \"$1\" ידנית.",
+       "tags-update-add-not-allowed-multi": "לא ניתן להוסיף את {{PLURAL:$2|התגית הבאה|התגיות הבאות}} ידנית: $1",
+       "tags-update-remove-not-allowed-one": "לא ניתן להסיר את התגית \"$1\".",
+       "tags-update-remove-not-allowed-multi": "לא ניתן להסיר את {{PLURAL:$2|התגית הבאה|התגיות הבאות}} ידנית: $1",
+       "tags-edit-title": "עריכת תגיות",
+       "tags-edit-manage-link": "ניהול תגיות",
+       "tags-edit-revision-selected": "{{PLURAL:$1|הגרסה שנבחרה|הגרסאות שנבחרו}} מתוך [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|פעולת היומן שנבחרה|פעולות היומן שנבחרו}}:",
+       "tags-edit-revision-legend": "הוספה של תגיות {{PLURAL:$1|לגרסה הזאת|לכל $1 הגרסאות}} או הסרתן",
+       "tags-edit-logentry-legend": "הוספה של תגיות {{PLURAL:$1|לרשומת היומן הזאת|לכל $1 רשומות היומן}} או הסרתן",
+       "tags-edit-existing-tags": "תגיות קיימות:",
+       "tags-edit-existing-tags-none": "''אין''",
+       "tags-edit-new-tags": "תגיות חדשות:",
+       "tags-edit-add": "הוספת התגיות הבאות:",
+       "tags-edit-remove": "הסרת התגיות הבאות:",
+       "tags-edit-remove-all-tags": "(הסרת כל התגיות)",
+       "tags-edit-chosen-placeholder": "בחירת תגיות מסוימות",
+       "tags-edit-chosen-no-results": "לא נמצאו תגיות מתאימות",
+       "tags-edit-reason": "סיבה:",
+       "tags-edit-revision-submit": "החלת שינויים {{PLURAL:$1|לגרסה הזאת|ל־$1 גרסאות}}",
+       "tags-edit-logentry-submit": "החלת שינויים {{PLURAL:$1|לרשומת היומן הזאת|ל־$1 רשומת היומן}}",
+       "tags-edit-success": "<strong>השינויים הוחלו בהצלחה.</strong>",
+       "tags-edit-failure": "<strong>החלת השינויים נכשלה:</strong>\n$1",
+       "tags-edit-nooldid-title": "גרסת היעד אינה תקינה",
+       "tags-edit-nooldid-text": "או שלא נתת שום גרסת יעד לביצוע הפעולה הזאת או שהגרסה שציינת אינה קיימת.",
+       "tags-edit-none-selected": "יש לבחור לפחות תגית אחת להוספה או להסרה.",
        "comparepages": "השוואת דפים",
        "compare-page1": "דף 1",
        "compare-page2": "דף 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|מחק|מחקה}} את התגית \"4$\" (שהוסרה {{PLURAL:$5|מגרסה אחת/פריט יומן אחד|מ־$5 גרסאות ו/או פריטי יומן}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|הפעיל|הפעילה}} את התגית \"$4\" לשימוש על־ידי משתמשים ובוטים",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|ביטל|ביטלה}} את הפעלת התגית \"$4\" לשימוש על־ידי משתמשים ובוטים",
+       "log-name-tag": "יומן תגיות",
+       "log-description-tag": "הדף הזה מראה מתי משתמשים הוסיפו [[Special:Tags|תגיות]] לגרסאות או רשומות יומן מסוימות או הסירו אותן. היומן אינו מציג פעולות תיוג שבוצעו כחלק מעריכה, מחיקה או פעולה דומה.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|הוסיף|הוסיפה}} את {{PLURAL:$7|התגית|התגיות}} $6 לגרסה $4 של הדף $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|הוסיף|הוסיפה}} את {{PLURAL:$7|התגית|התגיות}} $6 לרשומת היומן $5 של הדף $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|הסיר|הסירה}} את {{PLURAL:$9|התגית|התגיות}} $8 מהגרסה $4 של הדף $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|הסיר|הסירה}} את {{PLURAL:$9|התגית|התגיות}} $8 מרשומת היומן $5 של הדף $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|עדכן|עדכנה}} את התגיות בגרסה $4 של הדף $3 ({{PLURAL:$7|הוסיף|הוסיפה}} את $6; {{PLURAL:$9|הסיר|הסירה}} את $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|עדכן|עדכנה}} את התגיות ברשומת היומן $5 של הדף $3 ({{PLURAL:$7|הוסיף|הוסיפה}} את $6; {{PLURAL:$9|הסיר|הסירה}} את $8)",
        "rightsnone": "(כלום)",
        "revdelete-summary": "תקציר העריכה",
        "feedback-adding": "הוספת משוב לדף...",
index 3b11ca5..a6c76e4 100644 (file)
        "viewhelppage": "सहायता पृष्ठ देखें",
        "categorypage": "श्रेणी पृष्ठ देखें",
        "viewtalkpage": "चर्चा देखें",
-       "otherlanguages": "à¤\85नà¥\8dय à¤­à¤¾à¤·à¤¾à¤\8fà¤\81",
+       "otherlanguages": "à¤\85नà¥\8dय à¤­à¤¾à¤·à¤¾à¤\93à¤\82 à¤®à¥\87à¤\82",
        "redirectedfrom": "($1 से पुनर्निर्देशित)",
        "redirectpagesub": "पुनर्निर्देश पृष्ठ",
        "redirectto": "को अनुप्रेषित:",
index 9434ddc..d8bb042 100644 (file)
        "mimetype": "Typo MIME:",
        "download": "discargar",
        "unwatchedpages": "Paginas non observate",
-       "listredirects": "Listar redirectiones",
+       "listredirects": "Lista de redirectiones",
        "listduplicatedfiles": "Lista de files con duplicatos",
        "listduplicatedfiles-summary": "Isto es un lista de files del quales le version le plus recente es un duplicato del version le plus recente de un altere file. Solmente le files local es examinate.",
        "listduplicatedfiles-entry": "[[:File:$1|$1]] ha [[$3|{{PLURAL:$2|un duplicato|$2 duplicatos}}]].",
index ac4e9f0..ccd38a2 100644 (file)
@@ -83,7 +83,7 @@
        "july-gen": "جولائی",
        "august-gen": "اگست",
        "september-gen": "ستمبار",
-       "october-gen": "اکتÙ\88بار",
+       "october-gen": "اکتوبر",
        "november-gen": "نومبار",
        "december-gen": "دسمبار",
        "jan": "جنوری",
        "lastmodifiedat": "آخری بار تدوین $2, $1 کورونو ھوی",
        "viewcount": "ھیہ صفحہ گیونو ھوی {{PLURAL:$1|ای‌بار|$1 مرتبہ}}",
        "protectedpage": "محفوظ شدہ صفحہ",
-       "jumpto": "ھیہ ووشکی څروٹھاوے",
+       "jumpto": "ھیہ ووݰکی څروٹھاوے",
        "jumptonavigation": "رہنمائی",
        "jumptosearch": "تلاش",
        "view-pool-error": "معذرت: تمام سرورا موجودہ وختہ اِضافی بوجھ شیر.\nبو زیادہ صارفین موجودہ وختہ ھیہ صفحو لاڑینیان \nبرائے مہربانی! صفحو لوڑیکو بچے دوبارہ کوشش کوریکاری پروشٹی پھوکرو انتظار کورے.\n\n$1",
        "currentevents": "ھنونو واقعات",
        "currentevents-url": "Project:ھنونو واقعات",
        "disclaimers": "اعلانات",
-       "disclaimerpage": "Project:عام کھوار اعلان",
+       "disclaimerpage": "Project:عام اعلان",
        "edithelp": "مدد براۓ ترمیم",
        "mainpage": "آویلو صفحہ",
        "mainpage-description": "سرورق",
        "policy-url": "Project:حکمتِ عملی",
-       "portal": "مھراکہ",
-       "portal-url": "Project:مھراکہ",
+       "portal": "دیوان عام",
+       "portal-url": "Project:دیوان عام",
        "privacy": "رازان فاش نو کوریکو بچے اصول",
        "privacypage": "Project:رازان فاش نو کوریکو بچے اصول",
        "badaccess": "خطائے اجازت",
        "editsection": "ترمیم",
        "editold": "ترمیم",
        "viewsourceold": "مآخذو لوڑے",
-       "editlink": "تدÙ\88Û\8cÙ\86 کورے",
+       "editlink": "اÛ\8cÚ\88Ù¹ کورے",
        "viewsourcelink": "مآخذو لوڑے",
        "editsectionhint": "تدوینِو حصّہ: $1",
        "toc": "فہرست",
        "headline_sample": "شہ سرخی",
        "headline_tip": "شہ سرخی درجہ دوم",
        "nowiki_sample": "غیرشکلبندشدہ متنو ھیارا درج کورور",
-       "nowiki_tip": "ویکی شکلبندی نظرانداز کورے",
+       "nowiki_tip": "ویکی شکلبندیو نظرانداز کورے",
        "image_tip": "پیوستہ مسل",
        "media_tip": "مسلو لنک",
        "sig_tip": "تہ دستخط بمع مہرِ وخت",
        "difference-title": "ایڈٹ کاردوان موژی فرق \"$1\"",
        "lineno": "لکیر $1:",
        "compareselectedversions": "منتخب متـنو موازنہ",
-       "editundo": "استرجع",
+       "editundo": "Ø¢Ú\86Û\8c(Undo)",
        "diff-multi-sameuser": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions}} by the same user not shown)",
        "searchresults": "تلاشو نتیجہ",
        "searchresults-title": "نتائجِ تلاش برائے \"$1\"",
        "searchprofile-everything": "سف اشناری",
        "searchprofile-advanced": "ایڈوانس",
        "searchprofile-articles-tooltip": "$1 ھیہ صفحا تلاش",
-       "searchprofile-images-tooltip": "تلاش برائے فایل",
+       "searchprofile-images-tooltip": "تلاش برائے فائل",
        "searchprofile-everything-tooltip": "ہر ژاغا تلاش کورے",
        "searchprofile-advanced-tooltip": "کسٹم نیم اسپیسا تلاش کورے",
        "search-result-size": "$1 ({{PLURAL:$2|1 لوظ|$2 الفاظ}})",
        "action-edit": "ھیہ صفحا ایڈیٹنگ کورے",
        "nchanges": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
        "enhancedrc-history": "تاریخچہ",
-       "recentchanges": "حاÙ\84Û\8cہ تبدیلی",
+       "recentchanges": "تازہ تبدیلی",
        "recentchanges-legend": "حالیہ تبدیلیان اختیارات",
        "recentchanges-summary": "ھیہ صفحا کھوار ویکیپیڈیا باک تازہ تریں تبدیلیان مشاہدہ کورے",
        "recentchanges-feed-description": "کھوارا ترجمہ",
        "filename": "فایلو نام",
        "filedesc": "خلاصہ",
        "license": "لایسنس",
-       "license-header": "لایسنسنگ",
+       "license-header": "لائسنسنگ",
        "imgfile": "فائل",
-       "file-anchor-link": "فایل",
+       "file-anchor-link": "فائل",
        "filehist": "مسلو تاریخ",
        "filehist-help": "ھیہ لوڑیکو بچے  کہ کیہ خاص وختہ فایل کیہ قسمہ ظاہر باو اوشتای ھتے  تاریخ یا وختہ طق(کلک) کورے",
        "filehist-revert": "آچی",
        "tooltip-search-go": "اگر بالکل ھیہ نامو سورا صفحہ کہ موجود شیر تھے ھتے صفحہا بوغے",
        "tooltip-search-fulltext": "ھیہ متنو بچے صفحاتن تلاش کورے",
        "tooltip-p-logo": "سرورقا بوغے",
-       "tooltip-n-mainpage": "اصلی صفحہا بوغے",
+       "tooltip-n-mainpage": "اصلی صفحا بوغے",
        "tooltip-n-mainpage-description": "اصلی صفحہ",
        "tooltip-n-portal": "کھوار ویکیپیڈیو منصوبو متعلقہ، تو کیاغ کوریکو بوس، اشناریان کورا تلاش کوریلک",
        "tooltip-n-currentevents": "حالیہ واقعاتہ بیرو پس منظری معلوماتن لوڑے",
        "exif-datetimedigitized": "ڈیجیٹلائز کوریکو تاریخ اوچے وخت",
        "exif-orientation-1": "عام",
        "exif-dc-date": "تاریخ",
-       "namespacesall": "تھÙ\85اÙ\85Ù\88",
+       "namespacesall": "سÙ\81",
        "monthsall": "سف",
        "confirm_purge_button": "OK/ٹھیک شیر",
        "table_pager_first": "آویلو صفحہ",
index c5ac395..86754cf 100644 (file)
        "history-feed-description": "Ähler Versione vun dä Wikisigg",
        "history-feed-item-nocomment": "$1 aam $3 öm $4 Uhr",
        "history-feed-empty": "De aanjefrochte Sigg jitt et nit. Künnt sin, dat se enzwesche fottjeschmesse udder ömjenannt woode es. Kanns jo ens [[Special:Search|em Wiki söke looße]], öm de zopass, neu Sigge ze finge.",
+       "history-edit-tags": "Donn de Makehronge vun de ußjesöhk Väsjohne beärbeide",
        "rev-deleted-comment": "(„Koot Zosammejefass, Quell“ usjeblendt)",
        "rev-deleted-user": "(Metmaacher Name usjeblendt)",
        "rev-deleted-event": "(Eijnzelheijte vom Logboch-Enndraach fottjenomme)",
        "right-editmyoptions": "De eije Enschtällonge ändere",
        "right-rollback": "All de letzte Änderunge fom letzte Metmaacher aan ene Sigg retur maache",
        "right-markbotedits": "Retur jemaate Änderonge als Bot-Änderung makeere",
-       "right-noratelimit": "Kein Beschränkunge dorch Jrenze (<i lang=\"en\">[http://www.mediawiki.org/wiki/Manual:%24wgRateLimits $wgRateLimits]</i>)",
+       "right-noratelimit": "Kein Beschränkonge dorsch Jränze (<i lang=\"en\">[http://www.mediawiki.org/wiki/Manual:%24wgRateLimits $wgRateLimits]</i>)",
        "right-import": "Sigge uß ander Wikis empochteere",
        "right-importupload": "Sigge övver et XML-Datei-Huhlade empochteere",
        "right-patrol": "Anderlücks Änderunge aan Sigge als „nohjeloort“ makeere",
        "right-sendemail": "<i lang=\"en\">e-mail</i> aan ander Metmaacher schecke",
        "right-passwordreset": "De <i lang=\"en\">e-mails</i> vum Paßwoot neu Säze aanloore",
        "right-managechangetags": "[[Special:Tags|Kännzeijsche]] en de Dahtebangk aanlähje udder fottschmiiße",
+       "right-applychangetags": "[[Special:Tags|Makehronge]] met de eije Änderonge zersamme verjävve",
+       "right-changetags": "[[Special:Tags|Makehronge]] vun Väsjohne un Enndrähsche em Logbohch fott nämme un zohföhje",
        "newuserlogpage": "Logboch för neu Metmaachere",
        "newuserlogpagetext": "He sin de Metmaacher opjelėßß, di sesh nöü aanjemäldt han.",
        "rightslog": "Logboch för Änderunge aan Metmaacher-Räächde",
        "action-editmyprivateinfo": "Ding päsöönlesche Aanjaabe ze ändere",
        "action-editcontentmodel": "et Modäll vum Ennhald vun Sigge ze verändere",
        "action-managechangetags": "Kännzeijsche en de Dahtebangk aanlähje udder fottschmiiße",
+       "action-applychangetags": "Makehronge met de eije Änderonge zersamme ze verjävve",
+       "action-changetags": "Makehronge vun Väsjohne un Enndrähsche em Logbohch fott nämme un zohföhje",
        "nchanges": "{{PLURAL:$1|Ein Änderong|$1 Änderonge|Kein Änderong}}",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|Ein|$1|Kein}} zigg_em läzde Aanloore",
        "enhancedrc-history": "Väsjohne",
        "logempty": "Mer han kein zopaß Endrähsch en däm Logbooch.",
        "log-title-wildcard": "Sök noh Titelle, di aanfange met …",
        "showhideselectedlogentries": "Ußjesöhk Endrääsch verschteische udder zeije",
+       "log-edit-tags": "Donn de Makehronge vun de ußjesöhk Enndrähsch em Logbohch beärbeide",
        "allpages": "All Sigge",
        "nextpage": "De nächste Sigg: „$1“",
        "prevpage": "Vörijje Sigg ($1)",
        "patrol-log-page": "Logboch vun de nohjeloorte Änderunge",
        "patrol-log-header": "<!-- -->",
        "log-show-hide-patrol": "$1 et Logbuch vum Sigge nohlooere",
+       "log-show-hide-tag": "Donn et Logbohch vun de Makehronge $1",
        "deletedrevision": "De ahl Version „$1“ es fottjeschmesse",
        "filedeleteerror-short": "Fäähler bem Datei-Fottschmieße: $1",
        "filedeleteerror-long": "Bem fosooch, de Datei fottzeschmieße, hatte mer Fäähler:\n\n$1",
        "tags-deactivate-reason": "Jrond:",
        "tags-deactivate-not-allowed": "Ed es nit müjjelesch, et Kännzeijsche „$1“ afzeschallde.",
        "tags-deactivate-submit": "Ußschallde",
+       "tags-apply-no-permission": "Do häs nit et Rääsch, zersamme met Dinge Änderonge noch Makehronge ze verjävve.",
+       "tags-apply-not-allowed-one": "De Makehrong „$1“ kam_mer nit vun Hand verjävve.",
+       "tags-apply-not-allowed-multi": "Heh {{PLURAL:$2|de Makehrong|di Makehronge|die kein Makehronge}} kam_mer nit vun Hand verjävve: $1",
+       "tags-update-no-permission": "Do häs nit et Rääsch, Makehronge vun einzel Väsjohne udder Enndrähsch en Logbohch fottzenämme udder zohzeföhje.",
+       "tags-update-add-not-allowed-one": "De Makehrong „$1“ kam_mer nit vun Hand verjävve.",
+       "tags-update-add-not-allowed-multi": "Heh {{PLURAL:$2|de Makehrong|di Makehronge|die kein Makehronge}} kam_mer nit vun Hand verjävve: $1",
+       "tags-update-remove-not-allowed-one": "De Makehronge „$1“ kam_mer nit fott nämme.",
+       "tags-update-remove-not-allowed-multi": "Heh {{PLURAL:$2|de Makehrong|di Makehronge|die kein Makehronge}} kam_mer nit vun Hand fott nämme: $1",
+       "tags-edit-title": "Makehronge ändere",
+       "tags-edit-manage-link": "makehronge verwallde",
+       "tags-edit-existing-tags": "Makehronge, di mer han:",
+       "tags-edit-existing-tags-none": "<tt>-&nbsp;nix&nbsp;-</tt>",
+       "tags-edit-new-tags": "Neuje Makehronge:",
+       "tags-edit-add": "Donn heh di Makehronge derbei:",
+       "tags-edit-remove": "Donn heh di Makehronge fottnämme:",
+       "tags-edit-remove-all-tags": "Alle Makehronge fottnämme",
+       "tags-edit-chosen-placeholder": "Donn heh Makehronge aanjävve",
+       "tags-edit-chosen-no-results": "Mer han kein zerpaß Makehronge jefonge",
+       "tags-edit-reason": "Jrond:",
        "comparepages": "Sigge verjliesche",
        "compare-page1": "De ein Sigg",
        "compare-page2": "De ander Sigg",
index 1de239c..d8a7346 100644 (file)
        "tags-create-tag-name": "Numm vun der Markéierung (Tag):",
        "tags-create-reason": "Grond:",
        "tags-create-submit": "Uleeën",
+       "tags-create-no-name": "Dir musst den Numm vun enger Markéierung (tag) uginn.",
        "tags-create-already-exists": "D'Markéierung (tag) ''$1'' gëtt et schonn.",
        "tags-delete-title": "Markéierung (tag) läSchen",
        "tags-delete-reason": "Grond:",
        "tags-deactivate-reason": "Grond:",
        "tags-deactivate-not-allowed": "Et ass net méiglech d'Markéierung \"$1\" ze desaktivéieren.",
        "tags-deactivate-submit": "Desaktivéieren",
+       "tags-update-remove-not-allowed-one": "D'Markéierung (tag) \"$1\" däerf net ewechgeholl ginn.",
+       "tags-update-remove-not-allowed-multi": "Dës {{PLURAL:$2|Markéierung däerf|Markéierungen däerfen}} net manuell ewechgeholl ginn: $1",
+       "tags-edit-new-tags": "Nei Markéierungen (tags):",
+       "tags-edit-add": "Dës Markéierungen (tags) dobäisetzen:",
+       "tags-edit-reason": "Grond:",
        "comparepages": "Säite vergläichen",
        "compare-page1": "Säit 1",
        "compare-page2": "Säit 2",
        "feedback-submit": "Schécken",
        "feedback-thanks": "Merci! Äre Feedback gouf op der Säit \"[$2 $1]\" gespäichert.",
        "feedback-thanks-title": "Merci!",
+       "feedback-useragent": "User Agent:",
        "searchsuggest-search": "Sichen",
        "searchsuggest-containing": "mat ...",
        "api-error-badaccess-groups": "Et ass Iech net erlaabt fir Fichieren op dës Wiki eropzelueden.",
index 3746426..6479033 100644 (file)
        "history-feed-description": "دوواره دیئن ویرگار سی بلگه د ویکی",
        "history-feed-item-nocomment": "$1 د\n$2",
        "history-feed-empty": "بلگه حاسته بیه وجود ناره.\nشایت وه د ویکی پاکسا بیه، یا نومش آلشت بیه.\nسی بلگیا مرتوط تازه [[ویجه:پی جوری|پی جوری د ویکی]] کوششت بکید.",
+       "history-edit-tags": "ویرایشت سردیسیا وانئریا انتخاو بیه",
        "rev-deleted-comment": "(ویرایشت چکسته جا وه جا بیه)",
        "rev-deleted-user": "(نوم کاروری جا وه جا بیه)",
        "rev-deleted-event": "(انجوم گر پهرستنومه جا وه جا بیه)",
        "right-sendemail": "سی کاریاریا هنی انجومانامه کل بکید",
        "right-passwordreset": "رازینه گواردن انجومانامه د نو دئه بیه نه بوینیت",
        "right-managechangetags": "راس کردن[[Special:سردیسیا|سردیسیا]] پاکسا کردن د رسینه جا",
+       "right-applychangetags": "وه کار گرتن [[Special:سردیسیا|سردیسیا]] واگرد آلشتیا هر کومشو",
        "newuserlogpage": "راس بیه وا کاریار",
        "newuserlogpagetext": "یه پهرستنومه راس بیئن کاریاره",
        "rightslog": "پهرستنومه حقوق کاریار",
        "action-editmyprivateinfo": "دونسمنیا شصقی خوتونه ویرایشت بکید",
        "action-editcontentmodel": "ویرایشت مدل مینونه یه گل بلگه",
        "action-managechangetags": "راس کردن و پاکسا کردن سردیسیا د رسینه جا",
+       "action-applychangetags": "سردیسیا نه واگرد آلشتیایی که خوتو دئیته وه کار بیئریت",
        "nchanges": "$1 {{جمی:$1|آلشت|آلشتیا}}",
        "enhancedrc-since-last-visit": "$1 {{جمی:$1|د آخری دیئن}}",
        "enhancedrc-history": "ويرگار",
        "logempty": "او چی ای که شما میهایت د پهرستنومه نیئش.",
        "log-title-wildcard": "بلگه یایی نه پی جوری کو که وا ای سرون شرو موئن",
        "showhideselectedlogentries": "آلشت دئن ورتیه گر پهرستنومه یا انتخاو بیه",
+       "log-edit-tags": "ویرایشت سردیسیایی که پهرستنومه شو گل گر بیه",
        "allpages": "همه بلگيا",
        "nextpage": "بلگه نهایی($1)",
        "prevpage": "بلگه دمایی($1)",
        "patrol-log-page": "پهرستنومه گشتن",
        "patrol-log-header": "یه پهرستنومه وانئریا سردیاری کرده هئ.",
        "log-show-hide-patrol": "$1 پهرستنومه سردیاری کردن",
+       "log-show-hide-tag": "سردیس پهرستنومه $1",
        "deletedrevision": "وانئری دماتری پاکسابیه د $1",
        "filedeleteerror-short": "خطا پاک نبیئن جانیا:$1",
        "filedeleteerror-long": "د گات پاکسا کردن جانیا یه گل خطا پیش اوما:\n\n\n$1",
        "version-poweredby-credits": "ای ویکی د لا '''[https://www.mediawiki.org/ ویکی وارسگر]''' حامینداری بوئه، همه حقوق پر و پیم کاری بیه© 2001-$1 $2.",
        "version-poweredby-others": "دیه رون",
        "version-poweredby-translators": "والریاریاtranslatewiki.net",
+       "version-credits-summary": "کسونایی که هان د زیر سی یه که د [[Special:Version|ویکی وارسگر]] ویرایشکاری داشتنه معرفی می کیم.",
+       "version-license-info": "ویکی وارسگر یه گل نرم افزار آزاده. شما می تونیت ونه وا شرایط نیسسه کاری ۲، یا (وا ویر و باور خوتو) هر نیسسه کاری تازه یی نه د پروانه کومله یکی خلکمن گنو که وه دس بنیاد نرم افزار آزاد درتیچ کاری بیه، د نو درتیچ کاری بکیت.\n\nویکی وارسگر وا ای دال که شایت خوو با درتیچ کاری بیه، ولی هیچ پشت راسکاری، حتی پشت راسکاری تجاری یا خوو بیین سی یه گل وه کار گرتن ویجه نه سیتو دیاری نمیکه.سی دونسمنیا بیشتر سیل پروانه کومله یکی خلکمن گنو بکیت.\n\nشما واس [{{SERVER}}{{SCRIPTPATH}}/COPYING یه گل نسقه د پراونه کومله یکی خلکمن گنو] نه واگرد ای برنامه گرته بایت. د غیر ای حال و بار وا Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA یا ونه [//www.gnu.org/licenses/old-licenses/gpl-2.0.html نامه نیسی کردوئیت یا ونه وه حال و بار برخط بحونیت].",
        "version-software": "نرم افزار پورسه بیه",
        "version-software-product": "نتجه",
        "version-software-version": "نسقه",
        "version-libraries-version": "نسقه",
        "redirect": "واگردونی وا جانیا،بلگه یا وانیئری نوم دیارکو",
        "redirect-legend": "واگردونی د جانیا یا بلگه",
+       "redirect-summary": "ای بلگه ویجه وا جانیا (نوم جانیا هیئش)، بلگه (شماره شناسیار بلگه یا شماره نسقه دیار بیه) یا بلگه کاریاری (شناسیار عددی کاریاری دیار بیه) واگردونی بوئه. طرز وه کار گرتن: [[{{#Special:Redirect}}/file/Example.jpg]]، \n[[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "رو",
        "redirect-lookup": "پی جوری:",
        "redirect-value": "ارزایشت:",
        "redirect-file": "نوم جانیا",
        "redirect-not-exists": "انازه که پیدا نبی",
        "fileduplicatesearch": "پی جوری سی جانیایا دوکونه",
+       "fileduplicatesearch-summary": "پی جوری سی جانیایا تکراری د پایه انازه د یک شیوسه ونو انجومگر بوئه.",
        "fileduplicatesearch-legend": "پی جوری سی دوکونه",
        "fileduplicatesearch-filename": "نوم جانیا:",
        "fileduplicatesearch-submit": "پی جوری",
        "fileduplicatesearch-info": "$1 × $2 pixel<br />انازه جانیا: $3<br />MIME type: $4",
+       "fileduplicatesearch-result-1": "جانیا «$1» تکراری نیئش.",
+       "fileduplicatesearch-result-n": "جانیا «$1» {{PLURAL:$2|یه گل چی تکراری|$2 یه گل چی تکراری}} داره.",
        "fileduplicatesearch-noresults": "جانیایی وا نوم «$1» یافت نبی.",
        "specialpages": "بلگيا ويجه",
        "specialpages-note-top": "میراث",
+       "specialpages-note": "* بلگه یا ویجه عادی.\n* <span class=\"mw-specialpagerestricted\">بلگه یا ویجه محدود کاری بیه.</span>",
        "specialpages-group-maintenance": "گزارشتیا واداشتن",
        "specialpages-group-other": "بلگه یا ویجه هنی",
        "specialpages-group-login": " اومائن د سيستم/راس كردن حساو",
        "tag-filter-submit": "فيلتر",
        "tag-list-wrapper": "([[Special:سردیسیا|{{PLURAL:$1|سردیس|سردیسیا}}]]: $2)",
        "tags-title": "سردیسیا",
+       "tags-intro": "ای بلگه یه گل نومگه د سردیسیاییه که نرم افزار وا ونو ویرایشتیا نه نشو کاری میکه، الوت واگرد هومبراوریاشو.",
        "tags-tag": "نوم سردیس",
        "tags-display-header": "دیاری کردن د نوم گه آلشتیا",
        "tags-description-header": "توضیح کامل هومبراور",
        "tags-hitcount": "$1 {{جمی:$1|آلشت|آلشتیا}}",
        "tags-manage-no-permission": "شما صلا یه نه که آلشت دئن سردیسیا نه دیوونداری بکیت ناریت.",
        "tags-create-heading": "راس کردن یه گل سردیس تازه",
+       "tags-create-explanation": "د شکل پیش فرض، سردیسیا تازه ره وندیاری بیه سی وه کار گرتن کاریاریا و رباتیا هان د دسرس.",
        "tags-create-tag-name": "نوم سردیس",
        "tags-create-reason": "دلیل",
        "tags-create-submit": "راس کردن",
        "tags-create-no-name": "شما واس سی هر سردیسی یه گل نوم راس بکیت.",
+       "tags-create-invalid-chars": "نوم سردیسیا نواس مینونه دار کاما (<code>,</code>) یا خط کج کوله(<code>/</code>) با.",
+       "tags-create-invalid-title-chars": "نوم تگیا نواس ده ور گرته حروفی با که نبوئه ونونه د داسون بلگه وه کار گرت.",
        "tags-create-already-exists": "سردیس\"$1\" ایسه هیئش.",
+       "tags-create-warnings-above": "د گات ره وندیاری سردیس \"$1\" وا {{PLURAL:$2|هشدار|هشداریا}} یی که هان د هار پیش اومانه:",
        "tags-create-warnings-below": "شما واقعن میهایت راس کردن ای سردیس نه دماداری بکیت؟",
        "tags-delete-title": "پاکسا کردن سردیس",
+       "tags-delete-explanation-initial": "شما د حال و بار پاکسا کردن سردیس «$1» د رسینه جا هئیت.",
+       "tags-delete-explanation-in-use": "یه د {{PLURAL:$2|$2 ویرایشت یا داده یا پهرستنومه یا وانئری|همه $2 ویرایشت و/یا درینده پهرستنومه}} پاکسا بوئه وا یه که ایسه تصیق کاری بیه.",
+       "tags-delete-explanation-warning": "ای انجومکاری<strong>ناورگشتنی</strong> یه، حتی ار دیوونداریا رسینه جا چنی کاری نه انجوم بیئن. یه دل بایت که یه همو سردیسیه که میهایت ونه پاکسا بکیت.",
+       "tags-delete-explanation-active": "<strong>سردیس \"$1\" هنی کنشتیاره و د نهاتر وه کار گرته بوئه.</strong> سی نهاگری د ای رخ ون، روئیت د بهرجایایی که سردیس کنشتیار بیه و د اوچه ناکشنتگرش بکیت.",
        "tags-delete-reason": "دليل:",
+       "tags-delete-submit": "ای سردیسه نه وه حال و بار ناورگشتنی پاکسا بک",
+       "tags-delete-not-allowed": "سردیسیایی که د یه گل افزونه تعریف موئن نبوئه پاکسا کاری بان، مر یه که او دمادیس د ای جریان ویجه صلا چنی کاری نه بیئه.",
        "tags-delete-not-found": "سردیس \"$1\" نیئش.",
+       "tags-delete-too-many-uses": "سردیس \"$1\" د بیشتر د $2 نسقه وه کار گرته بیه و نبوئه ونه پاکسا کرد.",
+       "tags-delete-warnings-after-delete": "سردیس \"$1\" وا خوش سرانجومی پاکسا بی، ولی واگرد {{PLURAL:$2|خطای|خطایا}} هار بی:",
        "tags-activate-title": "کنشتیاری کردن سردیس",
+       "tags-activate-question": "شما د حال و بار کنشتیاری سردیس «$1» یت.",
        "tags-activate-reason": "دليل:",
+       "tags-activate-not-allowed": "نبوئه سردیس «$1» نه کنشتیاری بکیت.",
        "tags-activate-not-found": "سردیس \"$1\" نیئش.",
        "tags-activate-submit": "کنشتیاری کردن",
        "tags-deactivate-title": "ناکشتیاری کردن سردیس",
+       "tags-deactivate-question": "شما د حال و بار کنشتیاری سردیس «$1» یت.",
        "tags-deactivate-reason": "دليل:",
+       "tags-deactivate-not-allowed": "نبوئه سردیس «$1» نه کنشتیاری بکیت.",
        "tags-deactivate-submit": "ناکنشتیاری کردن",
+       "tags-apply-no-permission": "شما صلا ینه که آلشتکاری سردیسیا نه واگرد آلشتیا خوتون وه کار بئریت ناریت.",
+       "tags-apply-not-allowed-one": "شما صلا ناریت سردیس  \"$1\" نه دسی وه کار بیئریت.",
+       "tags-apply-not-allowed-multi": "شما صلا ناریت که ای {{PLURAL:$2|سردیس|سردیسیا}} که د هار اومانه دسی وه کار بیئریت:$1",
+       "tags-update-no-permission": "شما صلا ینه که آلشتیا سردیسیا یا داده یا پهرستنومه نه د وانئریا شخصی ورداریت یا جا وه جا کاری بکیت ناریت.",
+       "tags-update-add-not-allowed-one": "شما صلا ناریت سردیس  \"$1\" نه دسی اضاف بکیت..",
+       "tags-update-add-not-allowed-multi": "شما صلا ناریت که ای {{PLURAL:$2|سردیس|سردیسیا}} که د هار اومانه دسی اضاف بکیت:$1",
+       "tags-update-remove-not-allowed-one": "شما صلا ناریت سردیس  \"$1\" نه دسی جا وه جاکاری بکیت.",
+       "tags-update-remove-not-allowed-multi": "شما صلا ناریت که ای {{PLURAL:$2|سردیس|سردیسیا}} که د هار اومانه دسی جا وه جاکاری بکیت:$1",
+       "tags-edit-title": "ویرایشت سردیسیا",
+       "tags-edit-manage-link": "دیوونداری سردیسیا",
+       "tags-edit-revision-selected": "{{PLURAL:$1|وانیری گل گر بیه|وانیری گل گر بیه}} د [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|پهرستنومه رخ ونیا انتخاو بیه|پهرستنومه رخ ونیا انتخاو بیه}}:",
+       "tags-edit-revision-legend": "اضاف کردن یا جا وه جاکاری سردیسیا د {{PLURAL:$1|د ای وانئری|همه وانئریا $1}}",
+       "tags-edit-logentry-legend": "اضاف کردن یا جا وه جاکاری سردیسیا د {{PLURAL:$1|د ای پهرستنومه|همه پهرستنومه یا $1}}",
+       "tags-edit-existing-tags": "سردیسیایی که هیئشو:",
+       "tags-edit-existing-tags-none": "\"هیشکوم\"",
+       "tags-edit-new-tags": "سردیسیا تازه:",
+       "tags-edit-add": "ای سردیسیا نه اضاف بکیت",
+       "tags-edit-remove": "ای سردیسیا نه ورداریت",
+       "tags-edit-remove-all-tags": "(همه سردیسیا نه ورداریت)",
+       "tags-edit-chosen-placeholder": "چن گل سردیس انتخاو بکیت",
+       "tags-edit-chosen-no-results": "هیچ سردیسی سی یکاگری دیاری نکرد",
+       "tags-edit-reason": "دلیل:",
+       "tags-edit-revision-submit": "وه کار گرتن سردیسیا د {{PLURAL:$1|د ای وانئری|وانئریا $1}}",
+       "tags-edit-logentry-submit": "وه کار گرتن سردیسیا د {{PLURAL:$1|د ای پهرستنومه|پهرستنومه یا $1}}",
+       "tags-edit-success": "<strong>آلشتکاریا وا خوش سرانجومی وه کار گرته بیین.</strong>",
+       "tags-edit-failure": "<strong>نبوئه آلشتکاریا وه کار گرته بان.</strong>",
+       "tags-edit-nooldid-title": "وانیری حاستنی نامعتوره",
+       "tags-edit-none-selected": "لطف بکیت یه حداقل یه گل سردیسی سی اضاف کردن یا جا وه جاکاری انتخاو بکیت.",
        "comparepages": "کنار یک نیاین بلگه یا",
        "compare-page1": "بلگه 1",
        "compare-page2": "بلگه 2",
        "compare-invalid-title": "داسونی که شما تیار کردیته خو نئ.",
        "compare-title-not-exists": "سرون مشقص بیه وجود ناره.",
        "compare-revision-not-exists": "وانئری که شما تی دیار کردیته وجود ناره.",
+       "dberr-problems": "د بدبختی! ای دیارگه مشگلیا وه کار گرتنی فنی داره.",
        "dberr-again": "چن دیقه آهره داری بکیت و دنو بلگه نه سوار بکیت.",
        "dberr-info": "(نبوئه وه رسینه جا:$1 دسرسی داشتوئیت)",
        "dberr-info-hidden": "(نبوئه د رسینه گا دسرسی داشت)",
+       "dberr-usegoogle": "شما د ای گات می تونیت سی پی جوری گوگل نه وه کار بئریت.",
+       "dberr-outofdate": "د ویر داشتوئیت که سیائه یا ونو د مینونه یا ایما شایت وه هنگوم نبا.",
+       "dberr-cachederror": "او چی که ها نها میا یه گل ورداشته د بلگه حاستنیه که که ها د کش و شایت  وه هنگوم نبا.",
        "htmlform-invalid-input": "یه قری مشگل ها د پاره یی د درینده یا شما.",
+       "htmlform-select-badoption": "ارزایشتی که دئیته یه گل گزینه حاستنی نئ.",
        "htmlform-int-invalid": "ارزایشتی که دئیته یه گل عدد صحیح نئ.",
        "htmlform-float-invalid": "ارزایشتی که دئیته یه گل عدد نئ.",
        "htmlform-int-toolow": "انازه یی که شما دئیته د کمترونه $1 فره کمتره.",
        "sqlite-no-fts": "$1 وا بی حامینداری پی جوری تمام نیسسه یی",
        "logentry-delete-delete": "$1 بلگه {{GENDER:$2|پاکسا بیه}} $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|}} بلگه $3 د نو زنه کرده",
+       "logentry-delete-event": "$1 دیاری {{PLURAL:$5|یه گل رخ ون د پهرستنومه|$5 رخ ونیا د پهرستنومه}} نه $3 {{GENDER:$2|آلشتکاری کرد}}: $4",
+       "logentry-delete-revision": "$1 دیاری {{PLURAL:$5|یه گل وانئری|$5 وانئریا}} نه $3 {{GENDER:$2|آلشتکاری کرد}}: $4",
+       "logentry-delete-event-legacy": "$1 دیاری پهرستنومه رخ ونیا نه د $3 {{GENDER:$2|آلشت ده}}",
+       "logentry-delete-revision-legacy": "$1 دیاری وانئریا نه د بلگه $3 {{GENDER:$2|آلشت ده}}",
+       "logentry-suppress-delete": "$1 $3 نه {{GENDER:$2| پاکساکاری کرد}}",
+       "logentry-suppress-event": "$1 دیاری {{PLURAL:$5|یه گل رخ ون د پهرستنومه|$5 رخ ونیا د پهرستنومه}} د $3 نه نهونی {{GENDER:$2|آلشتکاری کرد}}: $4",
+       "logentry-suppress-revision": "{{GENDER:$2|$1 دیاری {{PLURAL:$5|یه گل وانئری|$5 وانئریا}} نه د $3 نهونی {{GENDER:$2|آلشتکاری کرد}}: $4}}",
+       "logentry-suppress-event-legacy": "$1 دیاری پهرستنومه رخ ونیا نه د $3 نهونی {{GENDER:$2|آلشت ده}}",
+       "logentry-suppress-revision-legacy": "$1 دیاری نسقه یا $3 نه نهونی {{GENDER:$2|آلشت ده}}",
        "revdelete-content-hid": "مینونه قام بیه",
        "revdelete-summary-hid": "چکسته ویرایشت قام بیه",
        "revdelete-uname-hid": "نوم کاروری قام بیه",
        "revdelete-content-unhid": "مینونه قام نبیه",
        "revdelete-summary-unhid": "چکسته ویرایشت قام نبیه",
        "revdelete-uname-unhid": "نوم کاروری قام نبیه",
+       "revdelete-restricted": "دیوونداریا محدود بیین",
+       "revdelete-unrestricted": "ؤرداشتن محدودیت دیوونداریا",
        "logentry-move-move": "$1 {{GENDER:$2|جا وه جا کرده}} بلگه $3 نه سی $4",
        "logentry-newusers-newusers": "حساو کاریاری $1 {{GENDER:$2|دروس بیه}}",
        "logentry-newusers-create": "حساو کاریاری $1 {{GENDER:$2|راس بی}}",
        "logentry-upload-revert": "$1 $3 نه {{GENDER:$2|سوارکرد}}",
        "log-name-managetags": "سردیس دیوونداری کردن پهرستنومه",
        "logentry-managetags-create": "$1 {{GENDER:$2|سردیس \"$4\"}} نه راس کرده",
+       "log-name-tag": "پهرستنومه سردیس",
        "rightsnone": "(هيش كوم)",
        "revdelete-summary": "چکسه ویرایشت",
        "feedback-adding": "اضاف کردن هوال حون یار د بلگه....",
index d42add4..276b498 100644 (file)
        "history-feed-description": "Историја на измените на оваа страница на викито",
        "history-feed-item-nocomment": "$1 на $2",
        "history-feed-empty": "Бараната страница не постои.\nМоже била избришана од викито или преименувана.\nОбидете се да [[Special:Search|пребарате низ викито]] за релевантни нови страници.",
+       "history-edit-tags": "Измени ознаки да одредени преработки",
        "rev-deleted-comment": "(избришан опис на промени)",
        "rev-deleted-user": "(избришано корисничко име)",
        "rev-deleted-event": "(избришани податоци од дневникот)",
        "right-sendemail": "Испраќање на е-пошта до други корисници",
        "right-passwordreset": "Преглед на пораки по е-пошта за промена на лозинка",
        "right-managechangetags": "Создавање3 или бришење на [[Special:Tags|ознаки]] од базата",
+       "right-applychangetags": "Задавање на [[Special:Tags|ознаки]] заедно со направените измени",
+       "right-changetags": "Додавате и отстранување на произволни [[Special:Tags|ознаки]] во поединечни преработки и дневнички записи",
        "newuserlogpage": "Дневник на регистрирања на корисници",
        "newuserlogpagetext": "Ова е дневник на регистрирани корисници.",
        "rightslog": "Дневник на корисничките права",
        "action-editmyprivateinfo": "уредување на вашите лични податоци",
        "action-editcontentmodel": "уредување на содржинскиот модел на страница",
        "action-managechangetags": "создавање или бришење на ознаки од базата",
+       "action-applychangetags": "ставање на ознаки заедно со напревените промени",
+       "action-changetags": "додавање и отстранување на произволни ознаки во поединечни преработки и дневнички записи",
        "nchanges": "$1 {{PLURAL:$1|промена|промени}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|од последната посаета}}",
        "enhancedrc-history": "историја",
        "logempty": "Дневникот нема записи што одговараат на ова.",
        "log-title-wildcard": "Пребарај наслови кои почнуваат со овој текст",
        "showhideselectedlogentries": "Прикажи/скриј одбрани записи",
+       "log-edit-tags": "Измени ознаки на одредени дневнички записи",
        "allpages": "Сите страници",
        "nextpage": "Следна страница ($1)",
        "prevpage": "Претходна страница ($1)",
        "patrol-log-page": "Дневник на патролирања",
        "patrol-log-header": "Ова е дневник на патролирани преработки.",
        "log-show-hide-patrol": "$1 дневник на патролирање",
+       "log-show-hide-tag": "$1 дневник на ознаки",
        "deletedrevision": "Избришана стара преработка $1.",
        "filedeleteerror-short": "Грешка при бришење на податотека: $1",
        "filedeleteerror-long": "Се појавија грешки при бришењето на податотеката:\n\n$1",
        "tags-deactivate-reason": "Причина:",
        "tags-deactivate-not-allowed": "Не можам да ја деактивирам ознаката „$1“.",
        "tags-deactivate-submit": "Декативирај",
+       "tags-apply-no-permission": "Немате дозвола да ставате ознаки за промени заедно со измените што ги правите.",
+       "tags-apply-not-allowed-one": "Не е дозволено ознаката „$1“ да се става рачно.",
+       "tags-apply-not-allowed-multi": "Не е дозволено {{PLURAL:$2|следнава ознака да се става рачно|следниве ознаки да се ставаат рачно}}: $1",
+       "tags-update-no-permission": "Немате дозвола да додавате или отстранувате ознаки за промена од поединечни преработки или дневнички записи.",
+       "tags-update-add-not-allowed-one": "Не е дозволено ознаката „$1“ да се додава рачно.",
+       "tags-update-add-not-allowed-multi": "Не е дозволено {{PLURAL:$2|следнава ознака да се додава рачно|следниве ознаки да се додаваат рачно}}: $1",
+       "tags-update-remove-not-allowed-one": "Не е дозволено да се отстранува ознаката „$1“.",
+       "tags-update-remove-not-allowed-multi": "Не е дозволено {{PLURAL:$2|следнава ознака да се отстранува рачно|следниве ознаки да се отстрануваат рачно}}: $1",
+       "tags-edit-title": "Менување на ознаки",
+       "tags-edit-manage-link": "Раководство со ознаки",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Одбрана преработка|Одбрани преработки}} на [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Одбран настан од дневник|Одбрани настани од дневник}}:",
+       "tags-edit-revision-legend": "Додајте или отстранете ознаки од {{PLURAL:$1|преработкава|сите $1 преработки}}",
+       "tags-edit-logentry-legend": "Додајте или отстранете ознаки од {{PLURAL:$1|овој дневнички запис|сите $1 дневнички записи}}",
+       "tags-edit-existing-tags": "Постоечки ознаки",
+       "tags-edit-existing-tags-none": "''нема''",
+       "tags-edit-new-tags": "Нови ознаки:",
+       "tags-edit-add": "Додај ги следниве ознаки:",
+       "tags-edit-remove": "Отстрани ги следниве ознаки:",
+       "tags-edit-remove-all-tags": "(отстрани ги сите ознаки)",
+       "tags-edit-chosen-placeholder": "Одберете некои ознаки",
+       "tags-edit-chosen-no-results": "Не пронајдов одговарачки ознаки",
+       "tags-edit-reason": "Причина:",
+       "tags-edit-revision-submit": "Примени измени врз {{PLURAL:$1|преработкава|$1 преработки}}",
+       "tags-edit-logentry-submit": "Примени измени врз {{PLURAL:$1|овој дневнички запис|$1 дневнички записи}}",
+       "tags-edit-success": "<strong>Измените се успешно применети.</strong>",
+       "tags-edit-failure": "<strong>Не можев да ги применам измените:</strong>\n$1",
+       "tags-edit-nooldid-title": "Неважечка целна преработка",
+       "tags-edit-nooldid-text": "Немате укажано целна преработка врз која би се примениле измените, или пак укажаната преработка не постои.",
+       "tags-edit-none-selected": "Одберете барем една ознака за додавање или отстранување.",
        "comparepages": "Спореди страници",
        "compare-page1": "Страница 1",
        "compare-page2": "Страница 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|ја избриша}} ознаката „$4“ (отстранета од {{PLURAL:$5|една преработка или дневнички запис|$5 преработки и/или дневнички записи}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|ја активираше}} ознаката „$4“ за употреба од корисници и ботови",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|ја деактивираше}} ознаката „$4“ за употреба од корисници и ботови",
+       "log-name-tag": "Дневник на ознаки",
+       "log-description-tag": "На страницава се прикажани додавањата и отстранувањата на [[Special:Tags|ознаки]] од поединечни преработки или дневнички записи. Тука не се заведуваат означувањата направени како дел од уредување, бришење и слични дејства.",
+       "logentry-tag-update-add-revision": "$1 {{PLURAL:$7|ја|ги}} {{GENDER:$2|додаде}} {{PLURAL:$7|ознаката|ознаките}} $6 во преработката $4 на страницата $3",
+       "logentry-tag-update-add-logentry": "$1 {{PLURAL:$7|ја|ги}} {{GENDER:$2|додаде}} {{PLURAL:$7|ознаката|ознаките}} $6 во дневничкиот запис $5 на страницата $3",
+       "logentry-tag-update-remove-revision": "$1 {{PLURAL:$9|ја|ги}} {{GENDER:$2|отстрани}} {{PLURAL:$9|ознаката|ознаките}} $8 од преработката $4 на страницата $3",
+       "logentry-tag-update-remove-logentry": "$1 {{PLURAL:$9|ја|ги}} {{GENDER:$2|отстрани}} {{PLURAL:$9|ознаката|ознаките}} $8 од дневничкиот запис $5 на страницата $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|поднови}} ознаки во преработката $4 на страницата $3 ({{PLURAL:$7|додадена|додадени}} $6; {{PLURAL:$9|отстранета|отстранети}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|поднови}} ознаки во дневничкиот запис $5 на страницата $3 ({{PLURAL:$7|додадена|додадени}} $6; {{PLURAL:$9|отстранета|отстранети}} $8)",
        "rightsnone": "(нема)",
        "revdelete-summary": "опис на уредување",
        "feedback-adding": "Го додавам искажаното мислење во страницата...",
index c5c10bd..430bca7 100644 (file)
        "history-feed-description": "Cronologgia d' 'a paggena ncopp'a stu sito",
        "history-feed-item-nocomment": "$1 'o $2",
        "history-feed-empty": "'A paggena addimannata nun esiste.\nPuò darsi ca è stata scancellata d' 'a wiki, o s'è cagnato 'o nomme.\nProva a vedé [[Special:Search|dint' 'a wiki]] si ce stanno nnove paggene.",
+       "history-edit-tags": "Cagna tag 'e verziune scigliute",
        "rev-deleted-comment": "(Oggetto d' 'o cagnamiento luvato)",
        "rev-deleted-user": "(nomme utente luvato)",
        "rev-deleted-event": "(dettaglie d' 'o log luvate)",
        "right-sendemail": "Manna na mail a ll'at'utente",
        "right-passwordreset": "Vide 'e mmasciate 'e rimpustazione d' 'a password",
        "right-managechangetags": "Crìa e scancella 'e [[Special:Tags|tag]] d' 'o database",
+       "right-applychangetags": "Appreca [[Special:Tags|tag]] pe' tramente ca se fanno 'e cagnamiente 'e coccheruno",
+       "right-changetags": "Azzecca o lèva a caso 'e [[Special:Tags|tag]] dint'a verziune nnividuale e riggistre 'e log",
        "newuserlogpage": "Riggistro 'e nuove utente",
        "newuserlogpagetext": "Chest'è nu riggistro 'e criazione d'utenze.",
        "rightslog": "Deritte 'e ll'utente",
        "action-editmyprivateinfo": "cagnà 'e proprie date perzunale",
        "action-editcontentmodel": "càgna 'o mudelo 'e cuntenute 'e na paggena",
        "action-managechangetags": "crìa e scancella 'e tag d' 'o database",
+       "action-applychangetags": "appreca tag pe' tramente ca se fanno 'e cagnamiente vuoste",
+       "action-changetags": "azzecca o lèva tag a caso dint'a verziune nnividuale e riggistre 'e log",
        "nchanges": "$1 {{PLURAL:$1|cagnamiento|cagnamiente}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|'a ll'urdema visita}}",
        "enhancedrc-history": "cronologgia",
        "logempty": "Nun ce sta n'elemento dint' 'o riggistro azzeccato â ricerca.",
        "log-title-wildcard": "Ascìa titole c'accummencieno cu stu testo",
        "showhideselectedlogentries": "Cagna visibbelità d' 'e riggistre scigliute",
+       "log-edit-tags": "Cagna 'e tag d' 'e riggistre 'e log scigliute",
        "allpages": "Tutte 'e ppaggene",
        "nextpage": "Paggena appriesso ($1)",
        "prevpage": "Paggena apprima ($1)",
        "patrol-log-page": "Riggistro 'e cuntrolle",
        "patrol-log-header": "Chest'è nu riggistro ch' 'e verziune cuntrullate.",
        "log-show-hide-patrol": "$1 riggistro 'e cuntrolle",
+       "log-show-hide-tag": "$1 tag log",
        "deletedrevision": "Viecchia verziona scancellata $1",
        "filedeleteerror-short": "Errore pe' tramente ca se scancellava nu file: $1",
        "filedeleteerror-long": "N'errore s'è apprisentato pe' tramente ca se scancellava 'o file:\n\n$1",
        "tags-deactivate-reason": "Mutivo:",
        "tags-deactivate-not-allowed": "Nun se pò stutà 'o tag \"$1\".",
        "tags-deactivate-submit": "Stuta",
+       "tags-apply-no-permission": "Nun tenite premmesse pe' putè apprecà tag 'e cagnamiente pe' tramente ca facite cagnamiente.",
+       "tags-apply-not-allowed-one": "'O tag \"$1\" nun è premmesso a se ffà manualmente apprecà.",
+       "tags-update-add-not-allowed-one": "'O tag \"$1\" nun è premmesso 'e s'azzeccà 'n manuale.",
+       "tags-edit-title": "Cagna 'e tag",
+       "tags-edit-manage-link": "Gistisce 'e tag",
+       "tags-edit-existing-tags": "Tag 'e mo:",
+       "tags-edit-existing-tags-none": "''Nisciuno''",
+       "tags-edit-new-tags": "Nnove tag:",
+       "tags-edit-add": "Azzecca sti tag:",
+       "tags-edit-remove": "Lèva sti tag:",
+       "tags-edit-remove-all-tags": "(leva tutte 'e tag)",
+       "tags-edit-chosen-placeholder": "Sceglie cocche tag",
+       "tags-edit-chosen-no-results": "Nun se trovano tag azzeccate",
+       "tags-edit-reason": "Mutivo:",
        "comparepages": "Miette a cunfronto 'e paggene",
        "compare-page1": "Paggena 1",
        "compare-page2": "Paggena 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|scancellaje}} 'o tag \"$4\" (luvato 'a $5 {{PLURAL:$5|verziona o nutarella 'e riggistro|verziune o nutarelle 'e riggistro}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|appicciaje}} 'o tag \"$4\" pe ll'uso 'a ll'utente e re bot",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|stutaje}} 'o tag \"$4\" pe' ll'uso d'utente e re bot",
+       "log-name-tag": "Riggistro 'e tag",
        "rightsnone": "(nisciuno)",
        "revdelete-summary": "cagna 'o riepilego",
        "feedback-adding": "Azzecca nu feedback/na segnalazione â paggena..",
index 22f27fd..85a4d20 100644 (file)
        "history-feed-description": "Histórico de revisões para esta página neste wiki",
        "history-feed-item-nocomment": "$1 em $2",
        "history-feed-empty": "A página requisitada não existe.\nPoderá ter sido eliminada do wiki ou renomeada.\nTente [[Special:Search|pesquisar no wiki]] por páginas relevantes.",
+       "history-edit-tags": "Editar etiquetas das revisões selecionadas",
        "rev-deleted-comment": "(resumo da edição suprimido)",
        "rev-deleted-user": "(nome de usuário removido)",
        "rev-deleted-event": "(registros de detalhes eliminados)",
        "mimetype": "tipo MIME:",
        "download": "download",
        "unwatchedpages": "Páginas não vigiadas",
-       "listredirects": "Listar redirecionamentos",
+       "listredirects": "Lista de redirecionamentos",
        "listduplicatedfiles": "Lista de arquivos com duplicatas",
        "listduplicatedfiles-summary": "Esta é uma lista de arquivos, onde a versão mais recente do arquivo é uma duplicata da versão mais recente de algum outro arquivo. Somente os arquivos locais são considerados.",
        "listduplicatedfiles-entry": "O arquivo [[:File:$1|$1]] tem [[$3|{{PLURAL:$2|uma duplicata|$2 duplicatas}}]].",
index 99a70e7..db3c3a4 100644 (file)
        "history-feed-description": "Histórico de edições para esta página nesta wiki",
        "history-feed-item-nocomment": "$1 em $2",
        "history-feed-empty": "A página solicitada não existe.\nPode ter sido eliminada da wiki ou o nome sido alterado.\nTente [[Special:Search|pesquisar na wiki]] novas páginas relevantes.",
+       "history-edit-tags": "Editar etiquetas das revisões selecionadas",
        "rev-deleted-comment": "(resumo da edição suprimido)",
        "rev-deleted-user": "(nome de utilizador removido)",
        "rev-deleted-event": "(registos de detalhes eliminados)",
        "logempty": "Não há dados a apresentar.",
        "log-title-wildcard": "Procurar títulos iniciados por este texto",
        "showhideselectedlogentries": "Mostrar ou ocultar as entradas selecionadas",
+       "log-edit-tags": "Editar etiquetas das entradas de registo selecionadas",
        "allpages": "Todas as páginas",
        "nextpage": "Página seguinte ($1)",
        "prevpage": "Página anterior ($1)",
        "patrol-log-page": "Registo de edições patrulhadas",
        "patrol-log-header": "Este é um registo de edições patrulhadas.",
        "log-show-hide-patrol": "$1 registo de edições patrulhadas",
+       "log-show-hide-tag": "$1 registo de etiquetas",
        "deletedrevision": "Apagou a versão antiga $1",
        "filedeleteerror-short": "Erro ao eliminar ficheiro: $1",
        "filedeleteerror-long": "Foram encontrados erros ao tentar eliminar o ficheiro:\n\n$1",
        "tags-deactivate-reason": "Motivo:",
        "tags-deactivate-not-allowed": "Não é possível desativar a etiqueta \"$1\".",
        "tags-deactivate-submit": "Desativar",
+       "tags-apply-not-allowed-one": "A etiqueta \"$1\" não pode ser aplicada manualmente.",
+       "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser aplicada|As seguintes etiquetas não podem ser aplicadas}} manualmente: $1",
+       "tags-update-add-not-allowed-one": "A etiqueta \"$1\" não pode ser adicionada manualmente.",
+       "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser adicionada|As seguintes etiquetas não podem ser adicionadas}} manualmente: $1",
+       "tags-update-remove-not-allowed-one": "A remoção da etiqueta \"$1\" não é permitida.",
+       "tags-update-remove-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser removida|As seguintes etiquetas não podem ser removidas}} manualmente: $1",
+       "tags-edit-title": "Editar etiquetas",
+       "tags-edit-manage-link": "Gerir etiquetas",
+       "tags-edit-logentry-legend": "Adicionar ou remover etiquetas {{PLURAL:$1|desta entrada de registo|de todas as $1 entradas de registo}}",
+       "tags-edit-existing-tags": "Etiquetas existentes:",
+       "tags-edit-existing-tags-none": "''Nenhuma''",
+       "tags-edit-new-tags": "Novas etiquetas:",
+       "tags-edit-add": "Adicionar estas etiquetas:",
+       "tags-edit-remove": "Remover estas etiquetas:",
+       "tags-edit-remove-all-tags": "(remover todas as etiquetas)",
+       "tags-edit-chosen-placeholder": "Selecione algumas etiquetas",
+       "tags-edit-reason": "Motivo:",
+       "tags-edit-success": "<strong>As alterações foram aplicadas com sucesso.</strong>",
+       "tags-edit-failure": "<strong>As alterações não puderam ser aplicadas:</strong>\n$1",
+       "tags-edit-none-selected": "Por favor, selecione pelo menos uma etiqueta para adicionar ou remover.",
        "comparepages": "Comparar páginas",
        "compare-page1": "Página 1",
        "compare-page2": "Página 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|eliminou}} a etiqueta \"$4\" (removida de $5 {{PLURAL:$5|edição ou entrada de registo|edições e/ou entradas de registo}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|ativou}} a etiqueta \"$4\" para uso de utilizadores e robôs.",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|desativou}} a etiqueta \"$4\" para uso de utilizadores e robôs.",
+       "log-name-tag": "Registo de etiquetas",
        "rightsnone": "(nenhum)",
        "revdelete-summary": "editar resumo",
        "feedback-adding": "A acrescentar os comentários à página...",
index d2da2b1..10db75d 100644 (file)
        "tags-edit-logentry-selected": "{{Identical|logdelete-selected}}\n\nSee also:\n* {{msg-mw|tags-edit-revision-selected}}",
        "tags-edit-revision-explanation": "Leave blank.\n\nSee also:\n* {{msg-mw|tags-edit-logentry-explanation}}",
        "tags-edit-logentry-explanation": "Leave blank.\n\nSee also:\n* {{msg-mw|tags-edit-revision-explanation}}",
-       "tags-edit-revision-legend": "Form legend.\n\nSee also:\n* {{msg-mw|tags-edit-logentry-legend}}",
-       "tags-edit-logentry-legend": "Form legend.\n\nSee also:\n* {{msg-mw|tags-edit-revision-legend}}",
+       "tags-edit-revision-legend": "Form legend.\n\n$1 is the number of revisions.\n\nSee also:\n* {{msg-mw|tags-edit-logentry-legend}}",
+       "tags-edit-logentry-legend": "Form legend.\n\n$1 is the number of entries.\n\nSee also:\n* {{msg-mw|tags-edit-revision-legend}}",
        "tags-edit-existing-tags": "Heading beneath which a list of tags already applied to the revision or log entry is presented.",
        "tags-edit-existing-tags-none": "Shown when no tags are applied. Should be formatted differently (italicised or parenthesised).",
        "tags-edit-new-tags": "Heading beneath which the user chooses which tags should be attached to the revision or log entry. They may add or remove tags.",
index cb8d692..a9f41fe 100644 (file)
        "history-feed-description": "Istoricul versiunilor pentru această pagină din wiki",
        "history-feed-item-nocomment": "$1 la $2",
        "history-feed-empty": "Pagina solicitată nu există.\nE posibil să fi fost ștearsă sau redenumită.\nÎncearcă să [[Special:Search|cauți]] pe wiki pentru pagini noi semnificative.",
+       "history-edit-tags": "Modifică etichetele versiunilor selectate",
        "rev-deleted-comment": "(descrierea modificării ștearsă)",
        "rev-deleted-user": "(nume de utilizator șters)",
        "rev-deleted-event": "(detaliile din jurnalul șterse)",
        "right-sendemail": "Trimite e-mail altor utilizatori",
        "right-passwordreset": "Vizualizează e-mailurile de reinițializare a parolelor",
        "right-managechangetags": "Creează și șterge [[Special:Tags|etichete]] din baza de date",
+       "right-applychangetags": "Aplică [[Special:Tags|etichete]] asociate modificărilor unui utilizator",
        "newuserlogpage": "Jurnal utilizatori noi",
        "newuserlogpagetext": "Acesta este jurnalul creărilor conturilor de utilizator.",
        "rightslog": "Jurnal permisiuni de utilizator",
index 35f8049..71f0c18 100644 (file)
        "tags-active-no": "None",
        "tags-edit": "cange",
        "tags-hitcount": "$1 {{PLURAL:$1|cangiamende|cangiaminde}}",
+       "tags-edit-new-tags": "Tag nuève:",
        "comparepages": "Combronde le pàggene",
        "compare-page1": "Pàgene 1",
        "compare-page2": "Pàgene 2",
        "expand_templates_remove_comments": "Live le commende",
        "expand_templates_remove_nowiki": "No fà vede le tag <nowiki> jndr'à 'u resultate",
        "expand_templates_generate_xml": "Fà vedè l'arvule de l'analisi XML",
-       "expand_templates_preview": "Andeprime"
+       "expand_templates_preview": "Andeprime",
+       "special-characters-group-latin": "Latine",
+       "special-characters-group-latinextended": "Latine estese",
+       "special-characters-group-ipa": "IPA",
+       "special-characters-group-symbols": "Simbole",
+       "special-characters-group-greek": "Greche",
+       "special-characters-group-cyrillic": "Cirilliche",
+       "special-characters-group-arabic": "Arabe",
+       "special-characters-group-arabicextended": "Estenziune arabe",
+       "special-characters-group-persian": "Persiane",
+       "special-characters-group-hebrew": "Ebbrèe",
+       "special-characters-group-bangla": "Bangladesciane",
+       "special-characters-group-tamil": "Tamil",
+       "special-characters-group-telugu": "Telugu",
+       "special-characters-group-sinhala": "Sinhala",
+       "special-characters-group-gujarati": "Gujarati",
+       "special-characters-group-devanagari": "Devanagari",
+       "special-characters-group-thai": "Thai",
+       "special-characters-group-lao": "Lao",
+       "special-characters-group-khmer": "Khmer",
+       "special-characters-title-endash": "trattine en",
+       "special-characters-title-emdash": "trattine em",
+       "special-characters-title-minus": "segne mene"
 }
index f650974..67e9d27 100644 (file)
        "tags-deactivate-reason": "Причина:",
        "tags-deactivate-not-allowed": "Невозможно отключить метку «$1».",
        "tags-deactivate-submit": "Отключить",
+       "tags-update-add-not-allowed-one": "Тег \"$1\" не может быть добавлен вручную.",
+       "tags-update-add-not-allowed-multi": "Следующее {{PLURAL:$2|tag is|tags are}} не может быть добавлено вручную: $1",
+       "tags-edit-title": "Редактировать теги",
+       "tags-edit-manage-link": "Управление тегами",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Выбранная версия|Выбранные версии}} [[:$2]]:",
+       "tags-edit-revision-legend": "Добавить или удалить теги из {{PLURAL:$1|this revision|all $1 revisions}}",
+       "tags-edit-logentry-legend": "Добавить или удалить теги из {{PLURAL:$1|this log entry|all $1 log entries}}",
+       "tags-edit-existing-tags": "Существующие метки:",
+       "tags-edit-existing-tags-none": "''Нет''",
+       "tags-edit-new-tags": "Новые метки:",
+       "tags-edit-add": "Добавить эти метки:",
+       "tags-edit-remove": "Удалить эти метки:",
+       "tags-edit-remove-all-tags": "(удалить все метки)",
+       "tags-edit-chosen-placeholder": "Выберите один или несколько тэгов",
+       "tags-edit-reason": "Причина:",
+       "tags-edit-none-selected": "Пожалуйста, выберите по крайней мере один тег, чтобы добавить или удалить.",
        "comparepages": "Сравнение страниц",
        "compare-page1": "Первая страница",
        "compare-page2": "Вторая страница",
        "logentry-managetags-create": "$1 создал{{GENDER:$2||а}} метку «$4»",
        "logentry-managetags-activate": "$1 активировал{{GENDER:$2||а}} метку «$4» для использования участниками и ботами",
        "logentry-managetags-deactivate": "$1 отключил{{GENDER:$2||а}} метку «$4» для использования участниками и ботами",
+       "log-name-tag": "Журнал меток",
        "rightsnone": "(нет)",
        "revdelete-summary": "описание изменений",
        "feedback-adding": "Добавление отзыва на страницу…",
index bad88a6..e081d23 100644 (file)
        "tags-deactivate-reason": "Anledning:",
        "tags-deactivate-not-allowed": "Det är inte möjligt att inaktivera taggen \"$1\".",
        "tags-deactivate-submit": "Inaktivera",
+       "tags-update-remove-not-allowed-one": "Märket \"$1\" får inte tas bort.",
+       "tags-update-remove-not-allowed-multi": "Följande {{PLURAL:$2|märke|märken}} får inte tas bort manuellt: $1",
+       "tags-edit-title": "Redigera märken",
+       "tags-edit-manage-link": "Hantera märken",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Vald sidversion|Valda sidversioner}} från [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Vald logghändelse|Valda logghändelser}}:",
+       "tags-edit-revision-legend": "Lägg eller ta bort märken från {{PLURAL:$1|denna sidversion|alla $1 sidversioner}}",
+       "tags-edit-logentry-legend": "Lägg till eller ta bort märken från {{PLURAL:$1|denna loggpost|alla $1 loggposter}}",
+       "tags-edit-existing-tags": "Befintliga märken:",
+       "tags-edit-existing-tags-none": "''Inga''",
+       "tags-edit-new-tags": "Nya märken:",
+       "tags-edit-add": "Lägg till dessa märken:",
+       "tags-edit-remove": "Ta bort dessa märken:",
+       "tags-edit-remove-all-tags": "(ta bort alla märken)",
+       "tags-edit-chosen-placeholder": "Välj några märken",
+       "tags-edit-chosen-no-results": "Inga överensstämmande märken hittades",
+       "tags-edit-reason": "Anledning:",
+       "tags-edit-revision-submit": "Verkställ ändringar för {{PLURAL:$1|denna sidversion|$1 sidversioner}}",
+       "tags-edit-logentry-submit": "Verkställ ändringar för {{PLURAL:$1|denna loggpost|$1 loggposter}}",
+       "tags-edit-success": "<strong>Ändringarna verkställdes.</strong>",
+       "tags-edit-failure": "<strong>Ändringarna kunde inte verkställas:</strong>\n$1",
        "comparepages": "Jämför sidor",
        "compare-page1": "Sida 1",
        "compare-page2": "Sida 2",
        "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} taggen \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} taggen \"$4\" för användning av användare och botar.",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} taggen \"$4\" för användning av användare och botar.",
+       "log-name-tag": "Märkeslogg",
        "rightsnone": "(inga)",
        "revdelete-summary": "sammanfattning",
        "feedback-adding": "Ge feedback till sida...",
index 46c7b43..2bf4340 100644 (file)
        "right-viewsuppressed": "Перегляд змін, приховаих від усіх користувачів",
        "right-suppressionlog": "перегляд приватних журналів",
        "right-block": "заборона редагувань для інших дописувачів",
-       "right-blockemail": "Ð\91локування користувачам надсилання електронної пошти",
-       "right-hideuser": "Ð\91локування імені користувача і приховування його",
+       "right-blockemail": "блокування користувачам надсилання електронної пошти",
+       "right-hideuser": "блокування імені користувача і приховування його",
        "right-ipblock-exempt": "уникнення блокування за IP-адресою, автоблокування і блокування діапазонів",
        "right-proxyunbannable": "уникнення автоматичного блокування проксі-серверів",
        "right-unblockself": "розблоковування себе",
        "right-patrolmarks": "Перегляд патрульованих сторінок у нових редагуваннях",
        "right-unwatchedpages": "перегляд списку сторінок, за якими ніхто не спостерігає",
        "right-mergehistory": "об'єднання історій редагувань сторінок",
-       "right-userrights": "Ð\97міна всіх прав користувачів",
+       "right-userrights": "зміна всіх прав користувачів",
        "right-userrights-interwiki": "Зміна прав користувачів у інших вікі",
        "right-siteadmin": "Блокування і розблокування бази даних",
        "right-override-export-depth": "експорт сторінок, включаючи пов'язані сторінки з глибиною до 5",
index d5a5d78..1cec6c7 100644 (file)
        "history-feed-description": "ווערסיע היסטאריע פאר דעם בלאט אויפן וויקי",
        "history-feed-item-nocomment": "$1 אין $2",
        "history-feed-empty": "דער געבעטענער בלאט עקזיסטירט נישט.\nעס איז מעגליך אויסגעמעקט געווארן פון דער וויקי, אדער דער נאמען געטוישט.\nפרובירט [[Special:Search|צו זיכן אין וויקי]] נאך רעלאווענטע נייע בלעטער.",
+       "history-edit-tags": "רעדאקטירן טאגן פון אויסגעקליבענע ווערסיעס",
        "rev-deleted-comment": "(קורץ־ווארט אראָפגענומען)",
        "rev-deleted-user": "(באנוצער נאמען אראפגענומען)",
        "rev-deleted-event": "(לאגירן פרטים אראפגענומען)",
        "right-sendemail": "שיקן ע-פאסט צו אנדערע באניצער",
        "right-passwordreset": "באַקוקן פאַסווארט צוריקשטעלן ע־בריוו",
        "right-managechangetags": " [[Special:Tags|טאגן]] פון דעם שאפן און אויסמעקן",
+       "right-applychangetags": "אנווענדן [[Special:Tags|טאגן]] צוזאמען מיט ענדערונגען",
        "newuserlogpage": "נייע באַניצערס לאָג-בוך",
        "newuserlogpagetext": "דאס איז א לאג פון באַניצערס אײַנשרײַבונגען.",
        "rightslog": "באַניצער רעכטן לאג",
        "listfiles-delete": "אויסמעקן",
        "listfiles-summary": "דער דאזיקער באזונדערער בלאט ווייזט אלע ארויפגעלאדענע טעקעס.",
        "listfiles_search_for": "זוכן פֿאַר מעדיע נאָמען:",
+       "listfiles-userdoesnotexist": "באניצער קאנטע \"$1\" נישט איינגעשריבן.",
        "imgfile": "טעקע",
        "listfiles": "טעקע ליסטע",
        "listfiles_thumb": "געמינערט בילד",